diff --git a/.gitignore b/.gitignore index d5d19e9a..1ad4b78b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ pcaps caplets build -vendor bettercap*.* bettercap* bettercap.history diff --git a/README.md b/README.md index acab65ed..52581dd7 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Make sure you have a correctly configured **Go >= 1.8** environment, that `$GOPA $ go get github.com/bettercap/bettercap -This command will download bettercap, install its dependencies, compile it and move the `bettercap` executable to `$GOPATH/bin`. +This command will download bettercap, install its dependencies, compile it and move the `bettercap` executable to `$GOPATH/bin`. Now you can use `sudo bettercap -h` to show the basic command line options and just `sudo bettercap` to start an [interactive session](https://github.com/bettercap/bettercap/wiki/Interactive-Mode) on your default network interface, otherwise you can [load a caplet](https://github.com/bettercap/bettercap/wiki/Caplets) from [the dedicated repository](https://github.com/bettercap/caplets). diff --git a/vendor/github.com/adrianmo/go-nmea/.travis.yml b/vendor/github.com/adrianmo/go-nmea/.travis.yml new file mode 100644 index 00000000..8ad3a7f8 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/.travis.yml @@ -0,0 +1,30 @@ +# Travis CI (http://travis-ci.org/) is a continuous integration +# service for open source projects. This file configures it +# to run unit tests for go-nmea. + +language: go + +go: + - 1.7 + - tip + +matrix: + fast_finish: true + +before_install: + - go get golang.org/x/tools/cmd/cover + - go get github.com/golang/lint/golint + - go get github.com/mattn/goveralls + - go get github.com/stretchr/testify/assert + +install: + - go get -d -v ./... && go build -v ./... + +script: + - go vet -x ./... + - $HOME/gopath/bin/golint -set_exit_status ./... + - go test -v ./... + - go test -covermode=count -coverprofile=profile.cov . + +after_script: + - $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/vendor/github.com/adrianmo/go-nmea/LICENSE b/vendor/github.com/adrianmo/go-nmea/LICENSE new file mode 100644 index 00000000..f728c107 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Adrian Moreno + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/adrianmo/go-nmea/README.md b/vendor/github.com/adrianmo/go-nmea/README.md new file mode 100644 index 00000000..b0db9f69 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/README.md @@ -0,0 +1,52 @@ +# go-nmea [![Build Status](https://travis-ci.org/adrianmo/go-nmea.svg?branch=master)](https://travis-ci.org/adrianmo/go-nmea) [![Go Report Card](https://goreportcard.com/badge/github.com/adrianmo/go-nmea)](https://goreportcard.com/report/github.com/adrianmo/go-nmea) [![Coverage Status](https://coveralls.io/repos/adrianmo/go-nmea/badge.svg?branch=master&service=github)](https://coveralls.io/github/adrianmo/go-nmea?branch=master) [![GoDoc](https://godoc.org/github.com/adrianmo/go-nmea?status.svg)](https://godoc.org/github.com/adrianmo/go-nmea) + +This is a NMEA library for the Go programming language (http://golang.org). + +## Installing + +### Using `go get` + + go get github.com/adrianmo/go-nmea + +After this command *go-nmea* is ready to use. Its source will be in: + + $GOPATH/src/github.com/adrianmo/go-nmea + +## Supported sentences + +At this moment, this library supports the following sentence types: + +- [GPRMC](http://aprs.gids.nl/nmea/#rmc) - Recommended Minimum Specific GPS/Transit data +- [GNRMC](http://aprs.gids.nl/nmea/#rmc) - Recommended Minimum Specific GNSS data +- [GPGGA](http://aprs.gids.nl/nmea/#gga) - GPS Positioning System Fix Data +- [GNGGA](http://aprs.gids.nl/nmea/#gga) - GNSS Positioning System Fix Data +- [GPGSA](http://aprs.gids.nl/nmea/#gsa) - GPS DOP and active satellites +- [GPGSV](http://aprs.gids.nl/nmea/#gsv) - GPS Satellites in view +- [GLGSV](http://aprs.gids.nl/nmea/#gsv) - GLONASS Satellites in view +- [GPGLL](http://aprs.gids.nl/nmea/#gll) - Geographic Position, Latitude / Longitude and time +- [GPVTG](http://aprs.gids.nl/nmea/#vtg) - Track Made Good and Ground Speed +- [GPZDA](http://aprs.gids.nl/nmea/#zda) - Date & time data +- [PGRME](http://aprs.gids.nl/nmea/#rme) - Estimated Position Error (Garmin proprietary sentence) + + +## Example + +```go +package main + +import ( + "fmt" + "github.com/adrianmo/go-nmea" +) + +func main() { + m, err := nmea.Parse("$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70") + if err == nil { + fmt.Printf("%+v\n", m) + } +} +``` + +## Contributions + +Please, feel free to implement support for new sentences, fix bugs, refactor code, etc. and send a pull-request to update the library. diff --git a/vendor/github.com/adrianmo/go-nmea/glgsv.go b/vendor/github.com/adrianmo/go-nmea/glgsv.go new file mode 100644 index 00000000..62a0ac3b --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/glgsv.go @@ -0,0 +1,47 @@ +package nmea + +const ( + // PrefixGLGSV prefix + PrefixGLGSV = "GLGSV" +) + +// GLGSV represents the GPS Satellites in view +// http://aprs.gids.nl/nmea/#glgsv +type GLGSV struct { + Sent + TotalMessages int64 // Total number of messages of this type in this cycle + MessageNumber int64 // Message number + NumberSVsInView int64 // Total number of SVs in view + Info []GLGSVInfo // visible satellite info (0-4 of these) +} + +// GLGSVInfo represents information about a visible satellite +type GLGSVInfo struct { + SVPRNNumber int64 // SV PRN number, pseudo-random noise or gold code + Elevation int64 // Elevation in degrees, 90 maximum + Azimuth int64 // Azimuth, degrees from true north, 000 to 359 + SNR int64 // SNR, 00-99 dB (null when not tracking) +} + +// NewGLGSV constructor +func NewGLGSV(s Sent) (GLGSV, error) { + p := newParser(s, PrefixGLGSV) + m := GLGSV{ + Sent: s, + TotalMessages: p.Int64(0, "total number of messages"), + MessageNumber: p.Int64(1, "message number"), + NumberSVsInView: p.Int64(2, "number of SVs in view"), + } + for i := 0; i < 4; i++ { + if 5*i+4 > len(m.Fields) { + break + } + m.Info = append(m.Info, GLGSVInfo{ + SVPRNNumber: p.Int64(3+i*4, "SV prn number"), + Elevation: p.Int64(4+i*4, "elevation"), + Azimuth: p.Int64(5+i*4, "azimuth"), + SNR: p.Int64(6+i*4, "SNR"), + }) + } + return m, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/glgsv_test.go b/vendor/github.com/adrianmo/go-nmea/glgsv_test.go new file mode 100644 index 00000000..ca5f83c8 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/glgsv_test.go @@ -0,0 +1,96 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGLGSVGoodSentence(t *testing.T) { + goodMsg := "$GLGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*6B" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGLGSV, s.Prefix(), "Prefix does not match") + + sentence := s.(GLGSV) + assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match") + assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match") + assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match") + + assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match") + assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match") + assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match") + + assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match") + assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match") + assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match") + + assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match") + assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match") + assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match") + assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match") + + assert.Equal(t, int64(13), sentence.Info[3].SVPRNNumber, "Number of Info[3] SV PRN does not match") + assert.Equal(t, int64(6), sentence.Info[3].Elevation, "Number of Info[3] Elevation does not match") + assert.Equal(t, int64(292), sentence.Info[3].Azimuth, "Number of Info[3] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[3].SNR, "Number of Info[3] SNR does not match") +} + +func TestGLGSVShort(t *testing.T) { + goodMsg := "$GLGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12*56" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGLGSV, s.Prefix(), "Prefix does not match") + + sentence := s.(GLGSV) + assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match") + assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match") + assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match") + + assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match") + assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match") + assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match") + + assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match") + assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match") + assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match") + + assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match") + assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match") + assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match") + assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match") +} +func TestGLGSVBadSentence(t *testing.T) { + tests := []struct { + Input string + Error string + }{ + {"$GLGSV,3,1,11.2,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*77", "nmea: GLGSV invalid number of SVs in view: 11.2"}, + {"$GLGSV,A3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid total number of messages: A3"}, + {"$GLGSV,3,A1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid message number: A1"}, + {"$GLGSV,3,1,11,A03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid SV prn number: A03"}, + {"$GLGSV,3,1,11,03,A03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid elevation: A03"}, + {"$GLGSV,3,1,11,03,03,A111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid azimuth: A111"}, + {"$GLGSV,3,1,11,03,03,111,A00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid SNR: A00"}, + } + for _, tc := range tests { + _, err := Parse(tc.Input) + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, tc.Error, err.Error(), "Incorrect error message") + } + +} + +func TestGLGSVWrongSentence(t *testing.T) { + wrongMsg := "$GPXTE,A,A,4.07,L,N*6D" + sent, _ := ParseSentence(wrongMsg) + _, err := NewGLGSV(sent) + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GLGSV invalid prefix: GPXTE", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gngga.go b/vendor/github.com/adrianmo/go-nmea/gngga.go new file mode 100644 index 00000000..e0754f7b --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gngga.go @@ -0,0 +1,39 @@ +package nmea + +const ( + // PrefixGNGGA prefix + PrefixGNGGA = "GNGGA" +) + +// GNGGA is the Time, position, and fix related data of the receiver. +type GNGGA struct { + Sent + Time Time // Time of fix. + Latitude LatLong // Latitude. + Longitude LatLong // Longitude. + FixQuality string // Quality of fix. + NumSatellites int64 // Number of satellites in use. + HDOP float64 // Horizontal dilution of precision. + Altitude float64 // Altitude. + Separation float64 // Geoidal separation + DGPSAge string // Age of differential GPD data. + DGPSId string // DGPS reference station ID. +} + +// NewGNGGA constructor +func NewGNGGA(s Sent) (GNGGA, error) { + p := newParser(s, PrefixGNGGA) + return GNGGA{ + Sent: s, + Time: p.Time(0, "time"), + Latitude: p.LatLong(1, 2, "latitude"), + Longitude: p.LatLong(3, 4, "longitude"), + FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS), + NumSatellites: p.Int64(6, "number of satelites"), + HDOP: p.Float64(7, "hdop"), + Altitude: p.Float64(8, "altitude"), + Separation: p.Float64(10, "separation"), + DGPSAge: p.String(12, "dgps age"), + DGPSId: p.String(13, "dgps id"), + }, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gngga_test.go b/vendor/github.com/adrianmo/go-nmea/gngga_test.go new file mode 100644 index 00000000..e8941233 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gngga_test.go @@ -0,0 +1,71 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGNGGAGoodSentence(t *testing.T) { + goodMsg := "$GNGGA,203415.000,6325.6138,N,01021.4290,E,1,8,2.42,72.5,M,41.5,M,,*7C" + sentence, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + + lat, _ := ParseLatLong("6325.6138 N") + lon, _ := ParseLatLong("01021.4290 E") + // Attributes of the parsed sentence, and their expected values. + expected := GNGGA{ + Sent: Sent{ + Type: "GNGGA", + Fields: []string{"203415.000", "6325.6138", "N", "01021.4290", "E", "1", "8", "2.42", "72.5", "M", "41.5", "M", "", ""}, + Checksum: "7C", + Raw: "$GNGGA,203415.000,6325.6138,N,01021.4290,E,1,8,2.42,72.5,M,41.5,M,,*7C", + }, + Time: Time{true, 20, 34, 15, 0}, + Latitude: lat, + Longitude: lon, + FixQuality: GPS, + NumSatellites: 8, + HDOP: 2.42, + Altitude: 72.5, + Separation: 41.5, + DGPSAge: "", + DGPSId: "", + } + + assert.EqualValues(t, expected, sentence, "Sentence values do not match") +} + +func TestGNGGABadType(t *testing.T) { + badType := "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70" + s, err := Parse(badType) + + assert.NoError(t, err, "Unexpected error parsing sentence") + assert.NotEqual(t, "GNGGA", s.Prefix(), "Unexpected sentence type") +} + +func TestGNGGABadLatitude(t *testing.T) { + badLat := "$GNGGA,034225.077,A,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*24" + _, err := Parse(badLat) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GNGGA invalid latitude: cannot parse [A S], unknown format", err.Error(), "Error message does not match") +} + +func TestGNGGABadLongitude(t *testing.T) { + badLon := "$GNGGA,034225.077,3356.4650,S,A,E,1,03,9.7,-25.0,M,21.0,M,,0000*12" + _, err := Parse(badLon) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GNGGA invalid longitude: cannot parse [A E], unknown format", err.Error(), "Error message does not match") +} + +func TestGNGGABadFixQuality(t *testing.T) { + // Make sure bad fix mode is detected. + badMode := "$GNGGA,034225.077,3356.4650,S,15124.5567,E,5,03,9.7,-25.0,M,21.0,M,,0000*4B" + _, err := Parse(badMode) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, err.Error(), "nmea: GNGGA invalid fix quality: 5", "Error message not as expected") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gnrmc.go b/vendor/github.com/adrianmo/go-nmea/gnrmc.go new file mode 100644 index 00000000..ee5004b9 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gnrmc.go @@ -0,0 +1,40 @@ +package nmea + +const ( + // PrefixGNRMC prefix of GNRMC sentence type + PrefixGNRMC = "GNRMC" +) + +// GNRMC is the Recommended Minimum Specific GNSS data. +// http://aprs.gids.nl/nmea/#rmc +type GNRMC struct { + Sent + Time Time // Time Stamp + Validity string // validity - A-ok, V-invalid + Latitude LatLong // Latitude + Longitude LatLong // Longitude + Speed float64 // Speed in knots + Course float64 // True course + Date Date // Date + Variation float64 // Magnetic variation +} + +// NewGNRMC constructor +func NewGNRMC(s Sent) (GNRMC, error) { + p := newParser(s, PrefixGNRMC) + m := GNRMC{ + Sent: s, + Time: p.Time(0, "time"), + Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC), + Latitude: p.LatLong(2, 3, "latitude"), + Longitude: p.LatLong(4, 5, "longitude"), + Speed: p.Float64(6, "speed"), + Course: p.Float64(7, "course"), + Date: p.Date(8, "date"), + Variation: p.Float64(9, "variation"), + } + if p.EnumString(10, "direction", West, East) == West { + m.Variation = 0 - m.Variation + } + return m, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gnrmc_test.go b/vendor/github.com/adrianmo/go-nmea/gnrmc_test.go new file mode 100644 index 00000000..ab56245d --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gnrmc_test.go @@ -0,0 +1,78 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var gnrmctests = []struct { + Input string + Output GNRMC +}{ + { + "$GNRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*6E", + GNRMC{ + Time: Time{true, 22, 05, 16, 0}, + Validity: "A", + Speed: 173.8, + Course: 231.8, + Date: Date{true, 13, 06, 94}, + Variation: -4.2, + Latitude: MustParseGPS("5133.82 N"), + Longitude: MustParseGPS("00042.24 W"), + }, + }, + { + "$GNRMC,142754.0,A,4302.539570,N,07920.379823,W,0.0,,070617,0.0,E,A*21", + GNRMC{ + Time: Time{true, 14, 27, 54, 0}, + Validity: "A", + Speed: 0, + Course: 0, + Date: Date{true, 7, 6, 17}, + Variation: 0, + Latitude: MustParseGPS("4302.539570 N"), + Longitude: MustParseGPS("07920.379823 W"), + }, + }, +} + +func TestGNRMCGoodSentence(t *testing.T) { + + for _, tt := range gnrmctests { + + s, err := Parse(tt.Input) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGNRMC, s.Prefix(), "Prefix does not match") + + sentence := s.(GNRMC) + + assert.Equal(t, tt.Output.Time, sentence.Time, "Time does not match") + assert.Equal(t, tt.Output.Validity, sentence.Validity, "Status does not match") + assert.Equal(t, tt.Output.Speed, sentence.Speed, "Speed does not match") + assert.Equal(t, tt.Output.Course, sentence.Course, "Course does not match") + assert.Equal(t, tt.Output.Date, sentence.Date, "Date does not match") + assert.Equal(t, tt.Output.Variation, sentence.Variation, "Variation does not match") + assert.Equal(t, tt.Output.Latitude, sentence.Latitude, "Latitude does not match") + assert.Equal(t, tt.Output.Longitude, sentence.Longitude, "Longitude does not match") + } + +} + +func TestGNRMCBadSentence(t *testing.T) { + badMsg := "$GNRMC,220516,D,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*6B" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GNRMC invalid validity: D", err.Error(), "Incorrect error message") +} + +func TestGNRMCWrongSentence(t *testing.T) { + wrongMsg := "$GPXTE,A,A,4.07,L,N*6D" + _, err := Parse(wrongMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgga.go b/vendor/github.com/adrianmo/go-nmea/gpgga.go new file mode 100644 index 00000000..2b27c2bf --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgga.go @@ -0,0 +1,47 @@ +package nmea + +const ( + // PrefixGPGGA prefix + PrefixGPGGA = "GPGGA" + // Invalid fix quality. + Invalid = "0" + // GPS fix quality + GPS = "1" + // DGPS fix quality + DGPS = "2" +) + +// GPGGA represents fix data. +// http://aprs.gids.nl/nmea/#gga +type GPGGA struct { + Sent + Time Time // Time of fix. + Latitude LatLong // Latitude. + Longitude LatLong // Longitude. + FixQuality string // Quality of fix. + NumSatellites int64 // Number of satellites in use. + HDOP float64 // Horizontal dilution of precision. + Altitude float64 // Altitude. + Separation float64 // Geoidal separation + DGPSAge string // Age of differential GPD data. + DGPSId string // DGPS reference station ID. +} + +// NewGPGGA parses the GPGGA sentence into this struct. +// e.g: $GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*58 +func NewGPGGA(s Sent) (GPGGA, error) { + p := newParser(s, PrefixGPGGA) + return GPGGA{ + Sent: s, + Time: p.Time(0, "time"), + Latitude: p.LatLong(1, 2, "latitude"), + Longitude: p.LatLong(3, 4, "longitude"), + FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS), + NumSatellites: p.Int64(6, "number of satelites"), + HDOP: p.Float64(7, "hdap"), + Altitude: p.Float64(8, "altitude"), + Separation: p.Float64(10, "separation"), + DGPSAge: p.String(12, "dgps age"), + DGPSId: p.String(13, "dgps id"), + }, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgga_test.go b/vendor/github.com/adrianmo/go-nmea/gpgga_test.go new file mode 100644 index 00000000..1db5aeb8 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgga_test.go @@ -0,0 +1,71 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGPGGAGoodSentence(t *testing.T) { + goodMsg := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51" + sentence, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + + lat, _ := ParseLatLong("3356.4650 S") + lon, _ := ParseLatLong("15124.5567 E") + // Attributes of the parsed sentence, and their expected values. + expected := GPGGA{ + Sent: Sent{ + Type: "GPGGA", + Fields: []string{"034225.077", "3356.4650", "S", "15124.5567", "E", "1", "03", "9.7", "-25.0", "M", "21.0", "M", "", "0000"}, + Checksum: "51", + Raw: "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51", + }, + Time: Time{true, 3, 42, 25, 77}, + Latitude: lat, + Longitude: lon, + FixQuality: GPS, + NumSatellites: 03, + HDOP: 9.7, + Altitude: -25.0, + Separation: 21.0, + DGPSAge: "", + DGPSId: "0000", + } + + assert.EqualValues(t, expected, sentence, "Sentence values do not match") +} + +func TestGPGGABadType(t *testing.T) { + badType := "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70" + s, err := Parse(badType) + + assert.NoError(t, err, "Unexpected error parsing sentence") + assert.NotEqual(t, "GPGGA", s.Prefix(), "Unexpected sentence type") +} + +func TestGPGGABadLatitude(t *testing.T) { + badLat := "$GPGGA,034225.077,A,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*3A" + _, err := Parse(badLat) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPGGA invalid latitude: cannot parse [A S], unknown format", err.Error(), "Error message does not match") +} + +func TestGPGGABadLongitude(t *testing.T) { + badLon := "$GPGGA,034225.077,3356.4650,S,A,E,1,03,9.7,-25.0,M,21.0,M,,0000*0C" + _, err := Parse(badLon) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPGGA invalid longitude: cannot parse [A E], unknown format", err.Error(), "Error message does not match") +} + +func TestGPGGABadFixQuality(t *testing.T) { + // Make sure bad fix mode is detected. + badMode := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,5,03,9.7,-25.0,M,21.0,M,,0000*55" + _, err := Parse(badMode) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, err.Error(), "nmea: GPGGA invalid fix quality: 5", "Error message not as expected") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgll.go b/vendor/github.com/adrianmo/go-nmea/gpgll.go new file mode 100644 index 00000000..803e63c9 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgll.go @@ -0,0 +1,32 @@ +package nmea + +const ( + // PrefixGPGLL prefix for GPGLL sentence type + PrefixGPGLL = "GPGLL" + // ValidGLL character + ValidGLL = "A" + // InvalidGLL character + InvalidGLL = "V" +) + +// GPGLL is Geographic Position, Latitude / Longitude and time. +// http://aprs.gids.nl/nmea/#gll +type GPGLL struct { + Sent + Latitude LatLong // Latitude + Longitude LatLong // Longitude + Time Time // Time Stamp + Validity string // validity - A-valid +} + +// NewGPGLL constructor +func NewGPGLL(s Sent) (GPGLL, error) { + p := newParser(s, PrefixGPGLL) + return GPGLL{ + Sent: s, + Latitude: p.LatLong(0, 1, "latitude"), + Longitude: p.LatLong(2, 3, "longitude"), + Time: p.Time(4, "time"), + Validity: p.EnumString(5, "validity", ValidGLL, InvalidGLL), + }, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgll_test.go b/vendor/github.com/adrianmo/go-nmea/gpgll_test.go new file mode 100644 index 00000000..a39c8c0a --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgll_test.go @@ -0,0 +1,38 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGPGLLGoodSentence(t *testing.T) { + goodMsg := "$GPGLL,3926.7952,N,12000.5947,W,022732,A,A*58" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGPGLL, s.Prefix(), "Prefix does not match") + + sentence := s.(GPGLL) + + assert.Equal(t, "3926.7952", sentence.Latitude.PrintGPS(), "Latitude does not match") + assert.Equal(t, "12000.5947", sentence.Longitude.PrintGPS(), "Longitude does not match") + assert.Equal(t, Time{true, 2, 27, 32, 0}, sentence.Time, "Time does not match") + assert.Equal(t, "A", sentence.Validity, "Status does not match") +} + +func TestGPGLLBadSentence(t *testing.T) { + badMsg := "$GPGLL,3926.7952,N,12000.5947,W,022732,D,A*5D" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPGLL invalid validity: D", err.Error(), "Incorrect error message") +} + +func TestGPGLLWrongSentence(t *testing.T) { + wrongMsg := "$GPXTE,A,A,4.07,L,N*6D" + _, err := Parse(wrongMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgsa.go b/vendor/github.com/adrianmo/go-nmea/gpgsa.go new file mode 100644 index 00000000..79d152f9 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgsa.go @@ -0,0 +1,49 @@ +package nmea + +const ( + // PrefixGPGSA prefix of GPGSA sentence type + PrefixGPGSA = "GPGSA" + // Auto - Field 1, auto or manual fix. + Auto = "A" + // Manual - Field 1, auto or manual fix. + Manual = "M" + // FixNone - Field 2, fix type. + FixNone = "1" + // Fix2D - Field 2, fix type. + Fix2D = "2" + // Fix3D - Field 2, fix type. + Fix3D = "3" +) + +// GPGSA represents overview satellite data. +// http://aprs.gids.nl/nmea/#gsa +type GPGSA struct { + Sent + Mode string // The selection mode. + FixType string // The fix type. + SV []string // List of satellite PRNs used for this fix. + PDOP float64 // Dilution of precision. + HDOP float64 // Horizontal dilution of precision. + VDOP float64 // Vertical dilution of precision. +} + +// NewGPGSA parses the GPGSA sentence into this struct. +func NewGPGSA(s Sent) (GPGSA, error) { + p := newParser(s, PrefixGPGSA) + m := GPGSA{ + Sent: s, + Mode: p.EnumString(0, "selection mode", Auto, Manual), + FixType: p.EnumString(1, "fix type", FixNone, Fix2D, Fix3D), + } + // Satellites in view. + for i := 2; i < 14; i++ { + if v := p.String(i, "satelite in view"); v != "" { + m.SV = append(m.SV, v) + } + } + // Dilution of precision. + m.PDOP = p.Float64(14, "pdop") + m.HDOP = p.Float64(15, "hdop") + m.VDOP = p.Float64(16, "vdop") + return m, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgsa_test.go b/vendor/github.com/adrianmo/go-nmea/gpgsa_test.go new file mode 100644 index 00000000..ae1e9aa8 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgsa_test.go @@ -0,0 +1,50 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGPGSAGoodSentence(t *testing.T) { + goodMsg := "$GPGSA,A,3,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*36" + sentence, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + + // Attributes of the parsed sentence, and their expected values. + expected := GPGSA{ + Sent: Sent{ + Type: "GPGSA", + Fields: []string{"A", "3", "22", "19", "18", "27", "14", "03", "", "", "", "", "", "", "3.1", "2.0", "2.4"}, + Checksum: "36", + Raw: "$GPGSA,A,3,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*36", + }, + Mode: Auto, + FixType: Fix3D, + PDOP: 3.1, + HDOP: 2.0, + VDOP: 2.4, + SV: []string{"22", "19", "18", "27", "14", "03"}, + } + + assert.EqualValues(t, expected, sentence, "Sentence values do not match") +} + +func TestGPGSABadMode(t *testing.T) { + // Make sure bad fix mode is detected. + badMode := "$GPGSA,F,3,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*31" + _, err := Parse(badMode) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPGSA invalid selection mode: F", err.Error(), "Error message does not match") +} + +func TestGPGSABadFix(t *testing.T) { + // Make sure bad fix type is detected. + badFixType := "$GPGSA,A,6,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*33" + _, err := Parse(badFixType) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPGSA invalid fix type: 6", err.Error(), "Error message does not match") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgsv.go b/vendor/github.com/adrianmo/go-nmea/gpgsv.go new file mode 100644 index 00000000..85e51145 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgsv.go @@ -0,0 +1,47 @@ +package nmea + +const ( + // PrefixGPGSV prefix + PrefixGPGSV = "GPGSV" +) + +// GPGSV represents the GPS Satellites in view +// http://aprs.gids.nl/nmea/#gpgsv +type GPGSV struct { + Sent + TotalMessages int64 // Total number of messages of this type in this cycle + MessageNumber int64 // Message number + NumberSVsInView int64 // Total number of SVs in view + Info []GPGSVInfo // visible satellite info (0-4 of these) +} + +// GPGSVInfo represents information about a visible satellite +type GPGSVInfo struct { + SVPRNNumber int64 // SV PRN number, pseudo-random noise or gold code + Elevation int64 // Elevation in degrees, 90 maximum + Azimuth int64 // Azimuth, degrees from true north, 000 to 359 + SNR int64 // SNR, 00-99 dB (null when not tracking) +} + +// NewGPGSV constructor +func NewGPGSV(s Sent) (GPGSV, error) { + p := newParser(s, PrefixGPGSV) + m := GPGSV{ + Sent: s, + TotalMessages: p.Int64(0, "total number of messages"), + MessageNumber: p.Int64(1, "message number"), + NumberSVsInView: p.Int64(2, "number of SVs in view"), + } + for i := 0; i < 4; i++ { + if 5*i+4 > len(m.Fields) { + break + } + m.Info = append(m.Info, GPGSVInfo{ + SVPRNNumber: p.Int64(3+i*4, "SV prn number"), + Elevation: p.Int64(4+i*4, "elevation"), + Azimuth: p.Int64(5+i*4, "azimuth"), + SNR: p.Int64(6+i*4, "SNR"), + }) + } + return m, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpgsv_test.go b/vendor/github.com/adrianmo/go-nmea/gpgsv_test.go new file mode 100644 index 00000000..bf93b94c --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpgsv_test.go @@ -0,0 +1,96 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGPGSVGoodSentence(t *testing.T) { + goodMsg := "$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*77" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGPGSV, s.Prefix(), "Prefix does not match") + + sentence := s.(GPGSV) + assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match") + assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match") + assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match") + + assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match") + assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match") + assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match") + + assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match") + assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match") + assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match") + + assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match") + assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match") + assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match") + assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match") + + assert.Equal(t, int64(13), sentence.Info[3].SVPRNNumber, "Number of Info[3] SV PRN does not match") + assert.Equal(t, int64(6), sentence.Info[3].Elevation, "Number of Info[3] Elevation does not match") + assert.Equal(t, int64(292), sentence.Info[3].Azimuth, "Number of Info[3] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[3].SNR, "Number of Info[3] SNR does not match") +} + +func TestGPGSVShort(t *testing.T) { + goodMsg := "$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12*4A" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGPGSV, s.Prefix(), "Prefix does not match") + + sentence := s.(GPGSV) + assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match") + assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match") + assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match") + + assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match") + assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match") + assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match") + + assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match") + assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match") + assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match") + assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match") + + assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match") + assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match") + assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match") + assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match") +} +func TestGPGSVBadSentence(t *testing.T) { + tests := []struct { + Input string + Error string + }{ + {"$GPGSV,3,1,11.2,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*6b", "nmea: GPGSV invalid number of SVs in view: 11.2"}, + {"$GPGSV,A3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid total number of messages: A3"}, + {"$GPGSV,3,A1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid message number: A1"}, + {"$GPGSV,3,1,11,A03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid SV prn number: A03"}, + {"$GPGSV,3,1,11,03,A03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid elevation: A03"}, + {"$GPGSV,3,1,11,03,03,A111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid azimuth: A111"}, + {"$GPGSV,3,1,11,03,03,111,A00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid SNR: A00"}, + } + for _, tc := range tests { + _, err := Parse(tc.Input) + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, tc.Error, err.Error(), "Incorrect error message") + } + +} + +func TestGPGSVWrongSentence(t *testing.T) { + wrongMsg := "$GPXTE,A,A,4.07,L,N*6D" + sent, _ := ParseSentence(wrongMsg) + _, err := NewGPGSV(sent) + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPGSV invalid prefix: GPXTE", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gprmc.go b/vendor/github.com/adrianmo/go-nmea/gprmc.go new file mode 100644 index 00000000..72be1584 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gprmc.go @@ -0,0 +1,44 @@ +package nmea + +const ( + // PrefixGPRMC prefix of GPRMC sentence type + PrefixGPRMC = "GPRMC" + // ValidRMC character + ValidRMC = "A" + // InvalidRMC character + InvalidRMC = "V" +) + +// GPRMC is the Recommended Minimum Specific GNSS data. +// http://aprs.gids.nl/nmea/#rmc +type GPRMC struct { + Sent + Time Time // Time Stamp + Validity string // validity - A-ok, V-invalid + Latitude LatLong // Latitude + Longitude LatLong // Longitude + Speed float64 // Speed in knots + Course float64 // True course + Date Date // Date + Variation float64 // Magnetic variation +} + +// NewGPRMC constructor +func NewGPRMC(s Sent) (GPRMC, error) { + p := newParser(s, PrefixGPRMC) + m := GPRMC{ + Sent: s, + Time: p.Time(0, "time"), + Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC), + Latitude: p.LatLong(2, 3, "latitude"), + Longitude: p.LatLong(4, 5, "longitude"), + Speed: p.Float64(6, "speed"), + Course: p.Float64(7, "course"), + Date: p.Date(8, "date"), + Variation: p.Float64(9, "variation"), + } + if p.EnumString(10, "variation", West, East) == West { + m.Variation = 0 - m.Variation + } + return m, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gprmc_test.go b/vendor/github.com/adrianmo/go-nmea/gprmc_test.go new file mode 100644 index 00000000..e73588c6 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gprmc_test.go @@ -0,0 +1,86 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func MustParseGPS(s string) LatLong { + ll, err := ParseGPS(s) + if err != nil { + panic(err) + } + return ll +} + +var gprmctests = []struct { + Input string + Output GPRMC +}{ + { + "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70", + GPRMC{ + Time: Time{true, 22, 05, 16, 0}, + Validity: "A", + Speed: 173.8, + Course: 231.8, + Date: Date{true, 13, 6, 94}, + Variation: -4.2, + Latitude: MustParseGPS("5133.82 N"), + Longitude: MustParseGPS("00042.24 W"), + }, + }, + { + "$GPRMC,142754.0,A,4302.539570,N,07920.379823,W,0.0,,070617,0.0,E,A*3F", + GPRMC{ + Time: Time{true, 14, 27, 54, 0}, + Validity: "A", + Speed: 0, + Course: 0, + Date: Date{true, 7, 6, 17}, + Variation: 0, + Latitude: MustParseGPS("4302.539570 N"), + Longitude: MustParseGPS("07920.379823 W"), + }, + }, +} + +func TestGPRMCGoodSentence(t *testing.T) { + + for _, tt := range gprmctests { + + s, err := Parse(tt.Input) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGPRMC, s.Prefix(), "Prefix does not match") + + sentence := s.(GPRMC) + + assert.Equal(t, tt.Output.Time, sentence.Time, "Time does not match") + assert.Equal(t, tt.Output.Validity, sentence.Validity, "Status does not match") + assert.Equal(t, tt.Output.Speed, sentence.Speed, "Speed does not match") + assert.Equal(t, tt.Output.Course, sentence.Course, "Course does not match") + assert.Equal(t, tt.Output.Date, sentence.Date, "Date does not match") + assert.Equal(t, tt.Output.Variation, sentence.Variation, "Variation does not match") + assert.Equal(t, tt.Output.Latitude, sentence.Latitude, "Latitude does not match") + assert.Equal(t, tt.Output.Longitude, sentence.Longitude, "Longitude does not match") + } + +} + +func TestGPRMCBadSentence(t *testing.T) { + badMsg := "$GPRMC,220516,D,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*75" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPRMC invalid validity: D", err.Error(), "Incorrect error message") +} + +func TestGPRMCWrongSentence(t *testing.T) { + wrongMsg := "$GPXTE,A,A,4.07,L,N*6D" + _, err := Parse(wrongMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpvtg.go b/vendor/github.com/adrianmo/go-nmea/gpvtg.go new file mode 100644 index 00000000..097d806a --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpvtg.go @@ -0,0 +1,29 @@ +package nmea + +const ( + // PrefixGPVTG prefix + PrefixGPVTG = "GPVTG" +) + +// GPVTG represents track & speed data. +// http://aprs.gids.nl/nmea/#vtg +type GPVTG struct { + Sent + TrueTrack float64 + MagneticTrack float64 + GroundSpeedKnots float64 + GroundSpeedKPH float64 +} + +// NewGPVTG parses the GPVTG sentence into this struct. +// e.g: $GPVTG,360.0,T,348.7,M,000.0,N,000.0,K*43 +func NewGPVTG(s Sent) (GPVTG, error) { + p := newParser(s, PrefixGPVTG) + return GPVTG{ + Sent: s, + TrueTrack: p.Float64(0, "true track"), + MagneticTrack: p.Float64(2, "magnetic track"), + GroundSpeedKnots: p.Float64(4, "ground speed (knots)"), + GroundSpeedKPH: p.Float64(6, "ground speed (km/h)"), + }, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpvtg_test.go b/vendor/github.com/adrianmo/go-nmea/gpvtg_test.go new file mode 100644 index 00000000..a49d47f8 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpvtg_test.go @@ -0,0 +1,38 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGPVTGGoodSentence(t *testing.T) { + goodMsg := "$GPVTG,45.5,T,67.5,M,30.45,N,56.40,K*4B" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGPVTG, s.Prefix(), "Prefix does not match") + + sentence := s.(GPVTG) + + assert.Equal(t, 45.5, sentence.TrueTrack, "True track does not match") + assert.Equal(t, 67.5, sentence.MagneticTrack, "Magnetic track does not match") + assert.Equal(t, 30.45, sentence.GroundSpeedKnots, "Ground speed (knots) does not match") + assert.Equal(t, 56.40, sentence.GroundSpeedKPH, "Ground speed (km/h) does not match") +} + +func TestGPVTGBadSentence(t *testing.T) { + badMsg := "$GPVTG,T,45.5,67.5,M,30.45,N,56.40,K*4B" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPVTG invalid true track: T", err.Error(), "Incorrect error message") +} + +func TestGPVTGWrongSentence(t *testing.T) { + wrongMsg := "$GPXTE,A,A,4.07,L,N*6D" + _, err := Parse(wrongMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpzda.go b/vendor/github.com/adrianmo/go-nmea/gpzda.go new file mode 100644 index 00000000..16a4450f --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpzda.go @@ -0,0 +1,32 @@ +package nmea + +const ( + // PrefixGPZDA prefix + PrefixGPZDA = "GPZDA" +) + +// GPZDA represents date & time data. +// http://aprs.gids.nl/nmea/#zda +type GPZDA struct { + Sent + Time Time + Day int64 + Month int64 + Year int64 + OffsetHours int64 // Local time zone offset from GMT, hours + OffsetMinutes int64 // Local time zone offset from GMT, minutes +} + +// NewGPZDA constructor +func NewGPZDA(s Sent) (GPZDA, error) { + p := newParser(s, PrefixGPZDA) + return GPZDA{ + Sent: s, + Time: p.Time(0, "time"), + Day: p.Int64(1, "day"), + Month: p.Int64(2, "month"), + Year: p.Int64(3, "year"), + OffsetHours: p.Int64(4, "offset (hours)"), + OffsetMinutes: p.Int64(5, "offset (minutes)"), + }, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gpzda_test.go b/vendor/github.com/adrianmo/go-nmea/gpzda_test.go new file mode 100644 index 00000000..5b853644 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gpzda_test.go @@ -0,0 +1,40 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGPZDAGoodSentence(t *testing.T) { + goodMsg := "$GPZDA,172809.456,12,07,1996,00,00*57" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixGPZDA, s.Prefix(), "Prefix does not match") + + sentence := s.(GPZDA) + + assert.Equal(t, Time{true, 17, 28, 9, 456}, sentence.Time, "Time does not match") + assert.Equal(t, int64(12), sentence.Day, "Day does not match") + assert.Equal(t, int64(7), sentence.Month, "Month does not match") + assert.Equal(t, int64(1996), sentence.Year, "Yeah does not match") + assert.Equal(t, int64(0), sentence.OffsetHours, "Offset (hours) does not match") + assert.Equal(t, int64(0), sentence.OffsetMinutes, "Offset (minutes) does not match") +} + +func TestGPZDABadSentence(t *testing.T) { + badMsg := "$GPZDA,220516,D,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*76" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: GPZDA invalid day: D", err.Error(), "Incorrect error message") +} + +func TestGPZDAWrongSentence(t *testing.T) { + wrongMsg := "$GPXTE,A,A,4.07,L,N*6D" + _, err := Parse(wrongMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/parser.go b/vendor/github.com/adrianmo/go-nmea/parser.go new file mode 100644 index 00000000..e73d6106 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/parser.go @@ -0,0 +1,141 @@ +package nmea + +import ( + "fmt" + "strconv" +) + +// parser provides a simple way of accessing and parsing +// sentence fields +type parser struct { + Sent + prefix string + err error +} + +// newParser constructor +func newParser(s Sent, prefix string) *parser { + p := &parser{Sent: s, prefix: prefix} + if p.Type != prefix { + p.SetErr("prefix", p.Type) + } + return p +} + +// Err returns the first error encounterd during the parser's usage. +func (p *parser) Err() error { + return p.err +} + +// SetErr assigns an error. Calling this method has no +// effect if there is already an error. +func (p *parser) SetErr(context, value string) { + if p.err == nil { + p.err = fmt.Errorf("nmea: %s invalid %s: %s", p.prefix, context, value) + } +} + +// String returns the field value at the specified index. +func (p *parser) String(i int, context string) string { + if p.err != nil { + return "" + } + if i < 0 || i >= len(p.Fields) { + p.SetErr(context, "index out of range") + return "" + } + return p.Fields[i] +} + +// EnumString returns the field value at the specified index. +// An error occurs if the value is not one of the options. +func (p *parser) EnumString(i int, context string, options ...string) string { + s := p.String(i, context) + if p.err != nil { + return "" + } + for _, o := range options { + if o == s { + return s + } + } + p.SetErr(context, s) + return "" +} + +// Int64 returns the int64 value at the specified index. +// If the value is an emtpy string, 0 is returned. +func (p *parser) Int64(i int, context string) int64 { + s := p.String(i, context) + if p.err != nil { + return 0 + } + if s == "" { + return 0 + } + v, err := strconv.ParseInt(s, 10, 64) + if err != nil { + p.SetErr(context, s) + } + return v +} + +// Float64 returns the float64 value at the specified index. +// If the value is an empty string, 0 is returned. +func (p *parser) Float64(i int, context string) float64 { + s := p.String(i, context) + if p.err != nil { + return 0 + } + if s == "" { + return 0 + } + v, err := strconv.ParseFloat(s, 64) + if err != nil { + p.SetErr(context, s) + } + return v +} + +// Time returns the Time value at the specified index. +// If the value is empty, the Time is marked as invalid. +func (p *parser) Time(i int, context string) Time { + s := p.String(i, context) + if p.err != nil { + return Time{} + } + v, err := ParseTime(s) + if err != nil { + p.SetErr(context, s) + } + return v +} + +// Date returns the Date value at the specified index. +// If the value is empty, the Date is marked as invalid. +func (p *parser) Date(i int, context string) Date { + s := p.String(i, context) + if p.err != nil { + return Date{} + } + v, err := ParseDate(s) + if err != nil { + p.SetErr(context, s) + } + return v +} + +// LatLong returns the coordinate value of the specified fields. +func (p *parser) LatLong(i, j int, context string) LatLong { + a := p.String(i, context) + b := p.String(j, context) + if p.err != nil { + return 0 + } + s := fmt.Sprintf("%s %s", a, b) + v, err := ParseLatLong(s) + if err != nil { + p.SetErr(context, err.Error()) + } + return v +} diff --git a/vendor/github.com/adrianmo/go-nmea/parser_test.go b/vendor/github.com/adrianmo/go-nmea/parser_test.go new file mode 100644 index 00000000..6acdd4e1 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/parser_test.go @@ -0,0 +1,226 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var parsertests = []struct { + name string + fields []string + expected interface{} + hasErr bool + parse func(p *parser) interface{} +}{ + { + name: "String", + fields: []string{"foo", "bar"}, + expected: "bar", + parse: func(p *parser) interface{} { + return p.String(1, "") + }, + }, + { + name: "String out of range", + fields: []string{"wot"}, + expected: "", + hasErr: true, + parse: func(p *parser) interface{} { + return p.String(5, "thing") + }, + }, + { + name: "String with existing error", + expected: "", + hasErr: true, + parse: func(p *parser) interface{} { + p.SetErr("context", "value") + return p.String(123, "blah") + }, + }, + { + name: "EnumString", + fields: []string{"a", "b", "c"}, + expected: "b", + parse: func(p *parser) interface{} { + return p.EnumString(1, "context", "b", "d") + }, + }, + { + name: "EnumString invalid", + fields: []string{"a", "b", "c"}, + expected: "", + hasErr: true, + parse: func(p *parser) interface{} { + return p.EnumString(1, "context", "x", "y") + }, + }, + { + name: "EnumString with existing error", + fields: []string{"a", "b", "c"}, + expected: "", + hasErr: true, + parse: func(p *parser) interface{} { + p.SetErr("context", "value") + return p.EnumString(1, "context", "a", "b") + }, + }, + { + name: "Int64", + fields: []string{"123"}, + expected: int64(123), + parse: func(p *parser) interface{} { + return p.Int64(0, "context") + }, + }, + { + name: "Int64 empty field is zero", + fields: []string{""}, + expected: int64(0), + parse: func(p *parser) interface{} { + return p.Int64(0, "context") + }, + }, + { + name: "Int64 invalid", + fields: []string{"abc"}, + expected: int64(0), + hasErr: true, + parse: func(p *parser) interface{} { + return p.Int64(0, "context") + }, + }, + { + name: "Int64 with existing error", + fields: []string{"123"}, + expected: int64(0), + hasErr: true, + parse: func(p *parser) interface{} { + p.SetErr("context", "value") + return p.Int64(0, "context") + }, + }, + { + name: "Float64", + fields: []string{"123.123"}, + expected: float64(123.123), + parse: func(p *parser) interface{} { + return p.Float64(0, "context") + }, + }, + { + name: "Float64 empty field is zero", + fields: []string{""}, + expected: float64(0), + parse: func(p *parser) interface{} { + return p.Float64(0, "context") + }, + }, + { + name: "Float64 invalid", + fields: []string{"abc"}, + expected: float64(0), + hasErr: true, + parse: func(p *parser) interface{} { + return p.Float64(0, "context") + }, + }, + { + name: "Float64 with existing error", + fields: []string{"123.123"}, + expected: float64(0), + hasErr: true, + parse: func(p *parser) interface{} { + p.SetErr("context", "value") + return p.Float64(0, "context") + }, + }, + { + name: "Time", + fields: []string{"123456"}, + expected: Time{true, 12, 34, 56, 0}, + parse: func(p *parser) interface{} { + return p.Time(0, "context") + }, + }, + { + name: "Time empty field is zero", + fields: []string{""}, + expected: Time{}, + parse: func(p *parser) interface{} { + return p.Time(0, "context") + }, + }, + { + name: "Time with existing error", + fields: []string{"123456"}, + expected: Time{}, + hasErr: true, + parse: func(p *parser) interface{} { + p.SetErr("context", "value") + return p.Time(0, "context") + }, + }, + { + name: "Time invalid", + fields: []string{"wrong"}, + expected: Time{}, + hasErr: true, + parse: func(p *parser) interface{} { + return p.Time(0, "context") + }, + }, + { + name: "Date", + fields: []string{"010203"}, + expected: Date{true, 1, 2, 3}, + parse: func(p *parser) interface{} { + return p.Date(0, "context") + }, + }, + { + name: "Date empty field is zero", + fields: []string{""}, + expected: Date{}, + parse: func(p *parser) interface{} { + return p.Date(0, "context") + }, + }, + { + name: "Date invalid", + fields: []string{"Hello"}, + expected: Date{}, + hasErr: true, + parse: func(p *parser) interface{} { + return p.Date(0, "context") + }, + }, + { + name: "Date with existing error", + fields: []string{"010203"}, + expected: Date{}, + hasErr: true, + parse: func(p *parser) interface{} { + p.SetErr("context", "value") + return p.Date(0, "context") + }, + }, +} + +func TestParser(t *testing.T) { + for _, tt := range parsertests { + t.Run(tt.name, func(t *testing.T) { + p := newParser(Sent{ + Type: "type", + Fields: tt.fields, + }, "type") + assert.Equal(t, tt.expected, tt.parse(p)) + if tt.hasErr { + assert.Error(t, p.Err()) + } else { + assert.NoError(t, p.Err()) + } + }) + } +} diff --git a/vendor/github.com/adrianmo/go-nmea/pgrme.go b/vendor/github.com/adrianmo/go-nmea/pgrme.go new file mode 100644 index 00000000..2de745e3 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/pgrme.go @@ -0,0 +1,38 @@ +package nmea + +const ( + // PrefixPGRME prefix for PGRME sentence type + PrefixPGRME = "PGRME" + // ErrorUnit must be meters (M) + ErrorUnit = "M" +) + +// PGRME is Estimated Position Error (Garmin proprietary sentence) +// http://aprs.gids.nl/nmea/#rme +type PGRME struct { + Sent + Horizontal float64 // Estimated horizontal position error (HPE) in metres + Vertical float64 // Estimated vertical position error (VPE) in metres + Spherical float64 // Overall spherical equivalent position error in meters +} + +// NewPGRME constructor +func NewPGRME(s Sent) (PGRME, error) { + p := newParser(s, PrefixPGRME) + + horizontal := p.Float64(0, "horizontal error") + _ = p.EnumString(1, "horizontal error unit", ErrorUnit) + + vertial := p.Float64(2, "vertical error") + _ = p.EnumString(3, "vertical error unit", ErrorUnit) + + spherical := p.Float64(4, "spherical error") + _ = p.EnumString(5, "spherical error unit", ErrorUnit) + + return PGRME{ + Sent: s, + Horizontal: horizontal, + Vertical: vertial, + Spherical: spherical, + }, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/pgrme_test.go b/vendor/github.com/adrianmo/go-nmea/pgrme_test.go new file mode 100644 index 00000000..4fbeba5b --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/pgrme_test.go @@ -0,0 +1,70 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPGRMEGoodSentence(t *testing.T) { + goodMsg := "$PGRME,3.3,M,4.9,M,6.0,M*25" + s, err := Parse(goodMsg) + + assert.NoError(t, err, "Unexpected error parsing good sentence") + assert.Equal(t, PrefixPGRME, s.Prefix(), "Prefix does not match") + + sentence := s.(PGRME) + + assert.Equal(t, 3.3, sentence.Horizontal, "Horizontal error does not match") + assert.Equal(t, 4.9, sentence.Vertical, "Vertical error does not match") + assert.Equal(t, 6.0, sentence.Spherical, "Spherical error does not match") + +} + +func TestPGRMEInvalidHorizontalError(t *testing.T) { + badMsg := "$PGRME,A,M,4.9,M,6.0,M*4A" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: PGRME invalid horizontal error: A", err.Error(), "Incorrect error message") +} + +func TestPGRMEInvalidHorizontalErrorUnit(t *testing.T) { + badMsg := "$PGRME,3.3,A,4.9,M,6.0,M*29" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: PGRME invalid horizontal error unit: A", err.Error(), "Incorrect error message") +} + +func TestPGRMEInvalidVerticalError(t *testing.T) { + badMsg := "$PGRME,3.3,M,A,M,6.0,M*47" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: PGRME invalid vertical error: A", err.Error(), "Incorrect error message") +} + +func TestPGRMEInvalidVerticalErrorUnit(t *testing.T) { + badMsg := "$PGRME,3.3,M,4.9,A,6.0,M*29" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: PGRME invalid vertical error unit: A", err.Error(), "Incorrect error message") +} + +func TestPGRMEInvalidSphericalError(t *testing.T) { + badMsg := "$PGRME,3.3,M,4.9,M,A,M*4C" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: PGRME invalid spherical error: A", err.Error(), "Incorrect error message") +} + +func TestPGRMEInvalidSphericalErrorUnit(t *testing.T) { + badMsg := "$PGRME,3.3,M,4.9,M,6.0,A*29" + _, err := Parse(badMsg) + + assert.Error(t, err, "Parse error not returned") + assert.Equal(t, "nmea: PGRME invalid spherical error unit: A", err.Error(), "Incorrect error message") +} diff --git a/vendor/github.com/adrianmo/go-nmea/sentence.go b/vendor/github.com/adrianmo/go-nmea/sentence.go new file mode 100644 index 00000000..a611524a --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/sentence.go @@ -0,0 +1,118 @@ +package nmea + +import ( + "fmt" + "strings" +) + +const ( + // SentenceStart is the token to indicate the start of a sentence. + SentenceStart = "$" + + // FieldSep is the token to delimit fields of a sentence. + FieldSep = "," + + // ChecksumSep is the token to delimit the checksum of a sentence. + ChecksumSep = "*" +) + +// Message interface for all NMEA sentence +type Message interface { + fmt.Stringer + Sentence() Sent + Prefix() string + Validate() error +} + +// Sent contains the information about the NMEA sentence +type Sent struct { + Type string // The sentence type (e.g $GPGSA) + Fields []string // Array of fields + Checksum string // The Checksum + Raw string // The raw NMEA sentence received +} + +// Sentence returns the Messages Sent +func (s Sent) Sentence() Sent { return s } + +// Prefix returns the type of the message +func (s Sent) Prefix() string { return s.Type } + +// String formats the sentence into a string +func (s Sent) String() string { return s.Raw } + +// Validate returns an error if the sentence is not valid +func (s Sent) Validate() error { return nil } + +// ParseSentence parses a raw message into it's fields +func ParseSentence(raw string) (Sent, error) { + startIndex := strings.Index(raw, SentenceStart) + if startIndex != 0 { + return Sent{}, fmt.Errorf("nmea: sentence does not start with a '$'") + } + sumSepIndex := strings.Index(raw, ChecksumSep) + if sumSepIndex == -1 { + return Sent{}, fmt.Errorf("nmea: sentence does not contain checksum separator") + } + var ( + fieldsRaw = raw[startIndex+1 : sumSepIndex] + fields = strings.Split(fieldsRaw, FieldSep) + checksumRaw = strings.ToUpper(raw[sumSepIndex+1:]) + checksum = xorChecksum(fieldsRaw) + ) + // Validate the checksum + if checksum != checksumRaw { + return Sent{}, fmt.Errorf( + "nmea: sentence checksum mismatch [%s != %s]", checksum, checksumRaw) + } + return Sent{ + Type: fields[0], + Fields: fields[1:], + Checksum: checksumRaw, + Raw: raw, + }, nil +} + +// xor all the bytes in a string an return it +// as an uppercase hex string +func xorChecksum(s string) string { + var checksum uint8 + for i := 0; i < len(s); i++ { + checksum ^= s[i] + } + return fmt.Sprintf("%02X", checksum) +} + +// Parse parses the given string into the correct sentence type. +func Parse(raw string) (Message, error) { + s, err := ParseSentence(raw) + if err != nil { + return nil, err + } + switch s.Type { + case PrefixGPRMC: + return NewGPRMC(s) + case PrefixGNRMC: + return NewGNRMC(s) + case PrefixGPGGA: + return NewGPGGA(s) + case PrefixGNGGA: + return NewGNGGA(s) + case PrefixGPGSA: + return NewGPGSA(s) + case PrefixGPGLL: + return NewGPGLL(s) + case PrefixGPVTG: + return NewGPVTG(s) + case PrefixGPZDA: + return NewGPZDA(s) + case PrefixPGRME: + return NewPGRME(s) + case PrefixGPGSV: + return NewGPGSV(s) + case PrefixGLGSV: + return NewGLGSV(s) + default: + return nil, fmt.Errorf("nmea: sentence type '%s' not implemented", s.Type) + } +} diff --git a/vendor/github.com/adrianmo/go-nmea/sentence_test.go b/vendor/github.com/adrianmo/go-nmea/sentence_test.go new file mode 100644 index 00000000..0560026a --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/sentence_test.go @@ -0,0 +1,96 @@ +package nmea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestChecksumOK(t *testing.T) { + _, err := ParseSentence("$GPFOO,1,2,3.3,x,y,zz,*51") + assert.NoError(t, err, "Checksum check failed") +} + +func TestChecksumBad(t *testing.T) { + _, err := ParseSentence("$GPFOO,1,2,3.4,x,y,zz,*51") + assert.Error(t, err, "Checksum check failed") +} + +func TestChecksumBadRaw(t *testing.T) { + badRaw := "$GPFOO,1,2,3.3,x,y,zz,*33" + _, err := Parse(badRaw) + assert.Error(t, err, "Expected 'Sentence checksum mismatch [51 != 33]'") +} + +func TestBadStartCharacter(t *testing.T) { + // Check that a bad start character is flagged. + rawBadStart := "%GPFOO,1,2,3,x,y,z*1A" + _, err := Parse(rawBadStart) + assert.Error(t, err, "Expected 'Sentence does not start with a '$''") +} + +func TestBadChecksumDelimiter(t *testing.T) { + // Check that a bad checksum delimiter is flagged. + rawBadSumSep := "$GPFOO,1,2,3,x,y,z" + _, err := Parse(rawBadSumSep) + assert.Error(t, err, "Expected 'Sentence does not contain single checksum separator'") +} + +func TestGoodParsing(t *testing.T) { + // Check for good parsing. + raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C" + _, err := Parse(raw) + assert.NoError(t, err, "Parse error") +} + +func TestGoodFields(t *testing.T) { + raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C" + expectedFields := []string{"235236", "A", "3925.9479", "N", "11945.9211", "W", "44.7", "153.6", "250905", "15.2", "E", "A"} + m, _ := Parse(raw) + assert.EqualValues(t, expectedFields, m.Sentence().Fields, "Got '%q', expected '%q'", m.Sentence().Fields, expectedFields) +} + +func TestGoodSentenceType(t *testing.T) { + raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C" + expected := "GPRMC" + m, _ := Parse(raw) + assert.Equal(t, expected, m.Prefix(), "Got '%s', expected '%s'", m.Prefix(), expected) +} + +func TestGoodRawSentence(t *testing.T) { + raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C" + m, _ := Parse(raw) + assert.Equal(t, raw, m.String(), "Bad raw sentence") +} + +func TestMultipleStartDelimiterSentence(t *testing.T) { + raw := "$$$$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C" + result, err := Parse(raw) + assert.Nil(t, result, "Result should be nil") + assert.NotNil(t, err, "Err should be an error") + assert.Equal(t, "nmea: sentence checksum mismatch [28 != 0C]", err.Error(), "Error sentence mismatch") +} + +func TestNoStartDelimiterSentence(t *testing.T) { + raw := "abc$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C" + result, err := Parse(raw) + assert.Nil(t, result, "Result should be nil") + assert.NotNil(t, err, "Err should be an error") + assert.Equal(t, "nmea: sentence does not start with a '$'", err.Error(), "Error sentence mismatch") +} + +func TestNoContainDelimiterSentence(t *testing.T) { + raw := "GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C" + result, err := Parse(raw) + assert.Nil(t, result, "Result should be nil") + assert.NotNil(t, err, "Err should be an error") + assert.Equal(t, "nmea: sentence does not start with a '$'", err.Error(), "Error sentence mismatch") +} + +func TestReturnValues(t *testing.T) { + // Ensure Parse returns errors when appropriate. + result, err := Parse("$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0A") + assert.Nil(t, result, "Result should be nil") + assert.NotNil(t, err, "Err should be an error") + assert.Equal(t, "nmea: sentence checksum mismatch [0C != 0A]", err.Error(), "Error sentence mismatch") +} diff --git a/vendor/github.com/adrianmo/go-nmea/types.go b/vendor/github.com/adrianmo/go-nmea/types.go new file mode 100644 index 00000000..7ee189f2 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/types.go @@ -0,0 +1,259 @@ +package nmea + +// Latitude / longitude representation. + +import ( + "errors" + "fmt" + "math" + "strconv" + "strings" + "unicode" + // "unicode/utf8" +) + +const ( + // Degrees value + Degrees = '\u00B0' + // Minutes value + Minutes = '\'' + // Seconds value + Seconds = '"' + // Point value + Point = '.' + // North value + North = "N" + // South value + South = "S" + // East value + East = "E" + // West value + West = "W" +) + +// LatLong type +type LatLong float64 + +// PrintGPS returns the GPS format for the given LatLong. +func (l LatLong) PrintGPS() string { + padding := "" + value := float64(l) + degrees := math.Floor(math.Abs(value)) + fraction := (math.Abs(value) - degrees) * 60 + if fraction < 10 { + padding = "0" + } + return fmt.Sprintf("%d%s%.4f", int(degrees), padding, fraction) +} + +// PrintDMS returns the degrees, minutes, seconds format for the given LatLong. +func (l LatLong) PrintDMS() string { + val := math.Abs(float64(l)) + degrees := int(math.Floor(val)) + minutes := int(math.Floor(60 * (val - float64(degrees)))) + seconds := 3600 * (val - float64(degrees) - (float64(minutes) / 60)) + + return fmt.Sprintf("%d\u00B0 %d' %f\"", degrees, minutes, seconds) +} + +//ValidRange validates if the range is between -180 and +180. +func (l LatLong) ValidRange() bool { + return -180.0 <= l && l <= 180.0 +} + +// IsNear returns whether the coordinate is near the other coordinate, +// by no further than the given distance away. +func (l LatLong) IsNear(o LatLong, max float64) bool { + return math.Abs(float64(l-o)) <= max +} + +// ParseLatLong parses the supplied string into the LatLong. +// +// Supported formats are: +// - DMS (e.g. 33° 23' 22") +// - Decimal (e.g. 33.23454) +// - GPS (e.g 15113.4322S) +// +func ParseLatLong(s string) (LatLong, error) { + var l LatLong + var err error + invalid := LatLong(0.0) // The invalid value to return. + if l, err = ParseDMS(s); err == nil { + return l, nil + } else if l, err = ParseGPS(s); err == nil { + return l, nil + } else if l, err = ParseDecimal(s); err == nil { + return l, nil + } + if !l.ValidRange() { + return invalid, errors.New("coordinate is not in range -180, 180") + } + return invalid, fmt.Errorf("cannot parse [%s], unknown format", s) +} + +// ParseGPS parses a GPS/NMEA coordinate. +// e.g 15113.4322S +func ParseGPS(s string) (LatLong, error) { + parts := strings.Split(s, " ") + dir := parts[1] + value, err := strconv.ParseFloat(parts[0], 64) + if err != nil { + return 0, fmt.Errorf("parse error: %s", err.Error()) + } + + degrees := math.Floor(value / 100) + minutes := value - (degrees * 100) + value = degrees + minutes/60 + + if dir == North || dir == East { + return LatLong(value), nil + } else if dir == South || dir == West { + return LatLong(0 - value), nil + } else { + return 0, fmt.Errorf("invalid direction [%s]", dir) + } +} + +// ParseDecimal parses a decimal format coordinate. +// e.g: 151.196019 +func ParseDecimal(s string) (LatLong, error) { + // Make sure it parses as a float. + l, err := strconv.ParseFloat(s, 64) + if err != nil || s[0] != '-' && len(strings.Split(s, ".")[0]) > 3 { + return LatLong(0.0), errors.New("parse error (not decimal coordinate)") + } + return LatLong(l), nil +} + +// ParseDMS parses a coordinate in degrees, minutes, seconds. +// - e.g. 33° 23' 22" +func ParseDMS(s string) (LatLong, error) { + degrees := 0 + minutes := 0 + seconds := 0.0 + // Whether a number has finished parsing (i.e whitespace after it) + endNumber := false + // Temporary parse buffer. + tmpBytes := []byte{} + var err error + + for i, r := range s { + if unicode.IsNumber(r) || r == '.' { + if !endNumber { + tmpBytes = append(tmpBytes, s[i]) + } else { + return 0, errors.New("parse error (no delimiter)") + } + } else if unicode.IsSpace(r) && len(tmpBytes) > 0 { + endNumber = true + } else if r == Degrees { + if degrees, err = strconv.Atoi(string(tmpBytes)); err != nil { + return 0, errors.New("parse error (degrees)") + } + tmpBytes = tmpBytes[:0] + endNumber = false + } else if s[i] == Minutes { + if minutes, err = strconv.Atoi(string(tmpBytes)); err != nil { + return 0, errors.New("parse error (minutes)") + } + tmpBytes = tmpBytes[:0] + endNumber = false + } else if s[i] == Seconds { + if seconds, err = strconv.ParseFloat(string(tmpBytes), 64); err != nil { + return 0, errors.New("parse error (seconds)") + } + tmpBytes = tmpBytes[:0] + endNumber = false + } else if unicode.IsSpace(r) && len(tmpBytes) == 0 { + continue + } else { + return 0, fmt.Errorf("parse error (unknown symbol [%d])", s[i]) + } + } + val := LatLong(float64(degrees) + (float64(minutes) / 60.0) + (float64(seconds) / 60.0 / 60.0)) + return val, nil +} + +// Time type +type Time struct { + Valid bool + Hour int + Minute int + Second int + Millisecond int +} + +// String representation of Time +func (t Time) String() string { + return fmt.Sprintf("%02d:%02d:%02d.%04d", t.Hour, t.Minute, t.Second, t.Millisecond) +} + +// ParseTime parses wall clock time. +// e.g. hhmmss.ssss +// An empty time string will result in an invalid time. +func ParseTime(s string) (Time, error) { + if s == "" { + return Time{}, nil + } + ms := "0000" + hhmmss := s + if parts := strings.SplitN(s, ".", 2); len(parts) > 1 { + hhmmss, ms = parts[0], parts[1] + } + if len(hhmmss) != 6 { + return Time{}, fmt.Errorf("parse time: exptected hhmmss.ss format, got '%s'", s) + } + hour, err := strconv.Atoi(hhmmss[0:2]) + if err != nil { + return Time{}, errors.New(hhmmss) + } + minute, err := strconv.Atoi(hhmmss[2:4]) + if err != nil { + return Time{}, errors.New(hhmmss) + } + second, err := strconv.Atoi(hhmmss[4:6]) + if err != nil { + return Time{}, errors.New(hhmmss) + } + millisecond, err := strconv.Atoi(ms) + if err != nil { + return Time{}, errors.New(hhmmss) + } + return Time{true, hour, minute, second, millisecond}, nil +} + +// Date type +type Date struct { + Valid bool + DD int + MM int + YY int +} + +// String representation of date +func (d Date) String() string { + return fmt.Sprintf("%02d/%02d/%02d", d.DD, d.MM, d.YY) +} + +// ParseDate field ddmmyy format +func ParseDate(ddmmyy string) (Date, error) { + if ddmmyy == "" { + return Date{}, nil + } + if len(ddmmyy) != 6 { + return Date{}, fmt.Errorf("parse date: exptected ddmmyy format, got '%s'", ddmmyy) + } + dd, err := strconv.Atoi(ddmmyy[0:2]) + if err != nil { + return Date{}, errors.New(ddmmyy) + } + mm, err := strconv.Atoi(ddmmyy[2:4]) + if err != nil { + return Date{}, errors.New(ddmmyy) + } + yy, err := strconv.Atoi(ddmmyy[4:6]) + if err != nil { + return Date{}, errors.New(ddmmyy) + } + return Date{true, dd, mm, yy}, nil +} diff --git a/vendor/github.com/adrianmo/go-nmea/types_test.go b/vendor/github.com/adrianmo/go-nmea/types_test.go new file mode 100644 index 00000000..d271efdf --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/types_test.go @@ -0,0 +1,152 @@ +package nmea + +import "testing" + +var nearDistance = 0.001 + +func TestLatLongParse(t *testing.T) { + var l LatLong + var err error + value, expected := "3345.1232 N", LatLong(33.752054) + if l, err = ParseGPS(value); err != nil { + t.Errorf("ParseGPS error: %s", err) + } else if !l.IsNear(expected, nearDistance) { + t.Errorf("ParseGPS got %f, expected %f", l, expected) + } + + value, expected = "15145.9877 S", LatLong(-151.76646) + if l, err = ParseGPS(value); err != nil { + t.Errorf("ParseGPS error: %s", err) + } else if !l.IsNear(expected, nearDistance) { + t.Errorf("ParseGPS got %f, expected %f", l, expected) + } + + value, expected = "33\u00B0 12' 34.3423\"", LatLong(33.209540) + if l, err = ParseDMS(value); err != nil { + t.Errorf("ParseDMS error: %s", err) + } else if !l.IsNear(expected, nearDistance) { + t.Errorf("ParseDMS got %f, expected %f", l, expected) + } + + value, expected = "151.234532", LatLong(151.234532) + if l, err = ParseDecimal(value); err != nil { + t.Errorf("ParseDecimal error: %s", err) + } else if !l.IsNear(expected, nearDistance) { + t.Errorf("ParseDecimal got %f, expected %f", l, expected) + } + + value, expected = "-151.234532", LatLong(-151.234532) + if l, err = ParseDecimal(value); err != nil { + t.Errorf("ParseDecimal error: %s", err) + } else if !l.IsNear(expected, nearDistance) { + t.Errorf("ParseDecimal got %f, expected %f", l, expected) + } +} + +func TestLatLongPrint(t *testing.T) { + l, _ := ParseDecimal("151.434367") + exp := "15126.0620" + if s := l.PrintGPS(); s != exp { + t.Errorf("PrintGPS() got %s expected %s", s, exp) + } + + l, _ = ParseGPS("3356.4343 N") + exp = "3356.4343" + if s := l.PrintGPS(); s != exp { + t.Errorf("PrintGPS() got %s expected %s", s, exp) + } + + exp = "33° 56' 26.058000\"" + if s := l.PrintDMS(); s != exp { + t.Errorf("PrintDMS() got %s expected %s", s, exp) + } +} + +func TestTimeParse(t *testing.T) { + timetests := []struct { + value string + expected Time + ok bool + }{ + {"123456", Time{true, 12, 34, 56, 0}, true}, + {"", Time{}, true}, + {"112233.123", Time{true, 11, 22, 33, 123}, true}, + {"010203.04", Time{true, 1, 2, 3, 4}, true}, + {"10203.04", Time{}, false}, + {"x0u2xd", Time{}, false}, + {"xx2233.123", Time{}, false}, + {"11xx33.123", Time{}, false}, + {"1122xx.123", Time{}, false}, + {"112233.xxx", Time{}, false}, + } + for _, tt := range timetests { + actual, err := ParseTime(tt.value) + if !tt.ok { + if err == nil { + t.Errorf("ParseTime(%s) expected error", tt.value) + } + } else { + if err != nil { + t.Errorf("ParseTime(%s) %s", tt.value, err) + } + if actual != tt.expected { + t.Errorf("ParseTime(%s) got %s expected %s", tt.value, actual, tt.expected) + } + } + } +} + +func TestTimeString(t *testing.T) { + d := Time{ + Hour: 1, + Minute: 2, + Second: 3, + Millisecond: 4, + } + expected := "01:02:03.0004" + if s := d.String(); s != expected { + t.Fatalf("got %s, expected %s", s, expected) + } +} + +func TestDateParse(t *testing.T) { + datetests := []struct { + value string + expected Date + ok bool + }{ + {"010203", Date{true, 1, 2, 3}, true}, + {"01003", Date{}, false}, + {"", Date{}, true}, + {"xx0203", Date{}, false}, + {"01xx03", Date{}, false}, + {"0102xx", Date{}, false}, + } + for _, tt := range datetests { + actual, err := ParseDate(tt.value) + if !tt.ok { + if err == nil { + t.Errorf("ParseDate(%s) expected error", tt.value) + } + } else { + if err != nil { + t.Errorf("ParseDate(%s) %s", tt.value, err) + } + if actual != tt.expected { + t.Errorf("ParseDate(%s) got %s expected %s", tt.value, actual, tt.expected) + } + } + } +} + +func TestDateString(t *testing.T) { + d := Date{ + DD: 1, + MM: 2, + YY: 3, + } + expected := "01/02/03" + if s := d.String(); s != expected { + t.Fatalf("got %s expected %s", s, expected) + } +} diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml new file mode 100644 index 00000000..ba95cdd1 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/.travis.yml @@ -0,0 +1,21 @@ +sudo: false +language: go +go: + - 1.3.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - master +matrix: + allow_failures: + - go: master + fast_finish: true +install: + - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d -s .) + - go tool vet . + - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/LICENSE b/vendor/github.com/dustin/go-humanize/LICENSE new file mode 100644 index 00000000..8d9a94a9 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2008 Dustin Sallings + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown new file mode 100644 index 00000000..91b4ae56 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/README.markdown @@ -0,0 +1,124 @@ +# Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) + +Just a few functions for helping humanize times and sizes. + +`go get` it as `github.com/dustin/go-humanize`, import it as +`"github.com/dustin/go-humanize"`, use it as `humanize`. + +See [godoc](https://godoc.org/github.com/dustin/go-humanize) for +complete documentation. + +## Sizes + +This lets you take numbers like `82854982` and convert them to useful +strings like, `83 MB` or `79 MiB` (whichever you prefer). + +Example: + +```go +fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. +``` + +## Times + +This lets you take a `time.Time` and spit it out in relative terms. +For example, `12 seconds ago` or `3 days from now`. + +Example: + +```go +fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. +``` + +Thanks to Kyle Lemons for the time implementation from an IRC +conversation one day. It's pretty neat. + +## Ordinals + +From a [mailing list discussion][odisc] where a user wanted to be able +to label ordinals. + + 0 -> 0th + 1 -> 1st + 2 -> 2nd + 3 -> 3rd + 4 -> 4th + [...] + +Example: + +```go +fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. +``` + +## Commas + +Want to shove commas into numbers? Be my guest. + + 0 -> 0 + 100 -> 100 + 1000 -> 1,000 + 1000000000 -> 1,000,000,000 + -100000 -> -100,000 + +Example: + +```go +fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. +``` + +## Ftoa + +Nicer float64 formatter that removes trailing zeros. + +```go +fmt.Printf("%f", 2.24) // 2.240000 +fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 +fmt.Printf("%f", 2.0) // 2.000000 +fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 +``` + +## SI notation + +Format numbers with [SI notation][sinotation]. + +Example: + +```go +humanize.SI(0.00000000223, "M") // 2.23 nM +``` + +## English-specific functions + +The following functions are in the `humanize/english` subpackage. + +### Plurals + +Simple English pluralization + +```go +english.PluralWord(1, "object", "") // object +english.PluralWord(42, "object", "") // objects +english.PluralWord(2, "bus", "") // buses +english.PluralWord(99, "locus", "loci") // loci + +english.Plural(1, "object", "") // 1 object +english.Plural(42, "object", "") // 42 objects +english.Plural(2, "bus", "") // 2 buses +english.Plural(99, "locus", "loci") // 99 loci +``` + +### Word series + +Format comma-separated words lists with conjuctions: + +```go +english.WordSeries([]string{"foo"}, "and") // foo +english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar +english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz + +english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz +``` + +[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion +[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix diff --git a/vendor/github.com/dustin/go-humanize/big.go b/vendor/github.com/dustin/go-humanize/big.go new file mode 100644 index 00000000..f49dc337 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/big.go @@ -0,0 +1,31 @@ +package humanize + +import ( + "math/big" +) + +// order of magnitude (to a max order) +func oomm(n, b *big.Int, maxmag int) (float64, int) { + mag := 0 + m := &big.Int{} + for n.Cmp(b) >= 0 { + n.DivMod(n, b, m) + mag++ + if mag == maxmag && maxmag >= 0 { + break + } + } + return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag +} + +// total order of magnitude +// (same as above, but with no upper limit) +func oom(n, b *big.Int) (float64, int) { + mag := 0 + m := &big.Int{} + for n.Cmp(b) >= 0 { + n.DivMod(n, b, m) + mag++ + } + return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag +} diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go new file mode 100644 index 00000000..1a2bf617 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go @@ -0,0 +1,173 @@ +package humanize + +import ( + "fmt" + "math/big" + "strings" + "unicode" +) + +var ( + bigIECExp = big.NewInt(1024) + + // BigByte is one byte in bit.Ints + BigByte = big.NewInt(1) + // BigKiByte is 1,024 bytes in bit.Ints + BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) + // BigMiByte is 1,024 k bytes in bit.Ints + BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) + // BigGiByte is 1,024 m bytes in bit.Ints + BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) + // BigTiByte is 1,024 g bytes in bit.Ints + BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) + // BigPiByte is 1,024 t bytes in bit.Ints + BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) + // BigEiByte is 1,024 p bytes in bit.Ints + BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) + // BigZiByte is 1,024 e bytes in bit.Ints + BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) + // BigYiByte is 1,024 z bytes in bit.Ints + BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) +) + +var ( + bigSIExp = big.NewInt(1000) + + // BigSIByte is one SI byte in big.Ints + BigSIByte = big.NewInt(1) + // BigKByte is 1,000 SI bytes in big.Ints + BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) + // BigMByte is 1,000 SI k bytes in big.Ints + BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) + // BigGByte is 1,000 SI m bytes in big.Ints + BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) + // BigTByte is 1,000 SI g bytes in big.Ints + BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) + // BigPByte is 1,000 SI t bytes in big.Ints + BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) + // BigEByte is 1,000 SI p bytes in big.Ints + BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) + // BigZByte is 1,000 SI e bytes in big.Ints + BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) + // BigYByte is 1,000 SI z bytes in big.Ints + BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) +) + +var bigBytesSizeTable = map[string]*big.Int{ + "b": BigByte, + "kib": BigKiByte, + "kb": BigKByte, + "mib": BigMiByte, + "mb": BigMByte, + "gib": BigGiByte, + "gb": BigGByte, + "tib": BigTiByte, + "tb": BigTByte, + "pib": BigPiByte, + "pb": BigPByte, + "eib": BigEiByte, + "eb": BigEByte, + "zib": BigZiByte, + "zb": BigZByte, + "yib": BigYiByte, + "yb": BigYByte, + // Without suffix + "": BigByte, + "ki": BigKiByte, + "k": BigKByte, + "mi": BigMiByte, + "m": BigMByte, + "gi": BigGiByte, + "g": BigGByte, + "ti": BigTiByte, + "t": BigTByte, + "pi": BigPiByte, + "p": BigPByte, + "ei": BigEiByte, + "e": BigEByte, + "z": BigZByte, + "zi": BigZiByte, + "y": BigYByte, + "yi": BigYiByte, +} + +var ten = big.NewInt(10) + +func humanateBigBytes(s, base *big.Int, sizes []string) string { + if s.Cmp(ten) < 0 { + return fmt.Sprintf("%d B", s) + } + c := (&big.Int{}).Set(s) + val, mag := oomm(c, base, len(sizes)-1) + suffix := sizes[mag] + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + + return fmt.Sprintf(f, val, suffix) + +} + +// BigBytes produces a human readable representation of an SI size. +// +// See also: ParseBigBytes. +// +// BigBytes(82854982) -> 83 MB +func BigBytes(s *big.Int) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + return humanateBigBytes(s, bigSIExp, sizes) +} + +// BigIBytes produces a human readable representation of an IEC size. +// +// See also: ParseBigBytes. +// +// BigIBytes(82854982) -> 79 MiB +func BigIBytes(s *big.Int) string { + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + return humanateBigBytes(s, bigIECExp, sizes) +} + +// ParseBigBytes parses a string representation of bytes into the number +// of bytes it represents. +// +// See also: BigBytes, BigIBytes. +// +// ParseBigBytes("42 MB") -> 42000000, nil +// ParseBigBytes("42 mib") -> 44040192, nil +func ParseBigBytes(s string) (*big.Int, error) { + lastDigit := 0 + hasComma := false + for _, r := range s { + if !(unicode.IsDigit(r) || r == '.' || r == ',') { + break + } + if r == ',' { + hasComma = true + } + lastDigit++ + } + + num := s[:lastDigit] + if hasComma { + num = strings.Replace(num, ",", "", -1) + } + + val := &big.Rat{} + _, err := fmt.Sscanf(num, "%f", val) + if err != nil { + return nil, err + } + + extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) + if m, ok := bigBytesSizeTable[extra]; ok { + mv := (&big.Rat{}).SetInt(m) + val.Mul(val, mv) + rv := &big.Int{} + rv.Div(val.Num(), val.Denom()) + return rv, nil + } + + return nil, fmt.Errorf("unhandled size name: %v", extra) +} diff --git a/vendor/github.com/dustin/go-humanize/bigbytes_test.go b/vendor/github.com/dustin/go-humanize/bigbytes_test.go new file mode 100644 index 00000000..236ad080 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bigbytes_test.go @@ -0,0 +1,220 @@ +package humanize + +import ( + "math/big" + "testing" +) + +func TestBigByteParsing(t *testing.T) { + tests := []struct { + in string + exp uint64 + }{ + {"42", 42}, + {"42MB", 42000000}, + {"42MiB", 44040192}, + {"42mb", 42000000}, + {"42mib", 44040192}, + {"42MIB", 44040192}, + {"42 MB", 42000000}, + {"42 MiB", 44040192}, + {"42 mb", 42000000}, + {"42 mib", 44040192}, + {"42 MIB", 44040192}, + {"42.5MB", 42500000}, + {"42.5MiB", 44564480}, + {"42.5 MB", 42500000}, + {"42.5 MiB", 44564480}, + // No need to say B + {"42M", 42000000}, + {"42Mi", 44040192}, + {"42m", 42000000}, + {"42mi", 44040192}, + {"42MI", 44040192}, + {"42 M", 42000000}, + {"42 Mi", 44040192}, + {"42 m", 42000000}, + {"42 mi", 44040192}, + {"42 MI", 44040192}, + {"42.5M", 42500000}, + {"42.5Mi", 44564480}, + {"42.5 M", 42500000}, + {"42.5 Mi", 44564480}, + {"1,005.03 MB", 1005030000}, + // Large testing, breaks when too much larger than + // this. + {"12.5 EB", uint64(12.5 * float64(EByte))}, + {"12.5 E", uint64(12.5 * float64(EByte))}, + {"12.5 EiB", uint64(12.5 * float64(EiByte))}, + } + + for _, p := range tests { + got, err := ParseBigBytes(p.in) + if err != nil { + t.Errorf("Couldn't parse %v: %v", p.in, err) + } else { + if got.Uint64() != p.exp { + t.Errorf("Expected %v for %v, got %v", + p.exp, p.in, got) + } + } + } +} + +func TestBigByteErrors(t *testing.T) { + got, err := ParseBigBytes("84 JB") + if err == nil { + t.Errorf("Expected error, got %v", got) + } + got, err = ParseBigBytes("") + if err == nil { + t.Errorf("Expected error parsing nothing") + } +} + +func bbyte(in uint64) string { + return BigBytes((&big.Int{}).SetUint64(in)) +} + +func bibyte(in uint64) string { + return BigIBytes((&big.Int{}).SetUint64(in)) +} + +func TestBigBytes(t *testing.T) { + testList{ + {"bytes(0)", bbyte(0), "0 B"}, + {"bytes(1)", bbyte(1), "1 B"}, + {"bytes(803)", bbyte(803), "803 B"}, + {"bytes(999)", bbyte(999), "999 B"}, + + {"bytes(1024)", bbyte(1024), "1.0 kB"}, + {"bytes(1MB - 1)", bbyte(MByte - Byte), "1000 kB"}, + + {"bytes(1MB)", bbyte(1024 * 1024), "1.0 MB"}, + {"bytes(1GB - 1K)", bbyte(GByte - KByte), "1000 MB"}, + + {"bytes(1GB)", bbyte(GByte), "1.0 GB"}, + {"bytes(1TB - 1M)", bbyte(TByte - MByte), "1000 GB"}, + + {"bytes(1TB)", bbyte(TByte), "1.0 TB"}, + {"bytes(1PB - 1T)", bbyte(PByte - TByte), "999 TB"}, + + {"bytes(1PB)", bbyte(PByte), "1.0 PB"}, + {"bytes(1PB - 1T)", bbyte(EByte - PByte), "999 PB"}, + + {"bytes(1EB)", bbyte(EByte), "1.0 EB"}, + // Overflows. + // {"bytes(1EB - 1P)", Bytes((KByte*EByte)-PByte), "1023EB"}, + + {"bytes(0)", bibyte(0), "0 B"}, + {"bytes(1)", bibyte(1), "1 B"}, + {"bytes(803)", bibyte(803), "803 B"}, + {"bytes(1023)", bibyte(1023), "1023 B"}, + + {"bytes(1024)", bibyte(1024), "1.0 KiB"}, + {"bytes(1MB - 1)", bibyte(MiByte - IByte), "1024 KiB"}, + + {"bytes(1MB)", bibyte(1024 * 1024), "1.0 MiB"}, + {"bytes(1GB - 1K)", bibyte(GiByte - KiByte), "1024 MiB"}, + + {"bytes(1GB)", bibyte(GiByte), "1.0 GiB"}, + {"bytes(1TB - 1M)", bibyte(TiByte - MiByte), "1024 GiB"}, + + {"bytes(1TB)", bibyte(TiByte), "1.0 TiB"}, + {"bytes(1PB - 1T)", bibyte(PiByte - TiByte), "1023 TiB"}, + + {"bytes(1PB)", bibyte(PiByte), "1.0 PiB"}, + {"bytes(1PB - 1T)", bibyte(EiByte - PiByte), "1023 PiB"}, + + {"bytes(1EiB)", bibyte(EiByte), "1.0 EiB"}, + // Overflows. + // {"bytes(1EB - 1P)", bibyte((KIByte*EIByte)-PiByte), "1023EB"}, + + {"bytes(5.5GiB)", bibyte(5.5 * GiByte), "5.5 GiB"}, + + {"bytes(5.5GB)", bbyte(5.5 * GByte), "5.5 GB"}, + }.validate(t) +} + +func TestVeryBigBytes(t *testing.T) { + b, _ := (&big.Int{}).SetString("15347691069326346944512", 10) + s := BigBytes(b) + if s != "15 ZB" { + t.Errorf("Expected 15 ZB, got %v", s) + } + s = BigIBytes(b) + if s != "13 ZiB" { + t.Errorf("Expected 13 ZiB, got %v", s) + } + + b, _ = (&big.Int{}).SetString("15716035654990179271180288", 10) + s = BigBytes(b) + if s != "16 YB" { + t.Errorf("Expected 16 YB, got %v", s) + } + s = BigIBytes(b) + if s != "13 YiB" { + t.Errorf("Expected 13 YiB, got %v", s) + } +} + +func TestVeryVeryBigBytes(t *testing.T) { + b, _ := (&big.Int{}).SetString("16093220510709943573688614912", 10) + s := BigBytes(b) + if s != "16093 YB" { + t.Errorf("Expected 16093 YB, got %v", s) + } + s = BigIBytes(b) + if s != "13312 YiB" { + t.Errorf("Expected 13312 YiB, got %v", s) + } +} + +func TestParseVeryBig(t *testing.T) { + tests := []struct { + in string + out string + }{ + {"16 ZB", "16000000000000000000000"}, + {"16 ZiB", "18889465931478580854784"}, + {"16.5 ZB", "16500000000000000000000"}, + {"16.5 ZiB", "19479761741837286506496"}, + {"16 Z", "16000000000000000000000"}, + {"16 Zi", "18889465931478580854784"}, + {"16.5 Z", "16500000000000000000000"}, + {"16.5 Zi", "19479761741837286506496"}, + + {"16 YB", "16000000000000000000000000"}, + {"16 YiB", "19342813113834066795298816"}, + {"16.5 YB", "16500000000000000000000000"}, + {"16.5 YiB", "19947276023641381382651904"}, + {"16 Y", "16000000000000000000000000"}, + {"16 Yi", "19342813113834066795298816"}, + {"16.5 Y", "16500000000000000000000000"}, + {"16.5 Yi", "19947276023641381382651904"}, + } + + for _, test := range tests { + x, err := ParseBigBytes(test.in) + if err != nil { + t.Errorf("Error parsing %q: %v", test.in, err) + continue + } + + if x.String() != test.out { + t.Errorf("Expected %q for %q, got %v", test.out, test.in, x) + } + } +} + +func BenchmarkParseBigBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseBigBytes("16.5 Z") + } +} + +func BenchmarkBigBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + bibyte(16.5 * GByte) + } +} diff --git a/vendor/github.com/dustin/go-humanize/bytes.go b/vendor/github.com/dustin/go-humanize/bytes.go new file mode 100644 index 00000000..0b498f48 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bytes.go @@ -0,0 +1,143 @@ +package humanize + +import ( + "fmt" + "math" + "strconv" + "strings" + "unicode" +) + +// IEC Sizes. +// kibis of bits +const ( + Byte = 1 << (iota * 10) + KiByte + MiByte + GiByte + TiByte + PiByte + EiByte +) + +// SI Sizes. +const ( + IByte = 1 + KByte = IByte * 1000 + MByte = KByte * 1000 + GByte = MByte * 1000 + TByte = GByte * 1000 + PByte = TByte * 1000 + EByte = PByte * 1000 +) + +var bytesSizeTable = map[string]uint64{ + "b": Byte, + "kib": KiByte, + "kb": KByte, + "mib": MiByte, + "mb": MByte, + "gib": GiByte, + "gb": GByte, + "tib": TiByte, + "tb": TByte, + "pib": PiByte, + "pb": PByte, + "eib": EiByte, + "eb": EByte, + // Without suffix + "": Byte, + "ki": KiByte, + "k": KByte, + "mi": MiByte, + "m": MByte, + "gi": GiByte, + "g": GByte, + "ti": TiByte, + "t": TByte, + "pi": PiByte, + "p": PByte, + "ei": EiByte, + "e": EByte, +} + +func logn(n, b float64) float64 { + return math.Log(n) / math.Log(b) +} + +func humanateBytes(s uint64, base float64, sizes []string) string { + if s < 10 { + return fmt.Sprintf("%d B", s) + } + e := math.Floor(logn(float64(s), base)) + suffix := sizes[int(e)] + val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + + return fmt.Sprintf(f, val, suffix) +} + +// Bytes produces a human readable representation of an SI size. +// +// See also: ParseBytes. +// +// Bytes(82854982) -> 83 MB +func Bytes(s uint64) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} + return humanateBytes(s, 1000, sizes) +} + +// IBytes produces a human readable representation of an IEC size. +// +// See also: ParseBytes. +// +// IBytes(82854982) -> 79 MiB +func IBytes(s uint64) string { + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} + return humanateBytes(s, 1024, sizes) +} + +// ParseBytes parses a string representation of bytes into the number +// of bytes it represents. +// +// See Also: Bytes, IBytes. +// +// ParseBytes("42 MB") -> 42000000, nil +// ParseBytes("42 mib") -> 44040192, nil +func ParseBytes(s string) (uint64, error) { + lastDigit := 0 + hasComma := false + for _, r := range s { + if !(unicode.IsDigit(r) || r == '.' || r == ',') { + break + } + if r == ',' { + hasComma = true + } + lastDigit++ + } + + num := s[:lastDigit] + if hasComma { + num = strings.Replace(num, ",", "", -1) + } + + f, err := strconv.ParseFloat(num, 64) + if err != nil { + return 0, err + } + + extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) + if m, ok := bytesSizeTable[extra]; ok { + f *= float64(m) + if f >= math.MaxUint64 { + return 0, fmt.Errorf("too large: %v", s) + } + return uint64(f), nil + } + + return 0, fmt.Errorf("unhandled size name: %v", extra) +} diff --git a/vendor/github.com/dustin/go-humanize/bytes_test.go b/vendor/github.com/dustin/go-humanize/bytes_test.go new file mode 100644 index 00000000..0bb811c7 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bytes_test.go @@ -0,0 +1,146 @@ +package humanize + +import ( + "testing" +) + +func TestByteParsing(t *testing.T) { + tests := []struct { + in string + exp uint64 + }{ + {"42", 42}, + {"42MB", 42000000}, + {"42MiB", 44040192}, + {"42mb", 42000000}, + {"42mib", 44040192}, + {"42MIB", 44040192}, + {"42 MB", 42000000}, + {"42 MiB", 44040192}, + {"42 mb", 42000000}, + {"42 mib", 44040192}, + {"42 MIB", 44040192}, + {"42.5MB", 42500000}, + {"42.5MiB", 44564480}, + {"42.5 MB", 42500000}, + {"42.5 MiB", 44564480}, + // No need to say B + {"42M", 42000000}, + {"42Mi", 44040192}, + {"42m", 42000000}, + {"42mi", 44040192}, + {"42MI", 44040192}, + {"42 M", 42000000}, + {"42 Mi", 44040192}, + {"42 m", 42000000}, + {"42 mi", 44040192}, + {"42 MI", 44040192}, + {"42.5M", 42500000}, + {"42.5Mi", 44564480}, + {"42.5 M", 42500000}, + {"42.5 Mi", 44564480}, + // Bug #42 + {"1,005.03 MB", 1005030000}, + // Large testing, breaks when too much larger than + // this. + {"12.5 EB", uint64(12.5 * float64(EByte))}, + {"12.5 E", uint64(12.5 * float64(EByte))}, + {"12.5 EiB", uint64(12.5 * float64(EiByte))}, + } + + for _, p := range tests { + got, err := ParseBytes(p.in) + if err != nil { + t.Errorf("Couldn't parse %v: %v", p.in, err) + } + if got != p.exp { + t.Errorf("Expected %v for %v, got %v", + p.exp, p.in, got) + } + } +} + +func TestByteErrors(t *testing.T) { + got, err := ParseBytes("84 JB") + if err == nil { + t.Errorf("Expected error, got %v", got) + } + got, err = ParseBytes("") + if err == nil { + t.Errorf("Expected error parsing nothing") + } + got, err = ParseBytes("16 EiB") + if err == nil { + t.Errorf("Expected error, got %v", got) + } +} + +func TestBytes(t *testing.T) { + testList{ + {"bytes(0)", Bytes(0), "0 B"}, + {"bytes(1)", Bytes(1), "1 B"}, + {"bytes(803)", Bytes(803), "803 B"}, + {"bytes(999)", Bytes(999), "999 B"}, + + {"bytes(1024)", Bytes(1024), "1.0 kB"}, + {"bytes(9999)", Bytes(9999), "10 kB"}, + {"bytes(1MB - 1)", Bytes(MByte - Byte), "1000 kB"}, + + {"bytes(1MB)", Bytes(1024 * 1024), "1.0 MB"}, + {"bytes(1GB - 1K)", Bytes(GByte - KByte), "1000 MB"}, + + {"bytes(1GB)", Bytes(GByte), "1.0 GB"}, + {"bytes(1TB - 1M)", Bytes(TByte - MByte), "1000 GB"}, + {"bytes(10MB)", Bytes(9999 * 1000), "10 MB"}, + + {"bytes(1TB)", Bytes(TByte), "1.0 TB"}, + {"bytes(1PB - 1T)", Bytes(PByte - TByte), "999 TB"}, + + {"bytes(1PB)", Bytes(PByte), "1.0 PB"}, + {"bytes(1PB - 1T)", Bytes(EByte - PByte), "999 PB"}, + + {"bytes(1EB)", Bytes(EByte), "1.0 EB"}, + // Overflows. + // {"bytes(1EB - 1P)", Bytes((KByte*EByte)-PByte), "1023EB"}, + + {"bytes(0)", IBytes(0), "0 B"}, + {"bytes(1)", IBytes(1), "1 B"}, + {"bytes(803)", IBytes(803), "803 B"}, + {"bytes(1023)", IBytes(1023), "1023 B"}, + + {"bytes(1024)", IBytes(1024), "1.0 KiB"}, + {"bytes(1MB - 1)", IBytes(MiByte - IByte), "1024 KiB"}, + + {"bytes(1MB)", IBytes(1024 * 1024), "1.0 MiB"}, + {"bytes(1GB - 1K)", IBytes(GiByte - KiByte), "1024 MiB"}, + + {"bytes(1GB)", IBytes(GiByte), "1.0 GiB"}, + {"bytes(1TB - 1M)", IBytes(TiByte - MiByte), "1024 GiB"}, + + {"bytes(1TB)", IBytes(TiByte), "1.0 TiB"}, + {"bytes(1PB - 1T)", IBytes(PiByte - TiByte), "1023 TiB"}, + + {"bytes(1PB)", IBytes(PiByte), "1.0 PiB"}, + {"bytes(1PB - 1T)", IBytes(EiByte - PiByte), "1023 PiB"}, + + {"bytes(1EiB)", IBytes(EiByte), "1.0 EiB"}, + // Overflows. + // {"bytes(1EB - 1P)", IBytes((KIByte*EIByte)-PiByte), "1023EB"}, + + {"bytes(5.5GiB)", IBytes(5.5 * GiByte), "5.5 GiB"}, + + {"bytes(5.5GB)", Bytes(5.5 * GByte), "5.5 GB"}, + }.validate(t) +} + +func BenchmarkParseBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseBytes("16.5 GB") + } +} + +func BenchmarkBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + Bytes(16.5 * GByte) + } +} diff --git a/vendor/github.com/dustin/go-humanize/comma.go b/vendor/github.com/dustin/go-humanize/comma.go new file mode 100644 index 00000000..13611aaa --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/comma.go @@ -0,0 +1,108 @@ +package humanize + +import ( + "bytes" + "math" + "math/big" + "strconv" + "strings" +) + +// Comma produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Comma(834142) -> 834,142 +func Comma(v int64) string { + sign := "" + + // Min int64 can't be negated to a usable value, so it has to be special cased. + if v == math.MinInt64 { + return "-9,223,372,036,854,775,808" + } + + if v < 0 { + sign = "-" + v = 0 - v + } + + parts := []string{"", "", "", "", "", "", ""} + j := len(parts) - 1 + + for v > 999 { + parts[j] = strconv.FormatInt(v%1000, 10) + switch len(parts[j]) { + case 2: + parts[j] = "0" + parts[j] + case 1: + parts[j] = "00" + parts[j] + } + v = v / 1000 + j-- + } + parts[j] = strconv.Itoa(int(v)) + return sign + strings.Join(parts[j:], ",") +} + +// Commaf produces a string form of the given number in base 10 with +// commas after every three orders of magnitude. +// +// e.g. Commaf(834142.32) -> 834,142.32 +func Commaf(v float64) string { + buf := &bytes.Buffer{} + if v < 0 { + buf.Write([]byte{'-'}) + v = 0 - v + } + + comma := []byte{','} + + parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") + pos := 0 + if len(parts[0])%3 != 0 { + pos += len(parts[0]) % 3 + buf.WriteString(parts[0][:pos]) + buf.Write(comma) + } + for ; pos < len(parts[0]); pos += 3 { + buf.WriteString(parts[0][pos : pos+3]) + buf.Write(comma) + } + buf.Truncate(buf.Len() - 1) + + if len(parts) > 1 { + buf.Write([]byte{'.'}) + buf.WriteString(parts[1]) + } + return buf.String() +} + +// BigComma produces a string form of the given big.Int in base 10 +// with commas after every three orders of magnitude. +func BigComma(b *big.Int) string { + sign := "" + if b.Sign() < 0 { + sign = "-" + b.Abs(b) + } + + athousand := big.NewInt(1000) + c := (&big.Int{}).Set(b) + _, m := oom(c, athousand) + parts := make([]string, m+1) + j := len(parts) - 1 + + mod := &big.Int{} + for b.Cmp(athousand) >= 0 { + b.DivMod(b, athousand, mod) + parts[j] = strconv.FormatInt(mod.Int64(), 10) + switch len(parts[j]) { + case 2: + parts[j] = "0" + parts[j] + case 1: + parts[j] = "00" + parts[j] + } + j-- + } + parts[j] = strconv.Itoa(int(b.Int64())) + return sign + strings.Join(parts[j:], ",") +} diff --git a/vendor/github.com/dustin/go-humanize/comma_test.go b/vendor/github.com/dustin/go-humanize/comma_test.go new file mode 100644 index 00000000..89daca5f --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/comma_test.go @@ -0,0 +1,136 @@ +package humanize + +import ( + "math" + "math/big" + "testing" +) + +func TestCommas(t *testing.T) { + testList{ + {"0", Comma(0), "0"}, + {"10", Comma(10), "10"}, + {"100", Comma(100), "100"}, + {"1,000", Comma(1000), "1,000"}, + {"10,000", Comma(10000), "10,000"}, + {"100,000", Comma(100000), "100,000"}, + {"10,000,000", Comma(10000000), "10,000,000"}, + {"10,100,000", Comma(10100000), "10,100,000"}, + {"10,010,000", Comma(10010000), "10,010,000"}, + {"10,001,000", Comma(10001000), "10,001,000"}, + {"123,456,789", Comma(123456789), "123,456,789"}, + {"maxint", Comma(9.223372e+18), "9,223,372,000,000,000,000"}, + {"math.maxint", Comma(math.MaxInt64), "9,223,372,036,854,775,807"}, + {"math.minint", Comma(math.MinInt64), "-9,223,372,036,854,775,808"}, + {"minint", Comma(-9.223372e+18), "-9,223,372,000,000,000,000"}, + {"-123,456,789", Comma(-123456789), "-123,456,789"}, + {"-10,100,000", Comma(-10100000), "-10,100,000"}, + {"-10,010,000", Comma(-10010000), "-10,010,000"}, + {"-10,001,000", Comma(-10001000), "-10,001,000"}, + {"-10,000,000", Comma(-10000000), "-10,000,000"}, + {"-100,000", Comma(-100000), "-100,000"}, + {"-10,000", Comma(-10000), "-10,000"}, + {"-1,000", Comma(-1000), "-1,000"}, + {"-100", Comma(-100), "-100"}, + {"-10", Comma(-10), "-10"}, + }.validate(t) +} + +func TestCommafs(t *testing.T) { + testList{ + {"0", Commaf(0), "0"}, + {"10.11", Commaf(10.11), "10.11"}, + {"100", Commaf(100), "100"}, + {"1,000", Commaf(1000), "1,000"}, + {"10,000", Commaf(10000), "10,000"}, + {"100,000", Commaf(100000), "100,000"}, + {"834,142.32", Commaf(834142.32), "834,142.32"}, + {"10,000,000", Commaf(10000000), "10,000,000"}, + {"10,100,000", Commaf(10100000), "10,100,000"}, + {"10,010,000", Commaf(10010000), "10,010,000"}, + {"10,001,000", Commaf(10001000), "10,001,000"}, + {"123,456,789", Commaf(123456789), "123,456,789"}, + {"maxf64", Commaf(math.MaxFloat64), "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000"}, + {"minf64", Commaf(math.SmallestNonzeroFloat64), "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"}, + {"-123,456,789", Commaf(-123456789), "-123,456,789"}, + {"-10,100,000", Commaf(-10100000), "-10,100,000"}, + {"-10,010,000", Commaf(-10010000), "-10,010,000"}, + {"-10,001,000", Commaf(-10001000), "-10,001,000"}, + {"-10,000,000", Commaf(-10000000), "-10,000,000"}, + {"-100,000", Commaf(-100000), "-100,000"}, + {"-10,000", Commaf(-10000), "-10,000"}, + {"-1,000", Commaf(-1000), "-1,000"}, + {"-100.11", Commaf(-100.11), "-100.11"}, + {"-10", Commaf(-10), "-10"}, + }.validate(t) +} + +func BenchmarkCommas(b *testing.B) { + for i := 0; i < b.N; i++ { + Comma(1234567890) + } +} + +func BenchmarkCommaf(b *testing.B) { + for i := 0; i < b.N; i++ { + Commaf(1234567890.83584) + } +} + +func BenchmarkBigCommas(b *testing.B) { + for i := 0; i < b.N; i++ { + BigComma(big.NewInt(1234567890)) + } +} + +func bigComma(i int64) string { + return BigComma(big.NewInt(i)) +} + +func TestBigCommas(t *testing.T) { + testList{ + {"0", bigComma(0), "0"}, + {"10", bigComma(10), "10"}, + {"100", bigComma(100), "100"}, + {"1,000", bigComma(1000), "1,000"}, + {"10,000", bigComma(10000), "10,000"}, + {"100,000", bigComma(100000), "100,000"}, + {"10,000,000", bigComma(10000000), "10,000,000"}, + {"10,100,000", bigComma(10100000), "10,100,000"}, + {"10,010,000", bigComma(10010000), "10,010,000"}, + {"10,001,000", bigComma(10001000), "10,001,000"}, + {"123,456,789", bigComma(123456789), "123,456,789"}, + {"maxint", bigComma(9.223372e+18), "9,223,372,000,000,000,000"}, + {"minint", bigComma(-9.223372e+18), "-9,223,372,000,000,000,000"}, + {"-123,456,789", bigComma(-123456789), "-123,456,789"}, + {"-10,100,000", bigComma(-10100000), "-10,100,000"}, + {"-10,010,000", bigComma(-10010000), "-10,010,000"}, + {"-10,001,000", bigComma(-10001000), "-10,001,000"}, + {"-10,000,000", bigComma(-10000000), "-10,000,000"}, + {"-100,000", bigComma(-100000), "-100,000"}, + {"-10,000", bigComma(-10000), "-10,000"}, + {"-1,000", bigComma(-1000), "-1,000"}, + {"-100", bigComma(-100), "-100"}, + {"-10", bigComma(-10), "-10"}, + }.validate(t) +} + +func TestVeryBigCommas(t *testing.T) { + tests := []struct{ in, exp string }{ + { + "84889279597249724975972597249849757294578485", + "84,889,279,597,249,724,975,972,597,249,849,757,294,578,485", + }, + { + "-84889279597249724975972597249849757294578485", + "-84,889,279,597,249,724,975,972,597,249,849,757,294,578,485", + }, + } + for _, test := range tests { + n, _ := (&big.Int{}).SetString(test.in, 10) + got := BigComma(n) + if test.exp != got { + t.Errorf("Expected %q, got %q", test.exp, got) + } + } +} diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go new file mode 100644 index 00000000..620690de --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/commaf.go @@ -0,0 +1,40 @@ +// +build go1.6 + +package humanize + +import ( + "bytes" + "math/big" + "strings" +) + +// BigCommaf produces a string form of the given big.Float in base 10 +// with commas after every three orders of magnitude. +func BigCommaf(v *big.Float) string { + buf := &bytes.Buffer{} + if v.Sign() < 0 { + buf.Write([]byte{'-'}) + v.Abs(v) + } + + comma := []byte{','} + + parts := strings.Split(v.Text('f', -1), ".") + pos := 0 + if len(parts[0])%3 != 0 { + pos += len(parts[0]) % 3 + buf.WriteString(parts[0][:pos]) + buf.Write(comma) + } + for ; pos < len(parts[0]); pos += 3 { + buf.WriteString(parts[0][pos : pos+3]) + buf.Write(comma) + } + buf.Truncate(buf.Len() - 1) + + if len(parts) > 1 { + buf.Write([]byte{'.'}) + buf.WriteString(parts[1]) + } + return buf.String() +} diff --git a/vendor/github.com/dustin/go-humanize/commaf_test.go b/vendor/github.com/dustin/go-humanize/commaf_test.go new file mode 100644 index 00000000..21f7f9e5 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/commaf_test.go @@ -0,0 +1,44 @@ +// +build go1.6 + +package humanize + +import ( + "math" + "math/big" + "testing" +) + +func BenchmarkBigCommaf(b *testing.B) { + for i := 0; i < b.N; i++ { + Commaf(1234567890.83584) + } +} + +func TestBigCommafs(t *testing.T) { + testList{ + {"0", BigCommaf(big.NewFloat(0)), "0"}, + {"10.11", BigCommaf(big.NewFloat(10.11)), "10.11"}, + {"100", BigCommaf(big.NewFloat(100)), "100"}, + {"1,000", BigCommaf(big.NewFloat(1000)), "1,000"}, + {"10,000", BigCommaf(big.NewFloat(10000)), "10,000"}, + {"100,000", BigCommaf(big.NewFloat(100000)), "100,000"}, + {"834,142.32", BigCommaf(big.NewFloat(834142.32)), "834,142.32"}, + {"10,000,000", BigCommaf(big.NewFloat(10000000)), "10,000,000"}, + {"10,100,000", BigCommaf(big.NewFloat(10100000)), "10,100,000"}, + {"10,010,000", BigCommaf(big.NewFloat(10010000)), "10,010,000"}, + {"10,001,000", BigCommaf(big.NewFloat(10001000)), "10,001,000"}, + {"123,456,789", BigCommaf(big.NewFloat(123456789)), "123,456,789"}, + {"maxf64", BigCommaf(big.NewFloat(math.MaxFloat64)), "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000"}, + {"minf64", BigCommaf(big.NewFloat(math.SmallestNonzeroFloat64)), "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465"}, + {"-123,456,789", BigCommaf(big.NewFloat(-123456789)), "-123,456,789"}, + {"-10,100,000", BigCommaf(big.NewFloat(-10100000)), "-10,100,000"}, + {"-10,010,000", BigCommaf(big.NewFloat(-10010000)), "-10,010,000"}, + {"-10,001,000", BigCommaf(big.NewFloat(-10001000)), "-10,001,000"}, + {"-10,000,000", BigCommaf(big.NewFloat(-10000000)), "-10,000,000"}, + {"-100,000", BigCommaf(big.NewFloat(-100000)), "-100,000"}, + {"-10,000", BigCommaf(big.NewFloat(-10000)), "-10,000"}, + {"-1,000", BigCommaf(big.NewFloat(-1000)), "-1,000"}, + {"-100.11", BigCommaf(big.NewFloat(-100.11)), "-100.11"}, + {"-10", BigCommaf(big.NewFloat(-10)), "-10"}, + }.validate(t) +} diff --git a/vendor/github.com/dustin/go-humanize/common_test.go b/vendor/github.com/dustin/go-humanize/common_test.go new file mode 100644 index 00000000..fc7db151 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/common_test.go @@ -0,0 +1,18 @@ +package humanize + +import ( + "testing" +) + +type testList []struct { + name, got, exp string +} + +func (tl testList) validate(t *testing.T) { + for _, test := range tl { + if test.got != test.exp { + t.Errorf("On %v, expected '%v', but got '%v'", + test.name, test.exp, test.got) + } + } +} diff --git a/vendor/github.com/dustin/go-humanize/english/words.go b/vendor/github.com/dustin/go-humanize/english/words.go new file mode 100644 index 00000000..26e9918b --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/english/words.go @@ -0,0 +1,96 @@ +// Package english provides utilities to generate more user-friendly English output. +package english + +import ( + "fmt" + "strings" +) + +// These are included because they are common technical terms. +var specialPlurals = map[string]string{ + "index": "indices", + "matrix": "matrices", + "vertex": "vertices", +} + +var sibilantEndings = []string{"s", "sh", "tch", "x"} + +var isVowel = map[byte]bool{ + 'A': true, 'E': true, 'I': true, 'O': true, 'U': true, + 'a': true, 'e': true, 'i': true, 'o': true, 'u': true, +} + +// PluralWord builds the plural form of an English word. +// The simple English rules of regular pluralization will be used +// if the plural form is an empty string (i.e. not explicitly given). +// The special cases are not guaranteed to work for strings outside ASCII. +func PluralWord(quantity int, singular, plural string) string { + if quantity == 1 { + return singular + } + if plural != "" { + return plural + } + if plural = specialPlurals[singular]; plural != "" { + return plural + } + + // We need to guess what the English plural might be. Keep this + // function simple! It doesn't need to know about every possiblity; + // only regular rules and the most common special cases. + // + // Reference: http://en.wikipedia.org/wiki/English_plural + + for _, ending := range sibilantEndings { + if strings.HasSuffix(singular, ending) { + return singular + "es" + } + } + l := len(singular) + if l >= 2 && singular[l-1] == 'o' && !isVowel[singular[l-2]] { + return singular + "es" + } + if l >= 2 && singular[l-1] == 'y' && !isVowel[singular[l-2]] { + return singular[:l-1] + "ies" + } + + return singular + "s" +} + +// Plural formats an integer and a string into a single pluralized string. +// The simple English rules of regular pluralization will be used +// if the plural form is an empty string (i.e. not explicitly given). +func Plural(quantity int, singular, plural string) string { + return fmt.Sprintf("%d %s", quantity, PluralWord(quantity, singular, plural)) +} + +// WordSeries converts a list of words into a word series in English. +// It returns a string containing all the given words separated by commas, +// the coordinating conjunction, and a serial comma, as appropriate. +func WordSeries(words []string, conjunction string) string { + switch len(words) { + case 0: + return "" + case 1: + return words[0] + default: + return fmt.Sprintf("%s %s %s", strings.Join(words[:len(words)-1], ", "), conjunction, words[len(words)-1]) + } +} + +// OxfordWordSeries converts a list of words into a word series in English, +// using an Oxford comma (https://en.wikipedia.org/wiki/Serial_comma). It +// returns a string containing all the given words separated by commas, the +// coordinating conjunction, and a serial comma, as appropriate. +func OxfordWordSeries(words []string, conjunction string) string { + switch len(words) { + case 0: + return "" + case 1: + return words[0] + case 2: + return strings.Join(words, fmt.Sprintf(" %s ", conjunction)) + default: + return fmt.Sprintf("%s, %s %s", strings.Join(words[:len(words)-1], ", "), conjunction, words[len(words)-1]) + } +} diff --git a/vendor/github.com/dustin/go-humanize/english/words_test.go b/vendor/github.com/dustin/go-humanize/english/words_test.go new file mode 100644 index 00000000..3c1d7bea --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/english/words_test.go @@ -0,0 +1,94 @@ +package english + +import ( + "testing" +) + +func TestPluralWord(t *testing.T) { + tests := []struct { + n int + singular, plural string + want string + }{ + {0, "object", "", "objects"}, + {1, "object", "", "object"}, + {-1, "object", "", "objects"}, + {42, "object", "", "objects"}, + {2, "vax", "vaxen", "vaxen"}, + + // special cases + {2, "index", "", "indices"}, + + // ending in a sibilant sound + {2, "bus", "", "buses"}, + {2, "bush", "", "bushes"}, + {2, "watch", "", "watches"}, + {2, "box", "", "boxes"}, + + // ending with 'o' preceded by a consonant + {2, "hero", "", "heroes"}, + + // ending with 'y' preceded by a consonant + {2, "lady", "", "ladies"}, + {2, "day", "", "days"}, + } + for _, tt := range tests { + if got := PluralWord(tt.n, tt.singular, tt.plural); got != tt.want { + t.Errorf("PluralWord(%d, %q, %q)=%q; want: %q", tt.n, tt.singular, tt.plural, got, tt.want) + } + } +} + +func TestPlural(t *testing.T) { + tests := []struct { + n int + singular, plural string + want string + }{ + {1, "object", "", "1 object"}, + {42, "object", "", "42 objects"}, + } + for _, tt := range tests { + if got := Plural(tt.n, tt.singular, tt.plural); got != tt.want { + t.Errorf("Plural(%d, %q, %q)=%q; want: %q", tt.n, tt.singular, tt.plural, got, tt.want) + } + } +} + +func TestWordSeries(t *testing.T) { + tests := []struct { + words []string + conjunction string + want string + }{ + {[]string{}, "and", ""}, + {[]string{"foo"}, "and", "foo"}, + {[]string{"foo", "bar"}, "and", "foo and bar"}, + {[]string{"foo", "bar", "baz"}, "and", "foo, bar and baz"}, + {[]string{"foo", "bar", "baz"}, "or", "foo, bar or baz"}, + } + for _, tt := range tests { + if got := WordSeries(tt.words, tt.conjunction); got != tt.want { + t.Errorf("WordSeries(%q, %q)=%q; want: %q", tt.words, tt.conjunction, got, tt.want) + } + } +} + +func TestOxfordWordSeries(t *testing.T) { + tests := []struct { + words []string + conjunction string + want string + }{ + {[]string{}, "and", ""}, + {[]string{"foo"}, "and", "foo"}, + {[]string{"foo", "bar"}, "and", "foo and bar"}, + {[]string{"foo", "bar", "baz"}, "and", "foo, bar, and baz"}, + {[]string{"foo", "bar", "baz"}, "or", "foo, bar, or baz"}, + } + for _, tt := range tests { + if got := OxfordWordSeries(tt.words, tt.conjunction); got != tt.want { + t.Errorf("OxfordWordSeries(%q, %q)=%q; want: %q", tt.words, tt.conjunction, got, tt.want) + } + } +} diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go new file mode 100644 index 00000000..c76190b1 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ftoa.go @@ -0,0 +1,23 @@ +package humanize + +import "strconv" + +func stripTrailingZeros(s string) string { + offset := len(s) - 1 + for offset > 0 { + if s[offset] == '.' { + offset-- + break + } + if s[offset] != '0' { + break + } + offset-- + } + return s[:offset+1] +} + +// Ftoa converts a float to a string with no trailing zeros. +func Ftoa(num float64) string { + return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) +} diff --git a/vendor/github.com/dustin/go-humanize/ftoa_test.go b/vendor/github.com/dustin/go-humanize/ftoa_test.go new file mode 100644 index 00000000..276d411b --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ftoa_test.go @@ -0,0 +1,55 @@ +package humanize + +import ( + "fmt" + "regexp" + "strconv" + "testing" +) + +func TestFtoa(t *testing.T) { + testList{ + {"200", Ftoa(200), "200"}, + {"2", Ftoa(2), "2"}, + {"2.2", Ftoa(2.2), "2.2"}, + {"2.02", Ftoa(2.02), "2.02"}, + {"200.02", Ftoa(200.02), "200.02"}, + }.validate(t) +} + +func BenchmarkFtoaRegexTrailing(b *testing.B) { + trailingZerosRegex := regexp.MustCompile(`\.?0+$`) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + trailingZerosRegex.ReplaceAllString("2.00000", "") + trailingZerosRegex.ReplaceAllString("2.0000", "") + trailingZerosRegex.ReplaceAllString("2.000", "") + trailingZerosRegex.ReplaceAllString("2.00", "") + trailingZerosRegex.ReplaceAllString("2.0", "") + trailingZerosRegex.ReplaceAllString("2", "") + } +} + +func BenchmarkFtoaFunc(b *testing.B) { + for i := 0; i < b.N; i++ { + stripTrailingZeros("2.00000") + stripTrailingZeros("2.0000") + stripTrailingZeros("2.000") + stripTrailingZeros("2.00") + stripTrailingZeros("2.0") + stripTrailingZeros("2") + } +} + +func BenchmarkFmtF(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = fmt.Sprintf("%f", 2.03584) + } +} + +func BenchmarkStrconvF(b *testing.B) { + for i := 0; i < b.N; i++ { + strconv.FormatFloat(2.03584, 'f', 6, 64) + } +} diff --git a/vendor/github.com/dustin/go-humanize/humanize.go b/vendor/github.com/dustin/go-humanize/humanize.go new file mode 100644 index 00000000..a2c2da31 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/humanize.go @@ -0,0 +1,8 @@ +/* +Package humanize converts boring ugly numbers to human-friendly strings and back. + +Durations can be turned into strings such as "3 days ago", numbers +representing sizes like 82854982 into useful strings like, "83 MB" or +"79 MiB" (whichever you prefer). +*/ +package humanize diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go new file mode 100644 index 00000000..dec61865 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/number.go @@ -0,0 +1,192 @@ +package humanize + +/* +Slightly adapted from the source to fit go-humanize. + +Author: https://github.com/gorhill +Source: https://gist.github.com/gorhill/5285193 + +*/ + +import ( + "math" + "strconv" +) + +var ( + renderFloatPrecisionMultipliers = [...]float64{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + } + + renderFloatPrecisionRounders = [...]float64{ + 0.5, + 0.05, + 0.005, + 0.0005, + 0.00005, + 0.000005, + 0.0000005, + 0.00000005, + 0.000000005, + 0.0000000005, + } +) + +// FormatFloat produces a formatted number as string based on the following user-specified criteria: +// * thousands separator +// * decimal separator +// * decimal precision +// +// Usage: s := RenderFloat(format, n) +// The format parameter tells how to render the number n. +// +// See examples: http://play.golang.org/p/LXc1Ddm1lJ +// +// Examples of format strings, given n = 12345.6789: +// "#,###.##" => "12,345.67" +// "#,###." => "12,345" +// "#,###" => "12345,678" +// "#\u202F###,##" => "12 345,68" +// "#.###,###### => 12.345,678900 +// "" (aka default format) => 12,345.67 +// +// The highest precision allowed is 9 digits after the decimal symbol. +// There is also a version for integer number, FormatInteger(), +// which is convenient for calls within template. +func FormatFloat(format string, n float64) string { + // Special cases: + // NaN = "NaN" + // +Inf = "+Infinity" + // -Inf = "-Infinity" + if math.IsNaN(n) { + return "NaN" + } + if n > math.MaxFloat64 { + return "Infinity" + } + if n < -math.MaxFloat64 { + return "-Infinity" + } + + // default format + precision := 2 + decimalStr := "." + thousandStr := "," + positiveStr := "" + negativeStr := "-" + + if len(format) > 0 { + format := []rune(format) + + // If there is an explicit format directive, + // then default values are these: + precision = 9 + thousandStr = "" + + // collect indices of meaningful formatting directives + formatIndx := []int{} + for i, char := range format { + if char != '#' && char != '0' { + formatIndx = append(formatIndx, i) + } + } + + if len(formatIndx) > 0 { + // Directive at index 0: + // Must be a '+' + // Raise an error if not the case + // index: 0123456789 + // +0.000,000 + // +000,000.0 + // +0000.00 + // +0000 + if formatIndx[0] == 0 { + if format[formatIndx[0]] != '+' { + panic("RenderFloat(): invalid positive sign directive") + } + positiveStr = "+" + formatIndx = formatIndx[1:] + } + + // Two directives: + // First is thousands separator + // Raise an error if not followed by 3-digit + // 0123456789 + // 0.000,000 + // 000,000.00 + if len(formatIndx) == 2 { + if (formatIndx[1] - formatIndx[0]) != 4 { + panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") + } + thousandStr = string(format[formatIndx[0]]) + formatIndx = formatIndx[1:] + } + + // One directive: + // Directive is decimal separator + // The number of digit-specifier following the separator indicates wanted precision + // 0123456789 + // 0.00 + // 000,0000 + if len(formatIndx) == 1 { + decimalStr = string(format[formatIndx[0]]) + precision = len(format) - formatIndx[0] - 1 + } + } + } + + // generate sign part + var signStr string + if n >= 0.000000001 { + signStr = positiveStr + } else if n <= -0.000000001 { + signStr = negativeStr + n = -n + } else { + signStr = "" + n = 0.0 + } + + // split number into integer and fractional parts + intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) + + // generate integer part string + intStr := strconv.FormatInt(int64(intf), 10) + + // add thousand separator if required + if len(thousandStr) > 0 { + for i := len(intStr); i > 3; { + i -= 3 + intStr = intStr[:i] + thousandStr + intStr[i:] + } + } + + // no fractional part, we can leave now + if precision == 0 { + return signStr + intStr + } + + // generate fractional part + fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) + // may need padding + if len(fracStr) < precision { + fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr + } + + return signStr + intStr + decimalStr + fracStr +} + +// FormatInteger produces a formatted number as string. +// See FormatFloat. +func FormatInteger(format string, n int) string { + return FormatFloat(format, float64(n)) +} diff --git a/vendor/github.com/dustin/go-humanize/number_test.go b/vendor/github.com/dustin/go-humanize/number_test.go new file mode 100644 index 00000000..516f3378 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/number_test.go @@ -0,0 +1,79 @@ +package humanize + +import ( + "math" + "testing" +) + +type TestStruct struct { + name string + format string + num float64 + formatted string +} + +func TestFormatFloat(t *testing.T) { + tests := []TestStruct{ + {"default", "", 12345.6789, "12,345.68"}, + {"#", "#", 12345.6789, "12345.678900000"}, + {"#.", "#.", 12345.6789, "12346"}, + {"#,#", "#,#", 12345.6789, "12345,7"}, + {"#,##", "#,##", 12345.6789, "12345,68"}, + {"#,###", "#,###", 12345.6789, "12345,679"}, + {"#,###.", "#,###.", 12345.6789, "12,346"}, + {"#,###.##", "#,###.##", 12345.6789, "12,345.68"}, + {"#,###.###", "#,###.###", 12345.6789, "12,345.679"}, + {"#,###.####", "#,###.####", 12345.6789, "12,345.6789"}, + {"#.###,######", "#.###,######", 12345.6789, "12.345,678900"}, + {"bug46", "#,###.##", 52746220055.92342, "52,746,220,055.92"}, + {"#\u202f###,##", "#\u202f###,##", 12345.6789, "12 345,68"}, + + // special cases + {"NaN", "#", math.NaN(), "NaN"}, + {"+Inf", "#", math.Inf(1), "Infinity"}, + {"-Inf", "#", math.Inf(-1), "-Infinity"}, + {"signStr <= -0.000000001", "", -0.000000002, "-0.00"}, + {"signStr = 0", "", 0, "0.00"}, + {"Format directive must start with +", "+000", 12345.6789, "+12345.678900000"}, + } + + for _, test := range tests { + got := FormatFloat(test.format, test.num) + if got != test.formatted { + t.Errorf("On %v (%v, %v), got %v, wanted %v", + test.name, test.format, test.num, got, test.formatted) + } + } + // Test a single integer + got := FormatInteger("#", 12345) + if got != "12345.000000000" { + t.Errorf("On %v (%v, %v), got %v, wanted %v", + "integerTest", "#", 12345, got, "12345.000000000") + } + // Test the things that could panic + panictests := []TestStruct{ + {"RenderFloat(): invalid positive sign directive", "-", 12345.6789, "12,345.68"}, + {"RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers", "0.01", 12345.6789, "12,345.68"}, + } + for _, test := range panictests { + didPanic := false + var message interface{} + func() { + + defer func() { + if message = recover(); message != nil { + didPanic = true + } + }() + + // call the target function + _ = FormatFloat(test.format, test.num) + + }() + if didPanic != true { + t.Errorf("On %v, should have panic and did not.", + test.name) + } + } + +} diff --git a/vendor/github.com/dustin/go-humanize/ordinals.go b/vendor/github.com/dustin/go-humanize/ordinals.go new file mode 100644 index 00000000..43d88a86 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ordinals.go @@ -0,0 +1,25 @@ +package humanize + +import "strconv" + +// Ordinal gives you the input number in a rank/ordinal format. +// +// Ordinal(3) -> 3rd +func Ordinal(x int) string { + suffix := "th" + switch x % 10 { + case 1: + if x%100 != 11 { + suffix = "st" + } + case 2: + if x%100 != 12 { + suffix = "nd" + } + case 3: + if x%100 != 13 { + suffix = "rd" + } + } + return strconv.Itoa(x) + suffix +} diff --git a/vendor/github.com/dustin/go-humanize/ordinals_test.go b/vendor/github.com/dustin/go-humanize/ordinals_test.go new file mode 100644 index 00000000..51d85ee7 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ordinals_test.go @@ -0,0 +1,22 @@ +package humanize + +import ( + "testing" +) + +func TestOrdinals(t *testing.T) { + testList{ + {"0", Ordinal(0), "0th"}, + {"1", Ordinal(1), "1st"}, + {"2", Ordinal(2), "2nd"}, + {"3", Ordinal(3), "3rd"}, + {"4", Ordinal(4), "4th"}, + {"10", Ordinal(10), "10th"}, + {"11", Ordinal(11), "11th"}, + {"12", Ordinal(12), "12th"}, + {"13", Ordinal(13), "13th"}, + {"101", Ordinal(101), "101st"}, + {"102", Ordinal(102), "102nd"}, + {"103", Ordinal(103), "103rd"}, + }.validate(t) +} diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go new file mode 100644 index 00000000..b24e4816 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/si.go @@ -0,0 +1,113 @@ +package humanize + +import ( + "errors" + "math" + "regexp" + "strconv" +) + +var siPrefixTable = map[float64]string{ + -24: "y", // yocto + -21: "z", // zepto + -18: "a", // atto + -15: "f", // femto + -12: "p", // pico + -9: "n", // nano + -6: "µ", // micro + -3: "m", // milli + 0: "", + 3: "k", // kilo + 6: "M", // mega + 9: "G", // giga + 12: "T", // tera + 15: "P", // peta + 18: "E", // exa + 21: "Z", // zetta + 24: "Y", // yotta +} + +var revSIPrefixTable = revfmap(siPrefixTable) + +// revfmap reverses the map and precomputes the power multiplier +func revfmap(in map[float64]string) map[string]float64 { + rv := map[string]float64{} + for k, v := range in { + rv[v] = math.Pow(10, k) + } + return rv +} + +var riParseRegex *regexp.Regexp + +func init() { + ri := `^([\-0-9.]+)\s?([` + for _, v := range siPrefixTable { + ri += v + } + ri += `]?)(.*)` + + riParseRegex = regexp.MustCompile(ri) +} + +// ComputeSI finds the most appropriate SI prefix for the given number +// and returns the prefix along with the value adjusted to be within +// that prefix. +// +// See also: SI, ParseSI. +// +// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") +func ComputeSI(input float64) (float64, string) { + if input == 0 { + return 0, "" + } + mag := math.Abs(input) + exponent := math.Floor(logn(mag, 10)) + exponent = math.Floor(exponent/3) * 3 + + value := mag / math.Pow(10, exponent) + + // Handle special case where value is exactly 1000.0 + // Should return 1 M instead of 1000 k + if value == 1000.0 { + exponent += 3 + value = mag / math.Pow(10, exponent) + } + + value = math.Copysign(value, input) + + prefix := siPrefixTable[exponent] + return value, prefix +} + +// SI returns a string with default formatting. +// +// SI uses Ftoa to format float value, removing trailing zeros. +// +// See also: ComputeSI, ParseSI. +// +// e.g. SI(1000000, "B") -> 1 MB +// e.g. SI(2.2345e-12, "F") -> 2.2345 pF +func SI(input float64, unit string) string { + value, prefix := ComputeSI(input) + return Ftoa(value) + " " + prefix + unit +} + +var errInvalid = errors.New("invalid input") + +// ParseSI parses an SI string back into the number and unit. +// +// See also: SI, ComputeSI. +// +// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) +func ParseSI(input string) (float64, string, error) { + found := riParseRegex.FindStringSubmatch(input) + if len(found) != 4 { + return 0, "", errInvalid + } + mag := revSIPrefixTable[found[2]] + unit := found[3] + + base, err := strconv.ParseFloat(found[1], 64) + return base * mag, unit, err +} diff --git a/vendor/github.com/dustin/go-humanize/si_test.go b/vendor/github.com/dustin/go-humanize/si_test.go new file mode 100644 index 00000000..bc5bac66 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/si_test.go @@ -0,0 +1,101 @@ +package humanize + +import ( + "math" + "testing" +) + +func TestSI(t *testing.T) { + tests := []struct { + name string + num float64 + formatted string + }{ + {"e-24", 1e-24, "1 yF"}, + {"e-21", 1e-21, "1 zF"}, + {"e-18", 1e-18, "1 aF"}, + {"e-15", 1e-15, "1 fF"}, + {"e-12", 1e-12, "1 pF"}, + {"e-12", 2.2345e-12, "2.2345 pF"}, + {"e-12", 2.23e-12, "2.23 pF"}, + {"e-11", 2.23e-11, "22.3 pF"}, + {"e-10", 2.2e-10, "220 pF"}, + {"e-9", 2.2e-9, "2.2 nF"}, + {"e-8", 2.2e-8, "22 nF"}, + {"e-7", 2.2e-7, "220 nF"}, + {"e-6", 2.2e-6, "2.2 µF"}, + {"e-6", 1e-6, "1 µF"}, + {"e-5", 2.2e-5, "22 µF"}, + {"e-4", 2.2e-4, "220 µF"}, + {"e-3", 2.2e-3, "2.2 mF"}, + {"e-2", 2.2e-2, "22 mF"}, + {"e-1", 2.2e-1, "220 mF"}, + {"e+0", 2.2e-0, "2.2 F"}, + {"e+0", 2.2, "2.2 F"}, + {"e+1", 2.2e+1, "22 F"}, + {"0", 0, "0 F"}, + {"e+1", 22, "22 F"}, + {"e+2", 2.2e+2, "220 F"}, + {"e+2", 220, "220 F"}, + {"e+3", 2.2e+3, "2.2 kF"}, + {"e+3", 2200, "2.2 kF"}, + {"e+4", 2.2e+4, "22 kF"}, + {"e+4", 22000, "22 kF"}, + {"e+5", 2.2e+5, "220 kF"}, + {"e+6", 2.2e+6, "2.2 MF"}, + {"e+6", 1e+6, "1 MF"}, + {"e+7", 2.2e+7, "22 MF"}, + {"e+8", 2.2e+8, "220 MF"}, + {"e+9", 2.2e+9, "2.2 GF"}, + {"e+10", 2.2e+10, "22 GF"}, + {"e+11", 2.2e+11, "220 GF"}, + {"e+12", 2.2e+12, "2.2 TF"}, + {"e+15", 2.2e+15, "2.2 PF"}, + {"e+18", 2.2e+18, "2.2 EF"}, + {"e+21", 2.2e+21, "2.2 ZF"}, + {"e+24", 2.2e+24, "2.2 YF"}, + + // special case + {"1F", 1000 * 1000, "1 MF"}, + {"1F", 1e6, "1 MF"}, + + // negative number + {"-100 F", -100, "-100 F"}, + } + + for _, test := range tests { + got := SI(test.num, "F") + if got != test.formatted { + t.Errorf("On %v (%v), got %v, wanted %v", + test.name, test.num, got, test.formatted) + } + + gotf, gotu, err := ParseSI(test.formatted) + if err != nil { + t.Errorf("Error parsing %v (%v): %v", test.name, test.formatted, err) + continue + } + + if math.Abs(1-(gotf/test.num)) > 0.01 { + t.Errorf("On %v (%v), got %v, wanted %v (±%v)", + test.name, test.formatted, gotf, test.num, + math.Abs(1-(gotf/test.num))) + } + if gotu != "F" { + t.Errorf("On %v (%v), expected unit F, got %v", + test.name, test.formatted, gotu) + } + } + + // Parse error + gotf, gotu, err := ParseSI("x1.21JW") // 1.21 jigga whats + if err == nil { + t.Errorf("Expected error on x1.21JW, got %v %v", gotf, gotu) + } +} + +func BenchmarkParseSI(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseSI("2.2346ZB") + } +} diff --git a/vendor/github.com/dustin/go-humanize/times.go b/vendor/github.com/dustin/go-humanize/times.go new file mode 100644 index 00000000..dd3fbf5e --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/times.go @@ -0,0 +1,117 @@ +package humanize + +import ( + "fmt" + "math" + "sort" + "time" +) + +// Seconds-based time units +const ( + Day = 24 * time.Hour + Week = 7 * Day + Month = 30 * Day + Year = 12 * Month + LongTime = 37 * Year +) + +// Time formats a time into a relative string. +// +// Time(someT) -> "3 weeks ago" +func Time(then time.Time) string { + return RelTime(then, time.Now(), "ago", "from now") +} + +// A RelTimeMagnitude struct contains a relative time point at which +// the relative format of time will switch to a new format string. A +// slice of these in ascending order by their "D" field is passed to +// CustomRelTime to format durations. +// +// The Format field is a string that may contain a "%s" which will be +// replaced with the appropriate signed label (e.g. "ago" or "from +// now") and a "%d" that will be replaced by the quantity. +// +// The DivBy field is the amount of time the time difference must be +// divided by in order to display correctly. +// +// e.g. if D is 2*time.Minute and you want to display "%d minutes %s" +// DivBy should be time.Minute so whatever the duration is will be +// expressed in minutes. +type RelTimeMagnitude struct { + D time.Duration + Format string + DivBy time.Duration +} + +var defaultMagnitudes = []RelTimeMagnitude{ + {time.Second, "now", time.Second}, + {2 * time.Second, "1 second %s", 1}, + {time.Minute, "%d seconds %s", time.Second}, + {2 * time.Minute, "1 minute %s", 1}, + {time.Hour, "%d minutes %s", time.Minute}, + {2 * time.Hour, "1 hour %s", 1}, + {Day, "%d hours %s", time.Hour}, + {2 * Day, "1 day %s", 1}, + {Week, "%d days %s", Day}, + {2 * Week, "1 week %s", 1}, + {Month, "%d weeks %s", Week}, + {2 * Month, "1 month %s", 1}, + {Year, "%d months %s", Month}, + {18 * Month, "1 year %s", 1}, + {2 * Year, "2 years %s", 1}, + {LongTime, "%d years %s", Year}, + {math.MaxInt64, "a long while %s", 1}, +} + +// RelTime formats a time into a relative string. +// +// It takes two times and two labels. In addition to the generic time +// delta string (e.g. 5 minutes), the labels are used applied so that +// the label corresponding to the smaller time is applied. +// +// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" +func RelTime(a, b time.Time, albl, blbl string) string { + return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) +} + +// CustomRelTime formats a time into a relative string. +// +// It takes two times two labels and a table of relative time formats. +// In addition to the generic time delta string (e.g. 5 minutes), the +// labels are used applied so that the label corresponding to the +// smaller time is applied. +func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { + lbl := albl + diff := b.Sub(a) + + if a.After(b) { + lbl = blbl + diff = a.Sub(b) + } + + n := sort.Search(len(magnitudes), func(i int) bool { + return magnitudes[i].D > diff + }) + + if n >= len(magnitudes) { + n = len(magnitudes) - 1 + } + mag := magnitudes[n] + args := []interface{}{} + escaped := false + for _, ch := range mag.Format { + if escaped { + switch ch { + case 's': + args = append(args, lbl) + case 'd': + args = append(args, diff/mag.DivBy) + } + escaped = false + } else { + escaped = ch == '%' + } + } + return fmt.Sprintf(mag.Format, args...) +} diff --git a/vendor/github.com/dustin/go-humanize/times_test.go b/vendor/github.com/dustin/go-humanize/times_test.go new file mode 100644 index 00000000..b1ab8bf8 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/times_test.go @@ -0,0 +1,124 @@ +package humanize + +import ( + "math" + "testing" + "time" +) + +func TestPast(t *testing.T) { + now := time.Now() + testList{ + {"now", Time(now), "now"}, + {"1 second ago", Time(now.Add(-1 * time.Second)), "1 second ago"}, + {"12 seconds ago", Time(now.Add(-12 * time.Second)), "12 seconds ago"}, + {"30 seconds ago", Time(now.Add(-30 * time.Second)), "30 seconds ago"}, + {"45 seconds ago", Time(now.Add(-45 * time.Second)), "45 seconds ago"}, + {"1 minute ago", Time(now.Add(-63 * time.Second)), "1 minute ago"}, + {"15 minutes ago", Time(now.Add(-15 * time.Minute)), "15 minutes ago"}, + {"1 hour ago", Time(now.Add(-63 * time.Minute)), "1 hour ago"}, + {"2 hours ago", Time(now.Add(-2 * time.Hour)), "2 hours ago"}, + {"21 hours ago", Time(now.Add(-21 * time.Hour)), "21 hours ago"}, + {"1 day ago", Time(now.Add(-26 * time.Hour)), "1 day ago"}, + {"2 days ago", Time(now.Add(-49 * time.Hour)), "2 days ago"}, + {"3 days ago", Time(now.Add(-3 * Day)), "3 days ago"}, + {"1 week ago (1)", Time(now.Add(-7 * Day)), "1 week ago"}, + {"1 week ago (2)", Time(now.Add(-12 * Day)), "1 week ago"}, + {"2 weeks ago", Time(now.Add(-15 * Day)), "2 weeks ago"}, + {"1 month ago", Time(now.Add(-39 * Day)), "1 month ago"}, + {"3 months ago", Time(now.Add(-99 * Day)), "3 months ago"}, + {"1 year ago (1)", Time(now.Add(-365 * Day)), "1 year ago"}, + {"1 year ago (1)", Time(now.Add(-400 * Day)), "1 year ago"}, + {"2 years ago (1)", Time(now.Add(-548 * Day)), "2 years ago"}, + {"2 years ago (2)", Time(now.Add(-725 * Day)), "2 years ago"}, + {"2 years ago (3)", Time(now.Add(-800 * Day)), "2 years ago"}, + {"3 years ago", Time(now.Add(-3 * Year)), "3 years ago"}, + {"long ago", Time(now.Add(-LongTime)), "a long while ago"}, + }.validate(t) +} + +func TestReltimeOffbyone(t *testing.T) { + testList{ + {"1w-1", RelTime(time.Unix(0, 0), time.Unix(7*24*60*60, -1), "ago", ""), "6 days ago"}, + {"1w±0", RelTime(time.Unix(0, 0), time.Unix(7*24*60*60, 0), "ago", ""), "1 week ago"}, + {"1w+1", RelTime(time.Unix(0, 0), time.Unix(7*24*60*60, 1), "ago", ""), "1 week ago"}, + {"2w-1", RelTime(time.Unix(0, 0), time.Unix(14*24*60*60, -1), "ago", ""), "1 week ago"}, + {"2w±0", RelTime(time.Unix(0, 0), time.Unix(14*24*60*60, 0), "ago", ""), "2 weeks ago"}, + {"2w+1", RelTime(time.Unix(0, 0), time.Unix(14*24*60*60, 1), "ago", ""), "2 weeks ago"}, + }.validate(t) +} + +func TestFuture(t *testing.T) { + // Add a little time so that these things properly line up in + // the future. + now := time.Now().Add(time.Millisecond * 250) + testList{ + {"now", Time(now), "now"}, + {"1 second from now", Time(now.Add(+1 * time.Second)), "1 second from now"}, + {"12 seconds from now", Time(now.Add(+12 * time.Second)), "12 seconds from now"}, + {"30 seconds from now", Time(now.Add(+30 * time.Second)), "30 seconds from now"}, + {"45 seconds from now", Time(now.Add(+45 * time.Second)), "45 seconds from now"}, + {"15 minutes from now", Time(now.Add(+15 * time.Minute)), "15 minutes from now"}, + {"2 hours from now", Time(now.Add(+2 * time.Hour)), "2 hours from now"}, + {"21 hours from now", Time(now.Add(+21 * time.Hour)), "21 hours from now"}, + {"1 day from now", Time(now.Add(+26 * time.Hour)), "1 day from now"}, + {"2 days from now", Time(now.Add(+49 * time.Hour)), "2 days from now"}, + {"3 days from now", Time(now.Add(+3 * Day)), "3 days from now"}, + {"1 week from now (1)", Time(now.Add(+7 * Day)), "1 week from now"}, + {"1 week from now (2)", Time(now.Add(+12 * Day)), "1 week from now"}, + {"2 weeks from now", Time(now.Add(+15 * Day)), "2 weeks from now"}, + {"1 month from now", Time(now.Add(+30 * Day)), "1 month from now"}, + {"1 year from now", Time(now.Add(+365 * Day)), "1 year from now"}, + {"2 years from now", Time(now.Add(+2 * Year)), "2 years from now"}, + {"a while from now", Time(now.Add(+LongTime)), "a long while from now"}, + }.validate(t) +} + +func TestRange(t *testing.T) { + start := time.Time{} + end := time.Unix(math.MaxInt64, math.MaxInt64) + x := RelTime(start, end, "ago", "from now") + if x != "a long while from now" { + t.Errorf("Expected a long while from now, got %q", x) + } +} + +func TestCustomRelTime(t *testing.T) { + now := time.Now().Add(time.Millisecond * 250) + magnitudes := []RelTimeMagnitude{ + {time.Second, "now", time.Second}, + {2 * time.Second, "1 second %s", 1}, + {time.Minute, "%d seconds %s", time.Second}, + {Day - time.Second, "%d minutes %s", time.Minute}, + {Day, "%d hours %s", time.Hour}, + {2 * Day, "1 day %s", 1}, + {Week, "%d days %s", Day}, + {2 * Week, "1 week %s", 1}, + {6 * Month, "%d weeks %s", Week}, + {Year, "%d months %s", Month}, + } + customRelTime := func(then time.Time) string { + return CustomRelTime(then, time.Now(), "ago", "from now", magnitudes) + } + testList{ + {"now", customRelTime(now), "now"}, + {"1 second from now", customRelTime(now.Add(+1 * time.Second)), "1 second from now"}, + {"12 seconds from now", customRelTime(now.Add(+12 * time.Second)), "12 seconds from now"}, + {"30 seconds from now", customRelTime(now.Add(+30 * time.Second)), "30 seconds from now"}, + {"45 seconds from now", customRelTime(now.Add(+45 * time.Second)), "45 seconds from now"}, + {"15 minutes from now", customRelTime(now.Add(+15 * time.Minute)), "15 minutes from now"}, + {"2 hours from now", customRelTime(now.Add(+2 * time.Hour)), "120 minutes from now"}, + {"21 hours from now", customRelTime(now.Add(+21 * time.Hour)), "1260 minutes from now"}, + {"1 day from now", customRelTime(now.Add(+26 * time.Hour)), "1 day from now"}, + {"2 days from now", customRelTime(now.Add(+49 * time.Hour)), "2 days from now"}, + {"3 days from now", customRelTime(now.Add(+3 * Day)), "3 days from now"}, + {"1 week from now (1)", customRelTime(now.Add(+7 * Day)), "1 week from now"}, + {"1 week from now (2)", customRelTime(now.Add(+12 * Day)), "1 week from now"}, + {"2 weeks from now", customRelTime(now.Add(+15 * Day)), "2 weeks from now"}, + {"1 month from now", customRelTime(now.Add(+30 * Day)), "4 weeks from now"}, + {"6 months from now", customRelTime(now.Add(+6*Month - time.Second)), "25 weeks from now"}, + {"1 year from now", customRelTime(now.Add(+365 * Day)), "12 months from now"}, + {"2 years from now", customRelTime(now.Add(+2 * Year)), "24 months from now"}, + {"a while from now", customRelTime(now.Add(+LongTime)), "444 months from now"}, + }.validate(t) +} diff --git a/vendor/github.com/elazarl/goproxy/.gitignore b/vendor/github.com/elazarl/goproxy/.gitignore new file mode 100644 index 00000000..1005f6f1 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/.gitignore @@ -0,0 +1,2 @@ +bin +*.swp diff --git a/vendor/github.com/elazarl/goproxy/LICENSE b/vendor/github.com/elazarl/goproxy/LICENSE new file mode 100644 index 00000000..2067e567 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 Elazar Leibovich. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Elazar Leibovich. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/elazarl/goproxy/README.md b/vendor/github.com/elazarl/goproxy/README.md new file mode 100644 index 00000000..50d91efe --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/README.md @@ -0,0 +1,122 @@ +# Introduction + +[![GoDoc](https://godoc.org/github.com/elazarl/goproxy?status.svg)](https://godoc.org/github.com/elazarl/goproxy) +[![Join the chat at https://gitter.im/elazarl/goproxy](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/elazarl/goproxy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Package goproxy provides a customizable HTTP proxy library for Go (golang), + +It supports regular HTTP proxy, HTTPS through CONNECT, and "hijacking" HTTPS +connection using "Man in the Middle" style attack. + +The intent of the proxy, is to be usable with reasonable amount of traffic +yet, customizable and programmable. + +The proxy itself is simply a `net/http` handler. + +In order to use goproxy, one should set their browser to use goproxy as an HTTP +proxy. Here is how you do that [in Chrome](https://support.google.com/chrome/answer/96815?hl=en) +and [in Firefox](http://www.wikihow.com/Enter-Proxy-Settings-in-Firefox). + +For example, the URL you should use as proxy when running `./bin/basic` is +`localhost:8080`, as this is the default binding for the basic proxy. + +## Mailing List + +New features would be discussed on the [mailing list](https://groups.google.com/forum/#!forum/goproxy-dev) +before their development. + +## Latest Stable Release + +Get the latest goproxy from `gopkg.in/elazarl/goproxy.v1`. + +# Why not Fiddler2? + +Fiddler is an excellent software with similar intent. However, Fiddler is not +as customizable as goproxy intend to be. The main difference is, Fiddler is not +intended to be used as a real proxy. + +A possible use case that suits goproxy but +not Fiddler, is, gathering statistics on page load times for a certain website over a week. +With goproxy you could ask all your users to set their proxy to a dedicated machine running a +goproxy server. Fiddler is a GUI app not designed to be ran like a server for multiple users. + +# A taste of goproxy + +To get a taste of `goproxy`, a basic HTTP/HTTPS transparent proxy + +```go +package main + +import ( + "github.com/elazarl/goproxy" + "log" + "net/http" +) + +func main() { + proxy := goproxy.NewProxyHttpServer() + proxy.Verbose = true + log.Fatal(http.ListenAndServe(":8080", proxy)) +} +``` + +This line will add `X-GoProxy: yxorPoG-X` header to all requests sent through the proxy + +```go +proxy.OnRequest().DoFunc( + func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) { + r.Header.Set("X-GoProxy","yxorPoG-X") + return r,nil + }) +``` + +`DoFunc` will process all incoming requests to the proxy. It will add a header to the request +and return it. The proxy will send the modified request. + +Note that we returned nil value as the response. Had we returned a response, goproxy would +have discarded the request and sent the new response to the client. + +In order to refuse connections to reddit at work time + +```go +proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc( + func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) { + if h,_,_ := time.Now().Clock(); h >= 8 && h <= 17 { + return r,goproxy.NewResponse(r, + goproxy.ContentTypeText,http.StatusForbidden, + "Don't waste your time!") + } + return r,nil +}) +``` + +`DstHostIs` returns a `ReqCondition`, that is a function receiving a `Request` and returning a boolean +we will only process requests that matches the condition. `DstHostIs("www.reddit.com")` will return +a `ReqCondition` accepting only requests directed to "www.reddit.com". + +`DoFunc` will receive a function that will preprocess the request. We can change the request, or +return a response. If the time is between 8:00am and 17:00pm, we will neglect the request, and +return a precanned text response saying "do not waste your time". + +See additional examples in the examples directory. + +# What's New + +1. Ability to `Hijack` CONNECT requests. See +[the eavesdropper example](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-eavesdropper/main.go#L27) +2. Transparent proxy support for http/https including MITM certificate generation for TLS. See the [transparent example.](https://github.com/elazarl/goproxy/tree/master/examples/goproxy-transparent) + +# License + +I put the software temporarily under the Go-compatible BSD license, +if this prevents someone from using the software, do let me know and I'll consider changing it. + +At any rate, user feedback is very important for me, so I'll be delighted to know if you're using this package. + +# Beta Software + +I've received a positive feedback from a few people who use goproxy in production settings. +I believe it is good enough for usage. + +I'll try to keep reasonable backwards compatibility. In case of a major API change, +I'll change the import path. diff --git a/vendor/github.com/elazarl/goproxy/actions.go b/vendor/github.com/elazarl/goproxy/actions.go new file mode 100644 index 00000000..e1a3e7ff --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/actions.go @@ -0,0 +1,57 @@ +package goproxy + +import "net/http" + +// ReqHandler will "tamper" with the request coming to the proxy server +// If Handle returns req,nil the proxy will send the returned request +// to the destination server. If it returns nil,resp the proxy will +// skip sending any requests, and will simply return the response `resp` +// to the client. +type ReqHandler interface { + Handle(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) +} + +// A wrapper that would convert a function to a ReqHandler interface type +type FuncReqHandler func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) + +// FuncReqHandler.Handle(req,ctx) <=> FuncReqHandler(req,ctx) +func (f FuncReqHandler) Handle(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) { + return f(req, ctx) +} + +// after the proxy have sent the request to the destination server, it will +// "filter" the response through the RespHandlers it has. +// The proxy server will send to the client the response returned by the RespHandler. +// In case of error, resp will be nil, and ctx.RoundTrip.Error will contain the error +type RespHandler interface { + Handle(resp *http.Response, ctx *ProxyCtx) *http.Response +} + +// A wrapper that would convert a function to a RespHandler interface type +type FuncRespHandler func(resp *http.Response, ctx *ProxyCtx) *http.Response + +// FuncRespHandler.Handle(req,ctx) <=> FuncRespHandler(req,ctx) +func (f FuncRespHandler) Handle(resp *http.Response, ctx *ProxyCtx) *http.Response { + return f(resp, ctx) +} + +// When a client send a CONNECT request to a host, the request is filtered through +// all the HttpsHandlers the proxy has, and if one returns true, the connection is +// sniffed using Man in the Middle attack. +// That is, the proxy will create a TLS connection with the client, another TLS +// connection with the destination the client wished to connect to, and would +// send back and forth all messages from the server to the client and vice versa. +// The request and responses sent in this Man In the Middle channel are filtered +// through the usual flow (request and response filtered through the ReqHandlers +// and RespHandlers) +type HttpsHandler interface { + HandleConnect(req string, ctx *ProxyCtx) (*ConnectAction, string) +} + +// A wrapper that would convert a function to a HttpsHandler interface type +type FuncHttpsHandler func(host string, ctx *ProxyCtx) (*ConnectAction, string) + +// FuncHttpsHandler should implement the RespHandler interface +func (f FuncHttpsHandler) HandleConnect(host string, ctx *ProxyCtx) (*ConnectAction, string) { + return f(host, ctx) +} diff --git a/vendor/github.com/elazarl/goproxy/all.bash b/vendor/github.com/elazarl/goproxy/all.bash new file mode 100755 index 00000000..6503e73d --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/all.bash @@ -0,0 +1,15 @@ +#!/bin/bash + +go test || exit +for action in $@; do go $action; done + +mkdir -p bin +find regretable examples/* ext/* -maxdepth 0 -type d | while read d; do + (cd $d + go build -o ../../bin/$(basename $d) + find *_test.go -maxdepth 0 2>/dev/null|while read f;do + for action in $@; do go $action; done + go test + break + done) +done diff --git a/vendor/github.com/elazarl/goproxy/ca.pem b/vendor/github.com/elazarl/goproxy/ca.pem new file mode 100644 index 00000000..62653dae --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ca.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD +VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM +B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0 +aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0 +MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE +CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV +BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI +hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9 +3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP +sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9 +V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh +hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr +lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq +j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo +WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD +fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj +YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh +WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj +UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4 +uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F +AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0 +C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3 +Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin +o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye +i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr +bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY +VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft +8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86 +NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV +BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA== +-----END CERTIFICATE----- diff --git a/vendor/github.com/elazarl/goproxy/certs.go b/vendor/github.com/elazarl/goproxy/certs.go new file mode 100644 index 00000000..4731971e --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/certs.go @@ -0,0 +1,111 @@ +package goproxy + +import ( + "crypto/tls" + "crypto/x509" +) + +func init() { + if goproxyCaErr != nil { + panic("Error parsing builtin CA " + goproxyCaErr.Error()) + } + var err error + if GoproxyCa.Leaf, err = x509.ParseCertificate(GoproxyCa.Certificate[0]); err != nil { + panic("Error parsing builtin CA " + err.Error()) + } +} + +var tlsClientSkipVerify = &tls.Config{InsecureSkipVerify: true} + +var defaultTLSConfig = &tls.Config{ + InsecureSkipVerify: true, +} + +var CA_CERT = []byte(`-----BEGIN CERTIFICATE----- +MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD +VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM +B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0 +aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0 +MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE +CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV +BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI +hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9 +3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP +sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9 +V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh +hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr +lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq +j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo +WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD +fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj +YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh +WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj +UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4 +uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F +AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0 +C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3 +Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin +o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye +i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr +bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY +VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft +8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86 +NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV +BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA== +-----END CERTIFICATE-----`) + +var CA_KEY = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAnhDL4fqGGhjWzRBFy8iHGuNIdo79FtoWPevCpyek6AWrTuBF +0j3dzRMUpAkemC/p94tGES9f9iWUVi7gnfmUz1lxhjiqUoW5K1xfwmbx+qmC2YAw +HM+yq2oOLwz1FAYoQ3NT0gU6cJXtIB6Hjmxwy4jfDPzCuMFwfvOq4eS+pRJhnPTf +m31XpZOsfJMS9PjD6UU5U3ZsD/oMAjGuMGIXoOGgmqeFrRJm0N+/vtenAYbcSED+ +qiGGJisOu5grvMl0RJAvjgvDMw+6lWKCpqV+/5gd9CNuFP3nUhW6tbY0mBHIETrZ +0uuUdh21P20JMKt34ok0wn6On2ECN0i7UGv+SJ9TgXj7hksxH1R6OLQaSQ8qxh3I +yeqPSnQ+iDK8/WXiqZug8iYxi1qgW5iYxiV5uAL0s3XRsv3Urj6Mu3QjVie0TOuq +AmhawnO1gPDnjc3NLLlb79yrhdFiC2rVvRFbC5SKzB7OYyh7IdnwFAl7bEyMA6WU +BIN+prw4rdYAEcmnLjNSudQGIy48hPMP8W4PHgLkjDCULryAcBluU2qkFkJfScUK +0qNg5wjZKjkdtDY4LxAX7MZW524dRKiTiFLLYEF9nWl+/OKoF561YnAW9qkYHjic +geFYo0q+o7Es0jLt75MZGJY6iasBYzXxVJH0tlsHGkkrs8tLNapglhNEJkcCAwEA +AQKCAgAwSuNvxHHqUUJ3XoxkiXy1u1EtX9x1eeYnvvs2xMb+WJURQTYz2NEGUdkR +kPO2/ZSXHAcpQvcnpi2e8y2PNmy/uQ0VPATVt6NuWweqxncR5W5j82U/uDlXY8y3 +lVbfak4s5XRri0tikHvlP06dNgZ0OPok5qi7d+Zd8yZ3Y8LXfjkykiIrSG1Z2jdt +zCWTkNmSUKMGG/1CGFxI41Lb12xuq+C8v4f469Fb6bCUpyCQN9rffHQSGLH6wVb7 ++68JO+d49zCATpmx5RFViMZwEcouXxRvvc9pPHXLP3ZPBD8nYu9kTD220mEGgWcZ +3L9dDlZPcSocbjw295WMvHz2QjhrDrb8gXwdpoRyuyofqgCyNxSnEC5M13SjOxtf +pjGzjTqh0kDlKXg2/eTkd9xIHjVhFYiHIEeITM/lHCfWwBCYxViuuF7pSRPzTe8U +C440b62qZSPMjVoquaMg+qx0n9fKSo6n1FIKHypv3Kue2G0WhDeK6u0U288vQ1t4 +Ood3Qa13gZ+9hwDLbM/AoBfVBDlP/tpAwa7AIIU1ZRDNbZr7emFdctx9B6kLINv3 +4PDOGM2xrjOuACSGMq8Zcu7LBz35PpIZtviJOeKNwUd8/xHjWC6W0itgfJb5I1Nm +V6Vj368pGlJx6Se26lvXwyyrc9pSw6jSAwARBeU4YkNWpi4i6QKCAQEA0T7u3P/9 +jZJSnDN1o2PXymDrJulE61yguhc/QSmLccEPZe7or06/DmEhhKuCbv+1MswKDeag +/1JdFPGhL2+4G/f/9BK3BJPdcOZSz7K6Ty8AMMBf8AehKTcSBqwkJWcbEvpHpKJ6 +eDqn1B6brXTNKMT6fEEXCuZJGPBpNidyLv/xXDcN7kCOo3nGYKfB5OhFpNiL63tw ++LntU56WESZwEqr8Pf80uFvsyXQK3a5q5HhIQtxl6tqQuPlNjsDBvCqj0x72mmaJ +ZVsVWlv7khUrCwAXz7Y8K7mKKBd2ekF5hSbryfJsxFyvEaWUPhnJpTKV85lAS+tt +FQuIp9TvKYlRQwKCAQEAwWJN8jysapdhi67jO0HtYOEl9wwnF4w6XtiOYtllkMmC +06/e9h7RsRyWPMdu3qRDPUYFaVDy6+dpUDSQ0+E2Ot6AHtVyvjeUTIL651mFIo/7 +OSUCEc+HRo3SfPXdPhSQ2thNTxl6y9XcFacuvbthgr70KXbvC4k6IEmdpf/0Kgs9 +7QTZCG26HDrEZ2q9yMRlRaL2SRD+7Y2xra7gB+cQGFj6yn0Wd/07er49RqMXidQf +KR2oYfev2BDtHXoSZFfhFGHlOdLvWRh90D4qZf4vQ+g/EIMgcNSoxjvph1EShmKt +sjhTHtoHuu+XmEQvIewk2oCI+JvofBkcnpFrVvUUrQKCAQAaTIufETmgCo0BfuJB +N/JOSGIl0NnNryWwXe2gVgVltbsmt6FdL0uKFiEtWJUbOF5g1Q5Kcvs3O/XhBQGa +QbNlKIVt+tAv7hm97+Tmn/MUsraWagdk1sCluns0hXxBizT27KgGhDlaVRz05yfv +5CdJAYDuDwxDXXBAhy7iFJEgYSDH00+X61tCJrMNQOh4ycy/DEyBu1EWod+3S85W +t3sMjZsIe8P3i+4137Th6eMbdha2+JaCrxfTd9oMoCN5b+6JQXIDM/H+4DTN15PF +540yY7+aZrAnWrmHknNcqFAKsTqfdi2/fFqwoBwCtiEG91WreU6AfEWIiJuTZIru +sIibAoIBAAqIwlo5t+KukF+9jR9DPh0S5rCIdvCvcNaN0WPNF91FPN0vLWQW1bFi +L0TsUDvMkuUZlV3hTPpQxsnZszH3iK64RB5p3jBCcs+gKu7DT59MXJEGVRCHT4Um +YJryAbVKBYIGWl++sZO8+JotWzx2op8uq7o+glMMjKAJoo7SXIiVyC/LHc95urOi +9+PySphPKn0anXPpexmRqGYfqpCDo7rPzgmNutWac80B4/CfHb8iUPg6Z1u+1FNe +yKvcZHgW2Wn00znNJcCitufLGyAnMofudND/c5rx2qfBx7zZS7sKUQ/uRYjes6EZ +QBbJUA/2/yLv8YYpaAaqj4aLwV8hRpkCggEBAIh3e25tr3avCdGgtCxS7Y1blQ2c +ue4erZKmFP1u8wTNHQ03T6sECZbnIfEywRD/esHpclfF3kYAKDRqIP4K905Rb0iH +759ZWt2iCbqZznf50XTvptdmjm5KxvouJzScnQ52gIV6L+QrCKIPelLBEIqCJREh +pmcjjocD/UCCSuHgbAYNNnO/JdhnSylz1tIg26I+2iLNyeTKIepSNlsBxnkLmqM1 +cj/azKBaT04IOMLaN8xfSqitJYSraWMVNgGJM5vfcVaivZnNh0lZBv+qu6YkdM88 +4/avCJ8IutT+FcMM+GbGazOm5ALWqUyhrnbLGc4CQMPfe7Il6NxwcrOxT8w= +-----END RSA PRIVATE KEY-----`) + +var GoproxyCa, goproxyCaErr = tls.X509KeyPair(CA_CERT, CA_KEY) diff --git a/vendor/github.com/elazarl/goproxy/certs/openssl-gen.sh b/vendor/github.com/elazarl/goproxy/certs/openssl-gen.sh new file mode 100755 index 00000000..fdaab6a8 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/certs/openssl-gen.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -ex +# generate CA's key +openssl genrsa -aes256 -passout pass:1 -out ca.key.pem 4096 +openssl rsa -passin pass:1 -in ca.key.pem -out ca.key.pem.tmp +mv ca.key.pem.tmp ca.key.pem + +openssl req -config openssl.cnf -key ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out ca.pem diff --git a/vendor/github.com/elazarl/goproxy/certs/openssl.cnf b/vendor/github.com/elazarl/goproxy/certs/openssl.cnf new file mode 100644 index 00000000..2fbb5e5f --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/certs/openssl.cnf @@ -0,0 +1,39 @@ +[ ca ] +default_ca = CA_default +[ CA_default ] +default_md = sha256 +[ v3_ca ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer +basicConstraints = critical,CA:true +[ req ] +distinguished_name = req_distinguished_name +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = IL +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Center + +localityName = Locality Name (eg, city) +localityName_default = Lod + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = GoProxy + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = GoProxy + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_default = goproxy.github.io +commonName_max = 64 + +emailAddress = Email Address +emailAddress_default = elazarl@gmail.com +emailAddress_max = 64 diff --git a/vendor/github.com/elazarl/goproxy/chunked.go b/vendor/github.com/elazarl/goproxy/chunked.go new file mode 100644 index 00000000..83654f65 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/chunked.go @@ -0,0 +1,59 @@ +// Taken from $GOROOT/src/pkg/net/http/chunked +// needed to write https responses to client. +package goproxy + +import ( + "io" + "strconv" +) + +// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP +// "chunked" format before writing them to w. Closing the returned chunkedWriter +// sends the final 0-length chunk that marks the end of the stream. +// +// newChunkedWriter is not needed by normal applications. The http +// package adds chunking automatically if handlers don't set a +// Content-Length header. Using newChunkedWriter inside a handler +// would result in double chunking or chunking with a Content-Length +// length, both of which are wrong. +func newChunkedWriter(w io.Writer) io.WriteCloser { + return &chunkedWriter{w} +} + +// Writing to chunkedWriter translates to writing in HTTP chunked Transfer +// Encoding wire format to the underlying Wire chunkedWriter. +type chunkedWriter struct { + Wire io.Writer +} + +// Write the contents of data as one chunk to Wire. +// NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has +// a bug since it does not check for success of io.WriteString +func (cw *chunkedWriter) Write(data []byte) (n int, err error) { + + // Don't send 0-length data. It looks like EOF for chunked encoding. + if len(data) == 0 { + return 0, nil + } + + head := strconv.FormatInt(int64(len(data)), 16) + "\r\n" + + if _, err = io.WriteString(cw.Wire, head); err != nil { + return 0, err + } + if n, err = cw.Wire.Write(data); err != nil { + return + } + if n != len(data) { + err = io.ErrShortWrite + return + } + _, err = io.WriteString(cw.Wire, "\r\n") + + return +} + +func (cw *chunkedWriter) Close() error { + _, err := io.WriteString(cw.Wire, "0\r\n") + return err +} diff --git a/vendor/github.com/elazarl/goproxy/counterecryptor.go b/vendor/github.com/elazarl/goproxy/counterecryptor.go new file mode 100644 index 00000000..494e7a4f --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/counterecryptor.go @@ -0,0 +1,68 @@ +package goproxy + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "errors" +) + +type CounterEncryptorRand struct { + cipher cipher.Block + counter []byte + rand []byte + ix int +} + +func NewCounterEncryptorRandFromKey(key interface{}, seed []byte) (r CounterEncryptorRand, err error) { + var keyBytes []byte + switch key := key.(type) { + case *rsa.PrivateKey: + keyBytes = x509.MarshalPKCS1PrivateKey(key) + default: + err = errors.New("only RSA keys supported") + return + } + h := sha256.New() + if r.cipher, err = aes.NewCipher(h.Sum(keyBytes)[:aes.BlockSize]); err != nil { + return + } + r.counter = make([]byte, r.cipher.BlockSize()) + if seed != nil { + copy(r.counter, h.Sum(seed)[:r.cipher.BlockSize()]) + } + r.rand = make([]byte, r.cipher.BlockSize()) + r.ix = len(r.rand) + return +} + +func (c *CounterEncryptorRand) Seed(b []byte) { + if len(b) != len(c.counter) { + panic("SetCounter: wrong counter size") + } + copy(c.counter, b) +} + +func (c *CounterEncryptorRand) refill() { + c.cipher.Encrypt(c.rand, c.counter) + for i := 0; i < len(c.counter); i++ { + if c.counter[i]++; c.counter[i] != 0 { + break + } + } + c.ix = 0 +} + +func (c *CounterEncryptorRand) Read(b []byte) (n int, err error) { + if c.ix == len(c.rand) { + c.refill() + } + if n = len(c.rand) - c.ix; n > len(b) { + n = len(b) + } + copy(b, c.rand[c.ix:c.ix+n]) + c.ix += n + return +} diff --git a/vendor/github.com/elazarl/goproxy/counterecryptor_test.go b/vendor/github.com/elazarl/goproxy/counterecryptor_test.go new file mode 100644 index 00000000..12b31e16 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/counterecryptor_test.go @@ -0,0 +1,99 @@ +package goproxy_test + +import ( + "bytes" + "crypto/rsa" + "encoding/binary" + "github.com/elazarl/goproxy" + "io" + "math" + "math/rand" + "testing" +) + +type RandSeedReader struct { + r rand.Rand +} + +func (r *RandSeedReader) Read(b []byte) (n int, err error) { + for i := range b { + b[i] = byte(r.r.Int() & 0xFF) + } + return len(b), nil +} + +func TestCounterEncDifferentConsecutive(t *testing.T) { + k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128) + fatalOnErr(err, "rsa.GenerateKey", t) + c, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog")) + fatalOnErr(err, "NewCounterEncryptorRandFromKey", t) + for i := 0; i < 100*1000; i++ { + var a, b int64 + binary.Read(&c, binary.BigEndian, &a) + binary.Read(&c, binary.BigEndian, &b) + if a == b { + t.Fatal("two consecutive equal int64", a, b) + } + } +} + +func TestCounterEncIdenticalStreams(t *testing.T) { + k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128) + fatalOnErr(err, "rsa.GenerateKey", t) + c1, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog")) + fatalOnErr(err, "NewCounterEncryptorRandFromKey", t) + c2, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog")) + fatalOnErr(err, "NewCounterEncryptorRandFromKey", t) + nout := 1000 + out1, out2 := make([]byte, nout), make([]byte, nout) + io.ReadFull(&c1, out1) + tmp := out2[:] + rand.Seed(0xFF43109) + for len(tmp) > 0 { + n := 1 + rand.Intn(256) + if n > len(tmp) { + n = len(tmp) + } + n, err := c2.Read(tmp[:n]) + fatalOnErr(err, "CounterEncryptorRand.Read", t) + tmp = tmp[n:] + } + if !bytes.Equal(out1, out2) { + t.Error("identical CSPRNG does not produce the same output") + } +} + +func stddev(data []int) float64 { + var sum, sum_sqr float64 = 0, 0 + for _, h := range data { + sum += float64(h) + sum_sqr += float64(h) * float64(h) + } + n := float64(len(data)) + variance := (sum_sqr - ((sum * sum) / n)) / (n - 1) + return math.Sqrt(variance) +} + +func TestCounterEncStreamHistogram(t *testing.T) { + k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128) + fatalOnErr(err, "rsa.GenerateKey", t) + c, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog")) + fatalOnErr(err, "NewCounterEncryptorRandFromKey", t) + nout := 100 * 1000 + out := make([]byte, nout) + io.ReadFull(&c, out) + refhist := make([]int, 256) + for i := 0; i < nout; i++ { + refhist[rand.Intn(256)]++ + } + hist := make([]int, 256) + for _, b := range out { + hist[int(b)]++ + } + refstddev, stddev := stddev(refhist), stddev(hist) + // due to lack of time, I guestimate + t.Logf("ref:%v - act:%v = %v", refstddev, stddev, math.Abs(refstddev-stddev)) + if math.Abs(refstddev-stddev) >= 1 { + t.Errorf("stddev of ref histogram different than regular PRNG: %v %v", refstddev, stddev) + } +} diff --git a/vendor/github.com/elazarl/goproxy/ctx.go b/vendor/github.com/elazarl/goproxy/ctx.go new file mode 100644 index 00000000..70b4cf0f --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ctx.go @@ -0,0 +1,87 @@ +package goproxy + +import ( + "net/http" + "regexp" +) + +// ProxyCtx is the Proxy context, contains useful information about every request. It is passed to +// every user function. Also used as a logger. +type ProxyCtx struct { + // Will contain the client request from the proxy + Req *http.Request + // Will contain the remote server's response (if available. nil if the request wasn't send yet) + Resp *http.Response + RoundTripper RoundTripper + // will contain the recent error that occurred while trying to send receive or parse traffic + Error error + // A handle for the user to keep data in the context, from the call of ReqHandler to the + // call of RespHandler + UserData interface{} + // Will connect a request to a response + Session int64 + proxy *ProxyHttpServer +} + +type RoundTripper interface { + RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error) +} + +type RoundTripperFunc func(req *http.Request, ctx *ProxyCtx) (*http.Response, error) + +func (f RoundTripperFunc) RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error) { + return f(req, ctx) +} + +func (ctx *ProxyCtx) RoundTrip(req *http.Request) (*http.Response, error) { + if ctx.RoundTripper != nil { + return ctx.RoundTripper.RoundTrip(req, ctx) + } + return ctx.proxy.Tr.RoundTrip(req) +} + +func (ctx *ProxyCtx) printf(msg string, argv ...interface{}) { + ctx.proxy.Logger.Printf("[%03d] "+msg+"\n", append([]interface{}{ctx.Session & 0xFF}, argv...)...) +} + +// Logf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter +// This message will be printed only if the Verbose field of the ProxyHttpServer is set to true +// +// proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){ +// nr := atomic.AddInt32(&counter,1) +// ctx.Printf("So far %d requests",nr) +// return r, nil +// }) +func (ctx *ProxyCtx) Logf(msg string, argv ...interface{}) { + if ctx.proxy.Verbose { + ctx.printf("INFO: "+msg, argv...) + } +} + +// Warnf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter +// This message will always be printed. +// +// proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){ +// f,err := os.OpenFile(cachedContent) +// if err != nil { +// ctx.Warnf("error open file %v: %v",cachedContent,err) +// return r, nil +// } +// return r, nil +// }) +func (ctx *ProxyCtx) Warnf(msg string, argv ...interface{}) { + ctx.printf("WARN: "+msg, argv...) +} + +var charsetFinder = regexp.MustCompile("charset=([^ ;]*)") + +// Will try to infer the character set of the request from the headers. +// Returns the empty string if we don't know which character set it used. +// Currently it will look for charset= in the Content-Type header of the request. +func (ctx *ProxyCtx) Charset() string { + charsets := charsetFinder.FindStringSubmatch(ctx.Resp.Header.Get("Content-Type")) + if charsets == nil { + return "" + } + return charsets[1] +} diff --git a/vendor/github.com/elazarl/goproxy/dispatcher.go b/vendor/github.com/elazarl/goproxy/dispatcher.go new file mode 100644 index 00000000..4e7c9cb9 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/dispatcher.go @@ -0,0 +1,325 @@ +package goproxy + +import ( + "bytes" + "io/ioutil" + "net" + "net/http" + "regexp" + "strings" +) + +// ReqCondition.HandleReq will decide whether or not to use the ReqHandler on an HTTP request +// before sending it to the remote server +type ReqCondition interface { + RespCondition + HandleReq(req *http.Request, ctx *ProxyCtx) bool +} + +// RespCondition.HandleReq will decide whether or not to use the RespHandler on an HTTP response +// before sending it to the proxy client. Note that resp might be nil, in case there was an +// error sending the request. +type RespCondition interface { + HandleResp(resp *http.Response, ctx *ProxyCtx) bool +} + +// ReqConditionFunc.HandleReq(req,ctx) <=> ReqConditionFunc(req,ctx) +type ReqConditionFunc func(req *http.Request, ctx *ProxyCtx) bool + +// RespConditionFunc.HandleResp(resp,ctx) <=> RespConditionFunc(resp,ctx) +type RespConditionFunc func(resp *http.Response, ctx *ProxyCtx) bool + +func (c ReqConditionFunc) HandleReq(req *http.Request, ctx *ProxyCtx) bool { + return c(req, ctx) +} + +// ReqConditionFunc cannot test responses. It only satisfies RespCondition interface so that +// to be usable as RespCondition. +func (c ReqConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool { + return c(ctx.Req, ctx) +} + +func (c RespConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool { + return c(resp, ctx) +} + +// UrlHasPrefix returns a ReqCondition checking wether the destination URL the proxy client has requested +// has the given prefix, with or without the host. +// For example UrlHasPrefix("host/x") will match requests of the form 'GET host/x', and will match +// requests to url 'http://host/x' +func UrlHasPrefix(prefix string) ReqConditionFunc { + return func(req *http.Request, ctx *ProxyCtx) bool { + return strings.HasPrefix(req.URL.Path, prefix) || + strings.HasPrefix(req.URL.Host+req.URL.Path, prefix) || + strings.HasPrefix(req.URL.Scheme+req.URL.Host+req.URL.Path, prefix) + } +} + +// UrlIs returns a ReqCondition, testing whether or not the request URL is one of the given strings +// with or without the host prefix. +// UrlIs("google.com/","foo") will match requests 'GET /' to 'google.com', requests `'GET google.com/' to +// any host, and requests of the form 'GET foo'. +func UrlIs(urls ...string) ReqConditionFunc { + urlSet := make(map[string]bool) + for _, u := range urls { + urlSet[u] = true + } + return func(req *http.Request, ctx *ProxyCtx) bool { + _, pathOk := urlSet[req.URL.Path] + _, hostAndOk := urlSet[req.URL.Host+req.URL.Path] + return pathOk || hostAndOk + } +} + +// ReqHostMatches returns a ReqCondition, testing whether the host to which the request was directed to matches +// any of the given regular expressions. +func ReqHostMatches(regexps ...*regexp.Regexp) ReqConditionFunc { + return func(req *http.Request, ctx *ProxyCtx) bool { + for _, re := range regexps { + if re.MatchString(req.Host) { + return true + } + } + return false + } +} + +// ReqHostIs returns a ReqCondition, testing whether the host to which the request is directed to equal +// to one of the given strings +func ReqHostIs(hosts ...string) ReqConditionFunc { + hostSet := make(map[string]bool) + for _, h := range hosts { + hostSet[h] = true + } + return func(req *http.Request, ctx *ProxyCtx) bool { + _, ok := hostSet[req.URL.Host] + return ok + } +} + +var localHostIpv4 = regexp.MustCompile(`127\.0\.0\.\d+`) + +// IsLocalHost checks whether the destination host is explicitly local host +// (buggy, there can be IPv6 addresses it doesn't catch) +var IsLocalHost ReqConditionFunc = func(req *http.Request, ctx *ProxyCtx) bool { + return req.URL.Host == "::1" || + req.URL.Host == "0:0:0:0:0:0:0:1" || + localHostIpv4.MatchString(req.URL.Host) || + req.URL.Host == "localhost" +} + +// UrlMatches returns a ReqCondition testing whether the destination URL +// of the request matches the given regexp, with or without prefix +func UrlMatches(re *regexp.Regexp) ReqConditionFunc { + return func(req *http.Request, ctx *ProxyCtx) bool { + return re.MatchString(req.URL.Path) || + re.MatchString(req.URL.Host+req.URL.Path) + } +} + +// DstHostIs returns a ReqCondition testing wether the host in the request url is the given string +func DstHostIs(host string) ReqConditionFunc { + return func(req *http.Request, ctx *ProxyCtx) bool { + return req.URL.Host == host + } +} + +// SrcIpIs returns a ReqCondition testing whether the source IP of the request is one of the given strings +func SrcIpIs(ips ...string) ReqCondition { + return ReqConditionFunc(func(req *http.Request, ctx *ProxyCtx) bool { + for _, ip := range ips { + if strings.HasPrefix(req.RemoteAddr, ip+":") { + return true + } + } + return false + }) +} + +// Not returns a ReqCondition negating the given ReqCondition +func Not(r ReqCondition) ReqConditionFunc { + return func(req *http.Request, ctx *ProxyCtx) bool { + return !r.HandleReq(req, ctx) + } +} + +// ContentTypeIs returns a RespCondition testing whether the HTTP response has Content-Type header equal +// to one of the given strings. +func ContentTypeIs(typ string, types ...string) RespCondition { + types = append(types, typ) + return RespConditionFunc(func(resp *http.Response, ctx *ProxyCtx) bool { + if resp == nil { + return false + } + contentType := resp.Header.Get("Content-Type") + for _, typ := range types { + if contentType == typ || strings.HasPrefix(contentType, typ+";") { + return true + } + } + return false + }) +} + +// ProxyHttpServer.OnRequest Will return a temporary ReqProxyConds struct, aggregating the given condtions. +// You will use the ReqProxyConds struct to register a ReqHandler, that would filter +// the request, only if all the given ReqCondition matched. +// Typical usage: +// proxy.OnRequest(UrlIs("example.com/foo"),UrlMatches(regexp.MustParse(`.*\.exampl.\com\./.*`)).Do(...) +func (proxy *ProxyHttpServer) OnRequest(conds ...ReqCondition) *ReqProxyConds { + return &ReqProxyConds{proxy, conds} +} + +// ReqProxyConds aggregate ReqConditions for a ProxyHttpServer. Upon calling Do, it will register a ReqHandler that would +// handle the request if all conditions on the HTTP request are met. +type ReqProxyConds struct { + proxy *ProxyHttpServer + reqConds []ReqCondition +} + +// DoFunc is equivalent to proxy.OnRequest().Do(FuncReqHandler(f)) +func (pcond *ReqProxyConds) DoFunc(f func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)) { + pcond.Do(FuncReqHandler(f)) +} + +// ReqProxyConds.Do will register the ReqHandler on the proxy, +// the ReqHandler will handle the HTTP request if all the conditions +// aggregated in the ReqProxyConds are met. Typical usage: +// proxy.OnRequest().Do(handler) // will call handler.Handle(req,ctx) on every request to the proxy +// proxy.OnRequest(cond1,cond2).Do(handler) +// // given request to the proxy, will test if cond1.HandleReq(req,ctx) && cond2.HandleReq(req,ctx) are true +// // if they are, will call handler.Handle(req,ctx) +func (pcond *ReqProxyConds) Do(h ReqHandler) { + pcond.proxy.reqHandlers = append(pcond.proxy.reqHandlers, + FuncReqHandler(func(r *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) { + for _, cond := range pcond.reqConds { + if !cond.HandleReq(r, ctx) { + return r, nil + } + } + return h.Handle(r, ctx) + })) +} + +// HandleConnect is used when proxy receives an HTTP CONNECT request, +// it'll then use the HttpsHandler to determine what should it +// do with this request. The handler returns a ConnectAction struct, the Action field in the ConnectAction +// struct returned will determine what to do with this request. ConnectAccept will simply accept the request +// forwarding all bytes from the client to the remote host, ConnectReject will close the connection with the +// client, and ConnectMitm, will assume the underlying connection is an HTTPS connection, and will use Man +// in the Middle attack to eavesdrop the connection. All regular handler will be active on this eavesdropped +// connection. +// The ConnectAction struct contains possible tlsConfig that will be used for eavesdropping. If nil, the proxy +// will use the default tls configuration. +// proxy.OnRequest().HandleConnect(goproxy.AlwaysReject) // rejects all CONNECT requests +func (pcond *ReqProxyConds) HandleConnect(h HttpsHandler) { + pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers, + FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) { + for _, cond := range pcond.reqConds { + if !cond.HandleReq(ctx.Req, ctx) { + return nil, "" + } + } + return h.HandleConnect(host, ctx) + })) +} + +// HandleConnectFunc is equivalent to HandleConnect, +// for example, accepting CONNECT request if they contain a password in header +// io.WriteString(h,password) +// passHash := h.Sum(nil) +// proxy.OnRequest().HandleConnectFunc(func(host string, ctx *ProxyCtx) (*ConnectAction, string) { +// c := sha1.New() +// io.WriteString(c,ctx.Req.Header.Get("X-GoProxy-Auth")) +// if c.Sum(nil) == passHash { +// return OkConnect, host +// } +// return RejectConnect, host +// }) +func (pcond *ReqProxyConds) HandleConnectFunc(f func(host string, ctx *ProxyCtx) (*ConnectAction, string)) { + pcond.HandleConnect(FuncHttpsHandler(f)) +} + +func (pcond *ReqProxyConds) HijackConnect(f func(req *http.Request, client net.Conn, ctx *ProxyCtx)) { + pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers, + FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) { + for _, cond := range pcond.reqConds { + if !cond.HandleReq(ctx.Req, ctx) { + return nil, "" + } + } + return &ConnectAction{Action: ConnectHijack, Hijack: f}, host + })) +} + +// ProxyConds is used to aggregate RespConditions for a ProxyHttpServer. +// Upon calling ProxyConds.Do, it will register a RespHandler that would +// handle the HTTP response from remote server if all conditions on the HTTP response are met. +type ProxyConds struct { + proxy *ProxyHttpServer + reqConds []ReqCondition + respCond []RespCondition +} + +// ProxyConds.DoFunc is equivalent to proxy.OnResponse().Do(FuncRespHandler(f)) +func (pcond *ProxyConds) DoFunc(f func(resp *http.Response, ctx *ProxyCtx) *http.Response) { + pcond.Do(FuncRespHandler(f)) +} + +// ProxyConds.Do will register the RespHandler on the proxy, h.Handle(resp,ctx) will be called on every +// request that matches the conditions aggregated in pcond. +func (pcond *ProxyConds) Do(h RespHandler) { + pcond.proxy.respHandlers = append(pcond.proxy.respHandlers, + FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response { + for _, cond := range pcond.reqConds { + if !cond.HandleReq(ctx.Req, ctx) { + return resp + } + } + for _, cond := range pcond.respCond { + if !cond.HandleResp(resp, ctx) { + return resp + } + } + return h.Handle(resp, ctx) + })) +} + +// OnResponse is used when adding a response-filter to the HTTP proxy, usual pattern is +// proxy.OnResponse(cond1,cond2).Do(handler) // handler.Handle(resp,ctx) will be used +// // if cond1.HandleResp(resp) && cond2.HandleResp(resp) +func (proxy *ProxyHttpServer) OnResponse(conds ...RespCondition) *ProxyConds { + return &ProxyConds{proxy, make([]ReqCondition, 0), conds} +} + +// AlwaysMitm is a HttpsHandler that always eavesdrop https connections, for example to +// eavesdrop all https connections to www.google.com, we can use +// proxy.OnRequest(goproxy.ReqHostIs("www.google.com")).HandleConnect(goproxy.AlwaysMitm) +var AlwaysMitm FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) { + return MitmConnect, host +} + +// AlwaysReject is a HttpsHandler that drops any CONNECT request, for example, this code will disallow +// connections to hosts on any other port than 443 +// proxy.OnRequest(goproxy.Not(goproxy.ReqHostMatches(regexp.MustCompile(":443$"))). +// HandleConnect(goproxy.AlwaysReject) +var AlwaysReject FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) { + return RejectConnect, host +} + +// HandleBytes will return a RespHandler that read the entire body of the request +// to a byte array in memory, would run the user supplied f function on the byte arra, +// and will replace the body of the original response with the resulting byte array. +func HandleBytes(f func(b []byte, ctx *ProxyCtx) []byte) RespHandler { + return FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + ctx.Warnf("Cannot read response %s", err) + return resp + } + resp.Body.Close() + + resp.Body = ioutil.NopCloser(bytes.NewBuffer(f(b, ctx))) + return resp + }) +} diff --git a/vendor/github.com/elazarl/goproxy/doc.go b/vendor/github.com/elazarl/goproxy/doc.go new file mode 100644 index 00000000..50aaa71f --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/doc.go @@ -0,0 +1,100 @@ +/* +Package goproxy provides a customizable HTTP proxy, +supporting hijacking HTTPS connection. + +The intent of the proxy, is to be usable with reasonable amount of traffic +yet, customizable and programable. + +The proxy itself is simply an `net/http` handler. + +Typical usage is + + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest(..conditions..).Do(..requesthandler..) + proxy.OnRequest(..conditions..).DoFunc(..requesthandlerFunction..) + proxy.OnResponse(..conditions..).Do(..responesHandler..) + proxy.OnResponse(..conditions..).DoFunc(..responesHandlerFunction..) + http.ListenAndServe(":8080", proxy) + +Adding a header to each request + + proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){ + r.Header.Set("X-GoProxy","1") + return r, nil + }) + +Note that the function is called before the proxy sends the request to the server + +For printing the content type of all incoming responses + + proxy.OnResponse().DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx)*http.Response{ + println(ctx.Req.Host,"->",r.Header.Get("Content-Type")) + return r + }) + +note that we used the ProxyCtx context variable here. It contains the request +and the response (Req and Resp, Resp is nil if unavailable) of this specific client +interaction with the proxy. + +To print the content type of all responses from a certain url, we'll add a +ReqCondition to the OnResponse function: + + proxy.OnResponse(goproxy.UrlIs("golang.org/pkg")).DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx)*http.Response{ + println(ctx.Req.Host,"->",r.Header.Get("Content-Type")) + return r + }) + +We can write the condition ourselves, conditions can be set on request and on response + + var random = ReqConditionFunc(func(r *http.Request) bool { + return rand.Intn(1) == 0 + }) + var hasGoProxyHeader = RespConditionFunc(func(resp *http.Response,req *http.Request)bool { + return resp.Header.Get("X-GoProxy") != "" + }) + +Caution! If you give a RespCondition to the OnRequest function, you'll get a run time panic! It doesn't +make sense to read the response, if you still haven't got it! + +Finally, we have convenience function to throw a quick response + + proxy.OnResponse(hasGoProxyHeader).DoFunc(func(r*http.Response,ctx *goproxy.ProxyCtx)*http.Response { + r.Body.Close() + return goproxy.ForbiddenTextResponse(ctx.Req,"Can't see response with X-GoProxy header!") + }) + +we close the body of the original repsonse, and return a new 403 response with a short message. + +Example use cases: + +1. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-avgsize + +To measure the average size of an Html served in your site. One can ask +all the QA team to access the website by a proxy, and the proxy will +measure the average size of all text/html responses from your host. + +2. [not yet implemented] + +All requests to your web servers should be directed through the proxy, +when the proxy will detect html pieces sent as a response to AJAX +request, it'll send a warning email. + +3. https://github.com/elazarl/goproxy/blob/master/examples/goproxy-httpdump/ + +Generate a real traffic to your website by real users using through +proxy. Record the traffic, and try it again for more real load testing. + +4. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-no-reddit-at-worktime + +Will allow browsing to reddit.com between 8:00am and 17:00pm + +5. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-jquery-version + +Will warn if multiple versions of jquery are used in the same domain. + +6. https://github.com/elazarl/goproxy/blob/master/examples/goproxy-upside-down-ternet/ + +Modifies image files in an HTTP response via goproxy's image extension found in ext/. + +*/ +package goproxy diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-basic/README.md b/vendor/github.com/elazarl/goproxy/examples/goproxy-basic/README.md new file mode 100644 index 00000000..8778f2a7 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-basic/README.md @@ -0,0 +1,29 @@ +# Simple HTTP Proxy + +`goproxy-basic` starts an HTTP proxy on :8080. It only handles explicit CONNECT +requests. + +Start it in one shell: + +```sh +goproxy-basic -v +``` + +Fetch goproxy homepage in another: + +```sh +http_proxy=http://127.0.0.1:8080 wget -O - \ + http://ripper234.com/p/introducing-goproxy-light-http-proxy/ +``` + +The homepage HTML content should be displayed in the console. The proxy should +have logged the request being processed: + +```sh +2015/04/09 18:19:17 [001] INFO: Got request /p/introducing-goproxy-light-http-proxy/ ripper234.com GET http://ripper234.com/p/introducing-goproxy-light-http-proxy/ +2015/04/09 18:19:17 [001] INFO: Sending request GET http://ripper234.com/p/introducing-goproxy-light-http-proxy/ +2015/04/09 18:19:18 [001] INFO: Received response 200 OK +2015/04/09 18:19:18 [001] INFO: Copying response to client 200 OK [200] +2015/04/09 18:19:18 [001] INFO: Copied 44333 bytes to client error= +``` + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-basic/main.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-basic/main.go new file mode 100644 index 00000000..22dc4a90 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-basic/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/elazarl/goproxy" + "log" + "flag" + "net/http" +) + +func main() { + verbose := flag.Bool("v", false, "should every proxy request be logged to stdout") + addr := flag.String("addr", ":8080", "proxy listen address") + flag.Parse() + proxy := goproxy.NewProxyHttpServer() + proxy.Verbose = *verbose + log.Fatal(http.ListenAndServe(*addr, proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-customca/cert.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-customca/cert.go new file mode 100644 index 00000000..f4fb578f --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-customca/cert.go @@ -0,0 +1,75 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + + "github.com/elazarl/goproxy" +) + +var caCert = []byte(`-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIJAKe/ZGdfcHdPMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxGTAXBgNVBAMMEGRlbW8gZm9yIGdvcHJveHkwHhcNMTYw +OTI3MTQzNzQ3WhcNMTkwOTI3MTQzNzQ3WjBgMQswCQYDVQQGEwJBVTETMBEGA1UE +CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk +MRkwFwYDVQQDDBBkZW1vIGZvciBnb3Byb3h5MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA2+W48YZoch72zj0a+ZlyFVY2q2MWmqsEY9f/u53fAeTxvPE6 +1/DnqsydnA3FnGvxw9Dz0oZO6xG+PZvp+lhN07NZbuXK1nie8IpxCa342axpu4C0 +69lZwxikpGyJO4IL5ywp/qfb5a2DxPTAyQOQ8ROAaydoEmktRp25yicnQ2yeZW// +1SIQxt7gRxQIGmuOQ/Gqr/XN/z2cZdbGJVRUvQXk7N6NhQiCX1zlmp1hzUW9jwC+ +JEKKF1XVpQbc94Bo5supxhkKJ70CREPy8TH9mAUcQUZQRohnPvvt/lKneYAGhjHK +vhpajwlbMMSocVXFvY7o/IqIE/+ZUeQTs1SUwQIDAQABo1AwTjAdBgNVHQ4EFgQU +GnlWcIbfsWJW7GId+6xZIK8YlFEwHwYDVR0jBBgwFoAUGnlWcIbfsWJW7GId+6xZ +IK8YlFEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAoFUjSD15rKlY +xudzyVlr6n0fRNhITkiZMX3JlFOvtHNYif8RfK4TH/oHNBTmle69AgixjMgy8GGd +H90prytGQ5zCs1tKcCFsN5gRSgdAkc2PpRFOK6u8HwOITV5lV7sjucsddXJcOJbQ +4fyVe47V9TTxI+A7lRnUP2HYTR1Bd0R/IgRAH57d1ZHs7omHIuQ+Ea8ph2ppXMnP +DXVOlZ9zfczSnPnQoomqULOU9Fq2ycyi8Y/ROtAHP6O7wCFbYHXhxojdaHSdhkcd +troTflFMD2/4O6MtBKbHxSmEG6H0FBYz5xUZhZq7WUH24V3xYsfge29/lOCd5/Xf +A+j0RJc/lQ== +-----END CERTIFICATE-----`) + +var caKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2+W48YZoch72zj0a+ZlyFVY2q2MWmqsEY9f/u53fAeTxvPE6 +1/DnqsydnA3FnGvxw9Dz0oZO6xG+PZvp+lhN07NZbuXK1nie8IpxCa342axpu4C0 +69lZwxikpGyJO4IL5ywp/qfb5a2DxPTAyQOQ8ROAaydoEmktRp25yicnQ2yeZW// +1SIQxt7gRxQIGmuOQ/Gqr/XN/z2cZdbGJVRUvQXk7N6NhQiCX1zlmp1hzUW9jwC+ +JEKKF1XVpQbc94Bo5supxhkKJ70CREPy8TH9mAUcQUZQRohnPvvt/lKneYAGhjHK +vhpajwlbMMSocVXFvY7o/IqIE/+ZUeQTs1SUwQIDAQABAoIBAHK94ww8W0G5QIWL +Qwkc9XeGvg4eLUxVknva2Ll4fkZJxY4WveKx9OCd1lv4n7WoacYIwUGIDaQBZShW +s/eKnkmqGy+PvpC87gqL4sHvQpuqqJ1LYpxylLEFqduWOuGPUVC2Lc+QnWCycsCS +CgqZzsbMq0S+kkKRGSvw32JJneZCzqLgLNssQNVk+Gm6SI3s4jJsGPesjhnvoPaa +xZK14uFpltaA05GSTDaQeZJFEdnnb3f/eNPc2xMEfi0S2ZlJ6Q92WJEOepAetDlR +cRFi004bNyTb4Bphg8s4+9Cti5is199aFkGCRDWxeqEnc6aMY3Ezu9Qg3uttLVUd +uy830GUCgYEA7qS0X+9UH1R02L3aoANyADVbFt2ZpUwQGauw9WM92pH52xeHAw1S +ohus6FI3OC8xQq2CN525tGLUbFDZnNZ3YQHqFsfgevfnTs1//gbKXomitev0oFKh +VT+WYS4lkgYtPlXzhdGuk32q99T/wIocAguvCUY3PiA7yBz93ReyausCgYEA6+P8 +bugMqT8qjoiz1q/YCfxsw9bAGWjlVqme2xmp256AKtxvCf1BPsToAaJU3nFi3vkw +ICLxUWAYoMBODJ3YnbOsIZOavdXZwYHv54JqwqFealC3DG0Du6fZYZdiY8pK+E6m +3fiYzP1WoVK5tU4bH8ibuIQvpcI8j7Gy0cV6/AMCgYBHl7fZNAZro72uLD7DVGVF +9LvP/0kR0uDdoqli5JPw12w6szM40i1hHqZfyBJy042WsFDpeHL2z9Nkb1jpeVm1 +C4r7rJkGqwqElJf6UHUzqVzb8N6hnkhyN7JYkyyIQzwdgFGfaslRzBiXYxoa3BQM +9Q5c3OjDxY3JuhDa3DoVYwKBgDNqrWJLSD832oHZAEIicBe1IswJKjQfriWWsV6W +mHSbdtpg0/88aZVR/DQm+xLFakSp0jifBTS0momngRu06Dtvp2xmLQuF6oIIXY97 +2ON1owvPbibSOEcWDgb8pWCU/oRjOHIXts6vxctCKeKAFN93raGphm0+Ck9T72NU +BTubAoGBAMEhI/Wy9wAETuXwN84AhmPdQsyCyp37YKt2ZKaqu37x9v2iL8JTbPEz +pdBzkA2Gc0Wdb6ekIzRrTsJQl+c/0m9byFHsRsxXW2HnezfOFX1H4qAmF6KWP0ub +M8aIn6Rab4sNPSrvKGrU6rFpv/6M33eegzldVnV9ku6uPJI1fFTC +-----END RSA PRIVATE KEY-----`) + +func setCA(caCert, caKey []byte) error { + goproxyCa, err := tls.X509KeyPair(caCert, caKey) + if err != nil { + return err + } + if goproxyCa.Leaf, err = x509.ParseCertificate(goproxyCa.Certificate[0]); err != nil { + return err + } + goproxy.GoproxyCa = goproxyCa + goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} + goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} + goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} + goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)} + return nil +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-customca/main.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-customca/main.go new file mode 100644 index 00000000..11171ef6 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-customca/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "flag" + "log" + "net/http" + + "github.com/elazarl/goproxy" +) + +func main() { + verbose := flag.Bool("v", false, "should every proxy request be logged to stdout") + addr := flag.String("addr", ":8080", "proxy listen address") + flag.Parse() + setCA(caCert, caKey) + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) + proxy.Verbose = *verbose + log.Fatal(http.ListenAndServe(*addr, proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-eavesdropper/main.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-eavesdropper/main.go new file mode 100644 index 00000000..9d80653b --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-eavesdropper/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "bufio" + "flag" + "log" + "net" + "net/http" + "regexp" + + "github.com/elazarl/goproxy" +) + +func orPanic(err error) { + if err != nil { + panic(err) + } +} + +func main() { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*baidu.com$"))). + HandleConnect(goproxy.AlwaysReject) + proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))). + HandleConnect(goproxy.AlwaysMitm) + // enable curl -p for all hosts on port 80 + proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))). + HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) { + defer func() { + if e := recover(); e != nil { + ctx.Logf("error connecting to remote: %v", e) + client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n")) + } + client.Close() + }() + clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client)) + remote, err := net.Dial("tcp", req.URL.Host) + orPanic(err) + remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote)) + for { + req, err := http.ReadRequest(clientBuf.Reader) + orPanic(err) + orPanic(req.Write(remoteBuf)) + orPanic(remoteBuf.Flush()) + resp, err := http.ReadResponse(remoteBuf.Reader, req) + orPanic(err) + orPanic(resp.Write(clientBuf.Writer)) + orPanic(clientBuf.Flush()) + } + }) + verbose := flag.Bool("v", false, "should every proxy request be logged to stdout") + addr := flag.String("addr", ":8080", "proxy listen address") + flag.Parse() + proxy.Verbose = *verbose + log.Fatal(http.ListenAndServe(*addr, proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-httpdump/README.md b/vendor/github.com/elazarl/goproxy/examples/goproxy-httpdump/README.md new file mode 100644 index 00000000..7240d8ea --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-httpdump/README.md @@ -0,0 +1,30 @@ +# Trace HTTP Requests and Responses + +`goproxy-httpdump` starts an HTTP proxy on :8080. It handles explicit CONNECT +requests and traces them in a "db" directory created in the proxy working +directory. Each request type and headers are logged in a "log" file, while +their bodies are dumped in files prefixed with the request session identifier. + +Additionally, the example demonstrates how to: +- Log information asynchronously (see HttpLogger) +- Allow the proxy to be stopped manually while ensuring all pending requests + have been processed (in this case, logged). + +Start it in one shell: + +```sh +goproxy-httpdump +``` + +Fetch goproxy homepage in another: + +```sh +http_proxy=http://127.0.0.1:8080 wget -O - \ + http://ripper234.com/p/introducing-goproxy-light-http-proxy/ +``` + +A "db" directory should have appeared where you started the proxy, containing +two files: +- log: the request/response traces +- 1\_resp: the first response body + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-httpdump/httpdump.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-httpdump/httpdump.go new file mode 100644 index 00000000..62a9b882 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-httpdump/httpdump.go @@ -0,0 +1,285 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "io" + "log" + "net" + "net/http" + "net/http/httputil" + "os" + "os/signal" + "path" + "sync" + "time" + + "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/transport" +) + +type FileStream struct { + path string + f *os.File +} + +func NewFileStream(path string) *FileStream { + return &FileStream{path, nil} +} + +func (fs *FileStream) Write(b []byte) (nr int, err error) { + if fs.f == nil { + fs.f, err = os.Create(fs.path) + if err != nil { + return 0, err + } + } + return fs.f.Write(b) +} + +func (fs *FileStream) Close() error { + fmt.Println("Close", fs.path) + if fs.f == nil { + return errors.New("FileStream was never written into") + } + return fs.f.Close() +} + +type Meta struct { + req *http.Request + resp *http.Response + err error + t time.Time + sess int64 + bodyPath string + from string +} + +func fprintf(nr *int64, err *error, w io.Writer, pat string, a ...interface{}) { + if *err != nil { + return + } + var n int + n, *err = fmt.Fprintf(w, pat, a...) + *nr += int64(n) +} + +func write(nr *int64, err *error, w io.Writer, b []byte) { + if *err != nil { + return + } + var n int + n, *err = w.Write(b) + *nr += int64(n) +} + +func (m *Meta) WriteTo(w io.Writer) (nr int64, err error) { + if m.req != nil { + fprintf(&nr, &err, w, "Type: request\r\n") + } else if m.resp != nil { + fprintf(&nr, &err, w, "Type: response\r\n") + } + fprintf(&nr, &err, w, "ReceivedAt: %v\r\n", m.t) + fprintf(&nr, &err, w, "Session: %d\r\n", m.sess) + fprintf(&nr, &err, w, "From: %v\r\n", m.from) + if m.err != nil { + // note the empty response + fprintf(&nr, &err, w, "Error: %v\r\n\r\n\r\n\r\n", m.err) + } else if m.req != nil { + fprintf(&nr, &err, w, "\r\n") + buf, err2 := httputil.DumpRequest(m.req, false) + if err2 != nil { + return nr, err2 + } + write(&nr, &err, w, buf) + } else if m.resp != nil { + fprintf(&nr, &err, w, "\r\n") + buf, err2 := httputil.DumpResponse(m.resp, false) + if err2 != nil { + return nr, err2 + } + write(&nr, &err, w, buf) + } + return +} + +// HttpLogger is an asynchronous HTTP request/response logger. It traces +// requests and responses headers in a "log" file in logger directory and dumps +// their bodies in files prefixed with the session identifiers. +// Close it to ensure pending items are correctly logged. +type HttpLogger struct { + path string + c chan *Meta + errch chan error +} + +func NewLogger(basepath string) (*HttpLogger, error) { + f, err := os.Create(path.Join(basepath, "log")) + if err != nil { + return nil, err + } + logger := &HttpLogger{basepath, make(chan *Meta), make(chan error)} + go func() { + for m := range logger.c { + if _, err := m.WriteTo(f); err != nil { + log.Println("Can't write meta", err) + } + } + logger.errch <- f.Close() + }() + return logger, nil +} + +func (logger *HttpLogger) LogResp(resp *http.Response, ctx *goproxy.ProxyCtx) { + body := path.Join(logger.path, fmt.Sprintf("%d_resp", ctx.Session)) + from := "" + if ctx.UserData != nil { + from = ctx.UserData.(*transport.RoundTripDetails).TCPAddr.String() + } + if resp == nil { + resp = emptyResp + } else { + resp.Body = NewTeeReadCloser(resp.Body, NewFileStream(body)) + } + logger.LogMeta(&Meta{ + resp: resp, + err: ctx.Error, + t: time.Now(), + sess: ctx.Session, + from: from}) +} + +var emptyResp = &http.Response{} +var emptyReq = &http.Request{} + +func (logger *HttpLogger) LogReq(req *http.Request, ctx *goproxy.ProxyCtx) { + body := path.Join(logger.path, fmt.Sprintf("%d_req", ctx.Session)) + if req == nil { + req = emptyReq + } else { + req.Body = NewTeeReadCloser(req.Body, NewFileStream(body)) + } + logger.LogMeta(&Meta{ + req: req, + err: ctx.Error, + t: time.Now(), + sess: ctx.Session, + from: req.RemoteAddr}) +} + +func (logger *HttpLogger) LogMeta(m *Meta) { + logger.c <- m +} + +func (logger *HttpLogger) Close() error { + close(logger.c) + return <-logger.errch +} + +// TeeReadCloser extends io.TeeReader by allowing reader and writer to be +// closed. +type TeeReadCloser struct { + r io.Reader + w io.WriteCloser + c io.Closer +} + +func NewTeeReadCloser(r io.ReadCloser, w io.WriteCloser) io.ReadCloser { + return &TeeReadCloser{io.TeeReader(r, w), w, r} +} + +func (t *TeeReadCloser) Read(b []byte) (int, error) { + return t.r.Read(b) +} + +// Close attempts to close the reader and write. It returns an error if both +// failed to Close. +func (t *TeeReadCloser) Close() error { + err1 := t.c.Close() + err2 := t.w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +// stoppableListener serves stoppableConn and tracks their lifetime to notify +// when it is safe to terminate the application. +type stoppableListener struct { + net.Listener + sync.WaitGroup +} + +type stoppableConn struct { + net.Conn + wg *sync.WaitGroup +} + +func newStoppableListener(l net.Listener) *stoppableListener { + return &stoppableListener{l, sync.WaitGroup{}} +} + +func (sl *stoppableListener) Accept() (net.Conn, error) { + c, err := sl.Listener.Accept() + if err != nil { + return c, err + } + sl.Add(1) + return &stoppableConn{c, &sl.WaitGroup}, nil +} + +func (sc *stoppableConn) Close() error { + sc.wg.Done() + return sc.Conn.Close() +} + +func main() { + verbose := flag.Bool("v", false, "should every proxy request be logged to stdout") + addr := flag.String("l", ":8080", "on which address should the proxy listen") + flag.Parse() + proxy := goproxy.NewProxyHttpServer() + proxy.Verbose = *verbose + if err := os.MkdirAll("db", 0755); err != nil { + log.Fatal("Can't create dir", err) + } + logger, err := NewLogger("db") + if err != nil { + log.Fatal("can't open log file", err) + } + tr := transport.Transport{Proxy: transport.ProxyFromEnvironment} + // For every incoming request, override the RoundTripper to extract + // connection information. Store it is session context log it after + // handling the response. + proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + ctx.RoundTripper = goproxy.RoundTripperFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (resp *http.Response, err error) { + ctx.UserData, resp, err = tr.DetailedRoundTrip(req) + return + }) + logger.LogReq(req, ctx) + return req, nil + }) + proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + logger.LogResp(resp, ctx) + return resp + }) + l, err := net.Listen("tcp", *addr) + if err != nil { + log.Fatal("listen:", err) + } + sl := newStoppableListener(l) + ch := make(chan os.Signal) + signal.Notify(ch, os.Interrupt) + go func() { + <-ch + log.Println("Got SIGINT exiting") + sl.Add(1) + sl.Close() + logger.Close() + sl.Done() + }() + log.Println("Starting Proxy") + http.Serve(sl, proxy) + sl.Wait() + log.Println("All connections closed - exit") +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/README.md b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/README.md new file mode 100644 index 00000000..6efba22a --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/README.md @@ -0,0 +1,31 @@ +# Content Analysis + +`goproxy-jquery-version` starts an HTTP proxy on :8080. It checks HTML +responses, looks for scripts referencing jQuery library and emits warnings if +different versions of the library are being used for a given host. + +Start it in one shell: + +```sh +goproxy-jquery-version +``` + +Fetch goproxy homepage in another: + +```sh +http_proxy=http://127.0.0.1:8080 wget -O - \ + http://ripper234.com/p/introducing-goproxy-light-http-proxy/ +``` + +Goproxy homepage uses jQuery and a mix of plugins. First the proxy reports the +first use of jQuery it detects for the domain. Then, because the regular +expression matching the jQuery sources is imprecise, it reports a mismatch with +a plugin reference: + +```sh +2015/04/11 11:23:02 [001] WARN: ripper234.com uses //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js +2015/04/11 11:23:02 [001] WARN: In http://ripper234.com/p/introducing-goproxy-light-http-proxy/, \ + Contradicting jqueries //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js \ + http://ripper234.wpengine.netdna-cdn.com/wp-content/plugins/wp-ajax-edit-comments/js/jquery.colorbox.min.js?ver=5.0.36 +``` + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery1.html b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery1.html new file mode 100644 index 00000000..26771ce3 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery1.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery2.html b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery2.html new file mode 100644 index 00000000..7dce0361 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery2.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_homepage.html b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_homepage.html new file mode 100644 index 00000000..27dd0b38 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_homepage.html @@ -0,0 +1,233 @@ + + + + + jQuery: The Write Less, Do More, JavaScript Library + + + + + + + + + +
+
+ + +
+ +
+ + + + + +
+ +
+ +
+

jQuery is a new kind of JavaScript Library.

+

jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.

+ +
+ +
+

Grab the latest version!

+
+
+ Choose your compression level: +
+ + jquery-1.7.2.min.js + + + jquery-1.7.2.js + +
+ +

Current Release: v1.7.2

+
+
+ +
+ + + + +
+ +
+

Learn jQuery Now!

+

What does jQuery code look like? Here's the quick and dirty:

+
+
$("p.neat").addClass("ohmy").show("slow");
+ Run Code + +

Congratulations! You just ran a snippet of jQuery code. Wasn't that easy? There's lots of example code throughout the documentation on this site. Be sure to give all the code a test run to see what happens.

+
+
+ + + +
+

jQuery Resources

+ + + +
+ +
+ +
+

Books About jQuery

+ + + +
+ + + +
+ + + +
+ + + + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_test.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_test.go new file mode 100644 index 00000000..af300aaf --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/jquery_test.go @@ -0,0 +1,118 @@ +package main + +import ( + "bytes" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" +) + +func equal(u, v []string) bool { + if len(u) != len(v) { + return false + } + for i, _ := range u { + if u[i] != v[i] { + return false + } + } + return true +} + +func readFile(fname string, t *testing.T) string { + b, err := ioutil.ReadFile(fname) + if err != nil { + t.Fatal("readFile", err) + } + return string(b) +} + +func TestDefectiveScriptParser(t *testing.T) { + if l := len(findScriptSrc(` + + + + + + + `)); l != 0 { + t.Fail() + } + urls := findScriptSrc(readFile("w3schools.html", t)) + if !equal(urls, []string{"http://partner.googleadservices.com/gampad/google_service.js", + "//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"}) { + t.Error("w3schools.html", "src scripts are not recognized", urls) + } + urls = findScriptSrc(readFile("jquery_homepage.html", t)) + if !equal(urls, []string{"http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", + "http://code.jquery.com/jquery-1.4.2.min.js", + "http://static.jquery.com/files/rocker/scripts/custom.js", + "http://static.jquery.com/donate/donate.js"}) { + t.Error("jquery_homepage.html", "src scripts are not recognized", urls) + } +} + +func proxyWithLog() (*http.Client, *bytes.Buffer) { + proxy := NewJqueryVersionProxy() + proxyServer := httptest.NewServer(proxy) + buf := new(bytes.Buffer) + proxy.Logger = log.New(buf, "", 0) + proxyUrl, _ := url.Parse(proxyServer.URL) + tr := &http.Transport{Proxy: http.ProxyURL(proxyUrl)} + client := &http.Client{Transport: tr} + return client, buf +} + +func get(t *testing.T, server *httptest.Server, client *http.Client, url string) { + resp, err := client.Get(server.URL + url) + if err != nil { + t.Fatal("cannot get proxy", err) + } + ioutil.ReadAll(resp.Body) + resp.Body.Close() +} + +func TestProxyServiceTwoVersions(t *testing.T) { + var fs = httptest.NewServer(http.FileServer(http.Dir("."))) + defer fs.Close() + + client, buf := proxyWithLog() + + get(t, fs, client, "/w3schools.html") + get(t, fs, client, "/php_man.html") + if buf.String() != "" && + !strings.Contains(buf.String(), " uses jquery ") { + t.Error("shouldn't warn on a single URL", buf.String()) + } + get(t, fs, client, "/jquery1.html") + warnings := buf.String() + if !strings.Contains(warnings, "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js") || + !strings.Contains(warnings, "jquery.1.4.js") || + !strings.Contains(warnings, "Contradicting") { + t.Error("contradicting jquery versions (php_man.html, w3schools.html) does not issue warning", warnings) + } +} + +func TestProxyService(t *testing.T) { + var fs = httptest.NewServer(http.FileServer(http.Dir("."))) + defer fs.Close() + + client, buf := proxyWithLog() + + get(t, fs, client, "/jquery_homepage.html") + warnings := buf.String() + if !strings.Contains(warnings, "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js") || + !strings.Contains(warnings, "http://code.jquery.com/jquery-1.4.2.min.js") || + !strings.Contains(warnings, "Contradicting") { + t.Error("contradicting jquery versions does not issue warning") + } +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/main.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/main.go new file mode 100644 index 00000000..a92dddea --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/ext/html" + "log" + "net/http" + "regexp" +) + +var ( + // who said we can't parse HTML with regexp? + scriptMatcher = regexp.MustCompile(`(?i:]*\ssrc=["']([^"']*)["'])`) +) + +// findScripts returns all sources of HTML script tags found in input text. +func findScriptSrc(html string) []string { + srcs := make([]string, 0) + matches := scriptMatcher.FindAllStringIndex(html, -1) + for _, match := range matches { + // -1 to capture the whitespace at the end of the script tag + srcMatch := srcAttrMatcher.FindStringSubmatch(html[match[1]-1:]) + if srcMatch != nil { + srcs = append(srcs, srcMatch[1]) + } + } + return srcs +} + +// NewJQueryVersionProxy creates a proxy checking responses HTML content, looks +// for scripts referencing jQuery library and emits warnings if different +// versions of the library are being used for a given host. +func NewJqueryVersionProxy() *goproxy.ProxyHttpServer { + proxy := goproxy.NewProxyHttpServer() + m := make(map[string]string) + jqueryMatcher := regexp.MustCompile(`(?i:jquery\.)`) + proxy.OnResponse(goproxy_html.IsHtml).Do(goproxy_html.HandleString( + func(s string, ctx *goproxy.ProxyCtx) string { + for _, src := range findScriptSrc(s) { + if !jqueryMatcher.MatchString(src) { + continue + } + prev, ok := m[ctx.Req.Host] + if ok { + if prev != src { + ctx.Warnf("In %v, Contradicting jqueries %v %v", + ctx.Req.URL, prev, src) + break + } + } else { + ctx.Warnf("%s uses jquery %s", ctx.Req.Host, src) + m[ctx.Req.Host] = src + } + } + return s + })) + return proxy +} + +func main() { + proxy := NewJqueryVersionProxy() + log.Fatal(http.ListenAndServe(":8080", proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/php_man.html b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/php_man.html new file mode 100644 index 00000000..1159d762 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/php_man.html @@ -0,0 +1,323 @@ + + + + PHP: PHP Manual - Manual + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

+ + search for + + + in the + + + +

+
+
+ +
+
+ + + +
+
+ +
+ + + +   + +
+ [edit] Last updated: Fri, 23 Mar 2012 +
+
+

view this page in

+ +
+
+
+ + +
+

PHP Manual

+ + + +
+
+ +
by:
+ + Mehdi Achour + +
+ + +
+ + Friedhelm Betz + +
+ + +
+ + Antony Dovgal + +
+ + +
+ + Nuno Lopes + +
+ + +
+ + Hannes Magnusson + +
+ + +
+ + Georg Richter + +
+ + +
+ + Damien Seguy + +
+ + +
+ + Jakub Vrana + +
+ + + +
+ + + And several others + + +
+ +
+
2012-03-23
+ +
+
Edited By: + + Philip Olson + +
+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +


+
+
+ add a note add a note + User Contributed Notes + PHP Manual +
+
There are no user contributed notes for this page.

+
+
 
+
+ + + + + + + \ No newline at end of file diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/w3schools.html b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/w3schools.html new file mode 100644 index 00000000..ecf3a9df --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-jquery-version/w3schools.html @@ -0,0 +1,1610 @@ + + + + + + + + +HTML5 Tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ +
+ + W3Schools.com + +
+ + + +
+ + + + + +
+ + + + + +
+ +
+ +
+ +
+ +
+ + HOME + + HTML + + CSS + + XML + + JAVASCRIPT + + ASP + + PHP + + SQL + + MORE... + +
+ +
+ + REFERENCES | + + EXAMPLES | + + FORUM | + + ABOUT + +
+ +
+ + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ +

HTML5 Tutorial

+ +HTML5 Home
+ +HTML5 Introduction
+ +HTML5 New Elements
+ +HTML5 Video
+ +HTML5 Video/DOM
+ +HTML5 Audio
+ +HTML5 Drag and Drop
+ +HTML5 Canvas
+ +HTML5 SVG
+ +HTML5 Canvas vs. SVG
+ +HTML5 Geolocation
+ +HTML5 Web Storage
+ +HTML5 App Cache
+ +HTML5 Web Workers
+ +HTML5 SSE
+ +
+ +

HTML5 Forms

+ +HTML5 Input Types
+ +HTML5 Form Elements
+ +HTML5 Form Attributes
+ +
+ +

HTML5 Reference

+ +HTML5 Tags
+ +HTML5 Attributes
+ +HTML5 Events
+ +HTML5 Audio/Video
+ +HTML5 Canvas 2d
+ +HTML Valid DTDs
+ +
+ +

HTML5 Tags

+ +<!-->
+ +<!DOCTYPE>
+ +<a>
+ +<abbr>
+ +<acronym>
+ +<address>
+ +<applet>
+ +<area>
+ +<article>
+ +<aside>
+ +<audio>
+ +<b>
+ +<base>
+ +<basefont>
+ +<bdi>
+ +<bdo>
+ +<big>
+ +<blockquote>
+ +<body>
+ +<br>
+ +<button>
+ +<canvas>
+ +<caption>
+ +<center>
+ +<cite>
+ +<code>
+ +<col>
+ +<colgroup>
+ +<command>
+ +<datalist>
+ +<dd>
+ +<del>
+ +<details>
+ +<dfn>
+ +<dir>
+ +<div>
+ +<dl>
+ +<dt>
+ +<em>
+ +<embed>
+ +<fieldset>
+ +<figcaption>
+ +<figure>
+ +<font>
+ +<footer>
+ +<form>
+ +<frame>
+ +<frameset>
+ +<h1> - <h6>
+ +<head>
+ +<header>
+ +<hgroup>
+ +<hr>
+ +<html>
+ +<i>
+ +<iframe>
+ +<img>
+ +<input>
+ +<ins>
+ +<keygen>
+ +<kbd>
+ +<label>
+ +<legend>
+ +<li>
+ +<link>
+ +<map>
+ +<mark>
+ +<menu>
+ +<meta>
+ +<meter>
+ +<nav>
+ +<noframes>
+ +<noscript>
+ +<object>
+ +<ol>
+ +<optgroup>
+ +<option>
+ +<output>
+ +<p>
+ +<param>
+ +<pre>
+ +<progress>
+ +<q>
+ +<rp>
+ +<rt>
+ +<ruby>
+ +<s>
+ +<samp>
+ +<script>
+ +<section>
+ +<select>
+ +<small>
+ +<source>
+ +<span>
+ +<strike>
+ +<strong>
+ +<style>
+ +<sub>
+ +<summary>
+ +<sup>
+ +<table>
+ +<tbody>
+ +<td>
+ +<textarea>
+ +<tfoot>
+ +<th>
+ +<thead>
+ +<time>
+ +<title>
+ +<tr>
+ +<track>
+ +<tt>
+ +<u>
+ +<ul>
+ +<var>
+ +<video>
+ +<wbr>
+ +
+ +
+ + + +

HTML5 Tutorial

+ + + +
+ +
+ +
HTML5 is The New HTML Standard
+ +
+ + + + + +
+ + + + + + + + + + + +
+ +
+ +
+ +

HTML5

+ +
    + +
  • New Elements
  • + +
  • New Attributes
  • + +
  • Full CSS3 Support
  • + +
  • Video and Audio
  • + +
  • 2D/3D Graphics
  • + +
  • Local Storage
  • + +
  • Local SQL Database
  • + +
  • Web Applications
  • + +
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ +
    + +
  • + +
  • + +
  • + +
  • + +
  • + +
  • + +
+ +
+ +
+ +
+ +

Examples in Each Chapter

+ +

With our HTML editor, you can edit the HTML, and click on a button to view the result.

+ +
+ +

Example

+ +
+ + <!DOCTYPE HTML>
+ + <html>
+ + <body>
+ +
+ + <video width="320" height="240" controls="controls">
+ +  <source src="movie.mp4" type="video/mp4" />
+ +  <source src="movie.ogg" type="video/ogg" />
+ +  <source src="movie.webm" type="video/webm" />
+ + Your browser does not support the video tag.
+ + </video>
+ +
+ + </body>
+ + </html> + +
+ +
+ + Try it yourself »
+ +

Click on the "Try it yourself" button to see how it works

+ +

Start learning HTML5 now!

+ + + +

HTML5 References

+ +

At W3Schools you will find complete references about tags, global attributes, + +standard events, and more.

+ +

+ +HTML5 Tag Reference + +

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
WEB HOSTING
+ +Best Web Hosting + +
+ +PHP MySQL Hosting + +
+ +Best Hosting Coupons + +
+ +UK Reseller Hosting + +
+ +Cloud Hosting + +
+ +Top Web Hosting + +
+ +$3.98 Unlimited Hosting + +
+ +Premium Website Design + +
+ + + + + + + + + + + + + +
WEB BUILDING
+ + + +Download XML Editor + + + +
+ +FREE Website BUILDER + +
+ +Free Website Templates + +Free CSS Templates + +
+ +CREATE HTML Websites + +
+ + + + + + + +
W3SCHOOLS EXAMS
+ +Get Certified in:
HTML, CSS, JavaScript, XML, PHP, and ASP
+ +
+ + + + + + + +
W3SCHOOLS BOOKS
+ + + +New Books:
HTML, CSS
+ +JavaScript, and Ajax
+ +
+ + + + + +
STATISTICS
+ +Browser Statistics
+ +Browser OS
+ +Browser Display + +
+ + + + + + + + + + + + + +
SHARE THIS PAGE
+ + + +
+ + + + + + + +

+ +
+ + + + + +
+ +
+ + + +
+ +
+ +
+ +
+ +
+ + + +
+ +
+ + + + + + + + + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-no-reddit-at-worktime/README.md b/vendor/github.com/elazarl/goproxy/examples/goproxy-no-reddit-at-worktime/README.md new file mode 100644 index 00000000..23b52240 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-no-reddit-at-worktime/README.md @@ -0,0 +1,21 @@ +# Request Filtering + +`goproxy-no-reddit-at-work` starts an HTTP proxy on :8080. It denies requests +to "www.reddit.com" made between 8am to 5pm inclusive, local time. + +Start it in one shell: + +```sh +$ goproxy-no-reddit-at-work +``` + +Fetch reddit in another: + +```sh +$ http_proxy=http://127.0.0.1:8080 wget -O - http://www.reddit.com +--2015-04-11 16:59:01-- http://www.reddit.com/ +Connecting to 127.0.0.1:8080... connected. +Proxy request sent, awaiting response... 403 Forbidden +2015-04-11 16:59:01 ERROR 403: Forbidden. +``` + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-no-reddit-at-worktime/noreddit.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-no-reddit-at-worktime/noreddit.go new file mode 100644 index 00000000..b1748459 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-no-reddit-at-worktime/noreddit.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/elazarl/goproxy" + "log" + "net/http" + "time" +) + +func main() { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc( + func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + h, _, _ := time.Now().Clock() + if h >= 8 && h <= 17 { + return r, goproxy.NewResponse(r, + goproxy.ContentTypeText, http.StatusForbidden, + "Don't waste your time!") + } else { + ctx.Warnf("clock: %d, you can waste your time...", h) + } + return r, nil + }) + log.Fatalln(http.ListenAndServe(":8080", proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-sokeepalive/sokeepalive.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-sokeepalive/sokeepalive.go new file mode 100644 index 00000000..9135e575 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-sokeepalive/sokeepalive.go @@ -0,0 +1,25 @@ +package main + +import ( + "flag" + "github.com/elazarl/goproxy" + "log" + "net" + "net/http" +) + +func main() { + verbose := flag.Bool("v", false, "should every proxy request be logged to stdout") + addr := flag.String("addr", ":8080", "proxy listen address") + flag.Parse() + proxy := goproxy.NewProxyHttpServer() + proxy.Tr.Dial = func(network, addr string) (c net.Conn, err error) { + c, err = net.Dial(network, addr) + if c, ok := c.(*net.TCPConn); err == nil && ok { + c.SetKeepAlive(true) + } + return + } + proxy.Verbose = *verbose + log.Fatal(http.ListenAndServe(*addr, proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-sslstrip/sslstrip.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-sslstrip/sslstrip.go new file mode 100644 index 00000000..b7e2dc9e --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-sslstrip/sslstrip.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/elazarl/goproxy" + "log" + "flag" + "net/http" +) + +func main() { + verbose := flag.Bool("v", false, "should every proxy request be logged to stdout") + addr := flag.String("addr", ":8080", "proxy listen address") + flag.Parse() + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) + proxy.OnRequest().DoFunc(func (req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + if req.URL.Scheme == "https" { + req.URL.Scheme = "http" + } + return req, nil + }) + proxy.Verbose = *verbose + log.Fatal(http.ListenAndServe(*addr, proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-stats/README.md b/vendor/github.com/elazarl/goproxy/examples/goproxy-stats/README.md new file mode 100644 index 00000000..a51d4c8e --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-stats/README.md @@ -0,0 +1,43 @@ +# Gather Browsing Statistics + +`goproxy-stats` starts an HTTP proxy on :8080, counts the bytes received for +web resources and prints the cumulative sum per URL every 20 seconds. + +Start it in one shell: + +```sh +goproxy-stats +``` + +Fetch goproxy homepage in another: + +```sh +mkdir tmp +cd tmp +http_proxy=http://127.0.0.1:8080 wget -r -l 1 -H \ + http://ripper234.com/p/introducing-goproxy-light-http-proxy/ +``` + +Stop it after a moment. `goproxy-stats` should eventually print: +```sh +listening on :8080 +statistics +http://www.telerik.com/fiddler -> 84335 +http://msmvps.com/robots.txt -> 157 +http://eli.thegreenplace.net/robots.txt -> 294 +http://www.phdcomics.com/robots.txt -> 211 +http://resharper.blogspot.com/robots.txt -> 221 +http://idanz.blogli.co.il/robots.txt -> 271 +http://ripper234.com/p/introducing-goproxy-light-http-proxy/ -> 44407 +http://live.gnome.org/robots.txt -> 298 +http://ponetium.wordpress.com/robots.txt -> 178 +http://pilaheleg.blogli.co.il/robots.txt -> 321 +http://pilaheleg.wordpress.com/robots.txt -> 178 +http://blogli.co.il/ -> 9165 +http://nimrod-code.org/robots.txt -> 289 +http://www.joelonsoftware.com/robots.txt -> 1245 +http://top-performance.blogspot.com/robots.txt -> 227 +http://ooc-lang.org/robots.txt -> 345 +http://blogs.jetbrains.com/robots.txt -> 293 +``` + diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-stats/main.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-stats/main.go new file mode 100644 index 00000000..e4cde8d9 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-stats/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/ext/html" + "io" + "log" + . "net/http" + "time" +) + +type Count struct { + Id string + Count int64 +} +type CountReadCloser struct { + Id string + R io.ReadCloser + ch chan<- Count + nr int64 +} + +func (c *CountReadCloser) Read(b []byte) (n int, err error) { + n, err = c.R.Read(b) + c.nr += int64(n) + return +} +func (c CountReadCloser) Close() error { + c.ch <- Count{c.Id, c.nr} + return c.R.Close() +} + +func main() { + proxy := goproxy.NewProxyHttpServer() + timer := make(chan bool) + ch := make(chan Count, 10) + go func() { + for { + time.Sleep(20 * time.Second) + timer <- true + } + }() + go func() { + m := make(map[string]int64) + for { + select { + case c := <-ch: + m[c.Id] = m[c.Id] + c.Count + case <-timer: + fmt.Printf("statistics\n") + for k, v := range m { + fmt.Printf("%s -> %d\n", k, v) + } + } + } + }() + + // IsWebRelatedText filters on html/javascript/css resources + proxy.OnResponse(goproxy_html.IsWebRelatedText).DoFunc(func(resp *Response, ctx *goproxy.ProxyCtx) *Response { + resp.Body = &CountReadCloser{ctx.Req.URL.String(), resp.Body, ch, 0} + return resp + }) + fmt.Printf("listening on :8080\n") + log.Fatal(ListenAndServe(":8080", proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/README.md b/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/README.md new file mode 100644 index 00000000..7edb0989 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/README.md @@ -0,0 +1,17 @@ +# Transparent Proxy + +This transparent example in goproxy is meant to show how to transparenty proxy and hijack all http and https connections while doing a man-in-the-middle to the TLS session. It requires that goproxy sees all the packets traversing out to the internet. Linux iptables rules deal with changing the source/destination IPs to act transparently, but you do need to setup your network configuration so that goproxy is a mandatory stop on the outgoing route. Primarily you can do this by placing the proxy inline. goproxy does not have any WCCP support itself; patches welcome. + +## Why not explicit? + +Transparent proxies are more difficult to maintain and setup from a server side, but they require no configuration on the client(s) which could be in unmanaged systems or systems that don't support a proxy configuration. See the [eavesdropper example](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-eavesdropper/main.go) if you want to see an explicit proxy example. + +## Potential Issues + +Support for very old clients using HTTPS will fail. Clients need to send the SNI value in the TLS ClientHello which most modern clients do these days, but old clients will break. + +If you're routing table allows for it, an explicit http request to goproxy will cause it to fail in an endless loop since it will try to request resources from itself repeatedly. This could be solved in the goproxy code by looking up the hostnames, but it adds a delay that is much easier/faster to handle on the routing side. + +## Routing Rules + +Example routing rules are included in [proxy.sh](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-transparent/proxy.sh) but are best when setup using your distribution's configuration. diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/proxy.sh b/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/proxy.sh new file mode 100644 index 00000000..c6311143 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/proxy.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# goproxy IP +GOPROXY_SERVER="10.10.10.1" +# goproxy port +GOPROXY_PORT="3129" +GOPROXY_PORT_TLS="3128" +# DO NOT MODIFY BELOW +# Load IPTABLES modules for NAT and IP conntrack support +modprobe ip_conntrack +modprobe ip_conntrack_ftp +echo 1 > /proc/sys/net/ipv4/ip_forward +echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter + +# Clean old firewall +iptables -t nat -F +iptables -t nat -X +iptables -t mangle -F +iptables -t mangle -X + +# Write new rules +iptables -t nat -A PREROUTING -s $GOPROXY_SERVER -p tcp --dport $GOPROXY_PORT -j ACCEPT +iptables -t nat -A PREROUTING -s $GOPROXY_SERVER -p tcp --dport $GOPROXY_PORT_TLS -j ACCEPT +iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT +iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT_TLS +# The following line supports using goproxy as an explicit proxy in addition +iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT +iptables -t nat -A POSTROUTING -j MASQUERADE +iptables -t mangle -A PREROUTING -p tcp --dport $GOPROXY_PORT -j DROP +iptables -t mangle -A PREROUTING -p tcp --dport $GOPROXY_PORT_TLS -j DROP diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/transparent.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/transparent.go new file mode 100644 index 00000000..b4134e23 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-transparent/transparent.go @@ -0,0 +1,148 @@ +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "log" + "net" + "net/http" + "net/url" + "regexp" + + "github.com/elazarl/goproxy" + "github.com/inconshreveable/go-vhost" +) + +func orPanic(err error) { + if err != nil { + panic(err) + } +} + +func main() { + verbose := flag.Bool("v", true, "should every proxy request be logged to stdout") + http_addr := flag.String("httpaddr", ":3129", "proxy http listen address") + https_addr := flag.String("httpsaddr", ":3128", "proxy https listen address") + flag.Parse() + + proxy := goproxy.NewProxyHttpServer() + proxy.Verbose = *verbose + if proxy.Verbose { + log.Printf("Server starting up! - configured to listen on http interface %s and https interface %s", *http_addr, *https_addr) + } + + proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.Host == "" { + fmt.Fprintln(w, "Cannot handle requests without Host header, e.g., HTTP 1.0") + return + } + req.URL.Scheme = "http" + req.URL.Host = req.Host + proxy.ServeHTTP(w, req) + }) + proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))). + HandleConnect(goproxy.AlwaysMitm) + proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))). + HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) { + defer func() { + if e := recover(); e != nil { + ctx.Logf("error connecting to remote: %v", e) + client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n")) + } + client.Close() + }() + clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client)) + remote, err := connectDial(proxy, "tcp", req.URL.Host) + orPanic(err) + remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote)) + for { + req, err := http.ReadRequest(clientBuf.Reader) + orPanic(err) + orPanic(req.Write(remoteBuf)) + orPanic(remoteBuf.Flush()) + resp, err := http.ReadResponse(remoteBuf.Reader, req) + orPanic(err) + orPanic(resp.Write(clientBuf.Writer)) + orPanic(clientBuf.Flush()) + } + }) + + go func() { + log.Fatalln(http.ListenAndServe(*http_addr, proxy)) + }() + + // listen to the TLS ClientHello but make it a CONNECT request instead + ln, err := net.Listen("tcp", *https_addr) + if err != nil { + log.Fatalf("Error listening for https connections - %v", err) + } + for { + c, err := ln.Accept() + if err != nil { + log.Printf("Error accepting new connection - %v", err) + continue + } + go func(c net.Conn) { + tlsConn, err := vhost.TLS(c) + if err != nil { + log.Printf("Error accepting new connection - %v", err) + } + if tlsConn.Host() == "" { + log.Printf("Cannot support non-SNI enabled clients") + return + } + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{ + Opaque: tlsConn.Host(), + Host: net.JoinHostPort(tlsConn.Host(), "443"), + }, + Host: tlsConn.Host(), + Header: make(http.Header), + } + resp := dumbResponseWriter{tlsConn} + proxy.ServeHTTP(resp, connectReq) + }(c) + } +} + +// copied/converted from https.go +func dial(proxy *goproxy.ProxyHttpServer, network, addr string) (c net.Conn, err error) { + if proxy.Tr.Dial != nil { + return proxy.Tr.Dial(network, addr) + } + return net.Dial(network, addr) +} + +// copied/converted from https.go +func connectDial(proxy *goproxy.ProxyHttpServer, network, addr string) (c net.Conn, err error) { + if proxy.ConnectDial == nil { + return dial(proxy, network, addr) + } + return proxy.ConnectDial(network, addr) +} + +type dumbResponseWriter struct { + net.Conn +} + +func (dumb dumbResponseWriter) Header() http.Header { + panic("Header() should not be called on this ResponseWriter") +} + +func (dumb dumbResponseWriter) Write(buf []byte) (int, error) { + if bytes.Equal(buf, []byte("HTTP/1.0 200 OK\r\n\r\n")) { + return len(buf), nil // throw away the HTTP OK response from the faux CONNECT request + } + return dumb.Conn.Write(buf) +} + +func (dumb dumbResponseWriter) WriteHeader(code int) { + panic("WriteHeader() should not be called on this ResponseWriter") +} + +func (dumb dumbResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + return dumb, bufio.NewReadWriter(bufio.NewReader(dumb), bufio.NewWriter(dumb)), nil +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-upside-down-ternet/main.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-upside-down-ternet/main.go new file mode 100644 index 00000000..4b683fd3 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-upside-down-ternet/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/ext/image" + "image" + "log" + "net/http" +) + +func main() { + proxy := goproxy.NewProxyHttpServer() + proxy.OnResponse().Do(goproxy_image.HandleImage(func(img image.Image, ctx *goproxy.ProxyCtx) image.Image { + dx, dy := img.Bounds().Dx(), img.Bounds().Dy() + + nimg := image.NewRGBA(img.Bounds()) + for i := 0; i < dx; i++ { + for j := 0; j <= dy; j++ { + nimg.Set(i, j, img.At(i, dy-j-1)) + } + } + return nimg + })) + proxy.Verbose = true + log.Fatal(http.ListenAndServe(":8080", proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/examples/goproxy-yui-minify/yui.go b/vendor/github.com/elazarl/goproxy/examples/goproxy-yui-minify/yui.go new file mode 100644 index 00000000..0e7eadbb --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/examples/goproxy-yui-minify/yui.go @@ -0,0 +1,91 @@ +// This example would minify standalone Javascript files (identified by their content type) +// using the command line utility YUI compressor http://yui.github.io/yuicompressor/ +// Example usage: +// +// ./yui -java /usr/local/bin/java -yuicompressor ~/Downloads/yuicompressor-2.4.8.jar +// $ curl -vx localhost:8080 http://golang.org/lib/godoc/godocs.js +// (function(){function g(){var u=$("#search");if(u.length===0){return}function t(){if(.... +// $ curl http://golang.org/lib/godoc/godocs.js | head -n 3 +// // Copyright 2012 The Go Authors. All rights reserved. +// // Use of this source code is governed by a BSD-style +// // license that can be found in the LICENSE file. +package main + +import ( + "flag" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "os/exec" + "path" + "strings" + + "github.com/elazarl/goproxy" +) + +func main() { + verbose := flag.Bool("v", false, "should every proxy request be logged to stdout") + addr := flag.String("addr", ":8080", "proxy listen address") + java := flag.String("javapath", "java", "where the Java executable is located") + yuicompressor := flag.String("yuicompressor", "", "where the yuicompressor is located, assumed to be in CWD") + yuicompressordir := flag.String("yuicompressordir", ".", "a folder to search yuicompressor in, will be ignored if yuicompressor is set") + flag.Parse() + if *yuicompressor == "" { + files, err := ioutil.ReadDir(*yuicompressordir) + if err != nil { + log.Fatal("Cannot find yuicompressor jar") + } + for _, file := range files { + if strings.HasPrefix(file.Name(), "yuicompressor") && strings.HasSuffix(file.Name(), ".jar") { + c := path.Join(*yuicompressordir, file.Name()) + yuicompressor = &c + break + } + } + } + if *yuicompressor == "" { + log.Fatal("Can't find yuicompressor jar, searched yuicompressor*.jar in dir ", *yuicompressordir) + } + if _, err := os.Stat(*yuicompressor); os.IsNotExist(err) { + log.Fatal("Can't find yuicompressor jar specified ", *yuicompressor) + } + proxy := goproxy.NewProxyHttpServer() + proxy.Verbose = *verbose + proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + contentType := resp.Header.Get("Content-Type") + if contentType == "application/javascript" || contentType == "application/x-javascript" { + // in real code, response should be streamed as well + var err error + cmd := exec.Command(*java, "-jar", *yuicompressor, "--type", "js") + cmd.Stdin = resp.Body + resp.Body, err = cmd.StdoutPipe() + if err != nil { + ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err) + return goproxy.TextResponse(ctx.Req, "Error getting stdout pipe") + } + stderr, err := cmd.StderrPipe() + if err != nil { + ctx.Logf("Error obtaining stderr from yuicompress: %s", err) + return goproxy.TextResponse(ctx.Req, "Error getting stderr pipe") + } + if err := cmd.Start(); err != nil { + ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err) + } + go func() { + defer stderr.Close() + const kb = 1024 + msg, err := ioutil.ReadAll(&io.LimitedReader{stderr, 50 * kb}) + if len(msg) != 0 { + ctx.Logf("Error executing yuicompress: %s", string(msg)) + } + if err != nil { + ctx.Logf("Error reading stderr from yuicompress: %s", string(msg)) + } + }() + } + return resp + }) + log.Fatal(http.ListenAndServe(*addr, proxy)) +} diff --git a/vendor/github.com/elazarl/goproxy/ext/auth/basic.go b/vendor/github.com/elazarl/goproxy/ext/auth/basic.go new file mode 100644 index 00000000..2641423c --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ext/auth/basic.go @@ -0,0 +1,76 @@ +package auth + +import ( + "bytes" + "encoding/base64" + "io/ioutil" + "net/http" + "strings" + + "github.com/elazarl/goproxy" +) + +var unauthorizedMsg = []byte("407 Proxy Authentication Required") + +func BasicUnauthorized(req *http.Request, realm string) *http.Response { + // TODO(elazar): verify realm is well formed + return &http.Response{ + StatusCode: 407, + ProtoMajor: 1, + ProtoMinor: 1, + Request: req, + Header: http.Header{"Proxy-Authenticate": []string{"Basic realm=" + realm}}, + Body: ioutil.NopCloser(bytes.NewBuffer(unauthorizedMsg)), + ContentLength: int64(len(unauthorizedMsg)), + } +} + +var proxyAuthorizationHeader = "Proxy-Authorization" + +func auth(req *http.Request, f func(user, passwd string) bool) bool { + authheader := strings.SplitN(req.Header.Get(proxyAuthorizationHeader), " ", 2) + req.Header.Del(proxyAuthorizationHeader) + if len(authheader) != 2 || authheader[0] != "Basic" { + return false + } + userpassraw, err := base64.StdEncoding.DecodeString(authheader[1]) + if err != nil { + return false + } + userpass := strings.SplitN(string(userpassraw), ":", 2) + if len(userpass) != 2 { + return false + } + return f(userpass[0], userpass[1]) +} + +// Basic returns a basic HTTP authentication handler for requests +// +// You probably want to use auth.ProxyBasic(proxy) to enable authentication for all proxy activities +func Basic(realm string, f func(user, passwd string) bool) goproxy.ReqHandler { + return goproxy.FuncReqHandler(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + if !auth(req, f) { + return nil, BasicUnauthorized(req, realm) + } + return req, nil + }) +} + +// BasicConnect returns a basic HTTP authentication handler for CONNECT requests +// +// You probably want to use auth.ProxyBasic(proxy) to enable authentication for all proxy activities +func BasicConnect(realm string, f func(user, passwd string) bool) goproxy.HttpsHandler { + return goproxy.FuncHttpsHandler(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) { + if !auth(ctx.Req, f) { + ctx.Resp = BasicUnauthorized(ctx.Req, realm) + return goproxy.RejectConnect, host + } + return goproxy.OkConnect, host + }) +} + +// ProxyBasic will force HTTP authentication before any request to the proxy is processed +func ProxyBasic(proxy *goproxy.ProxyHttpServer, realm string, f func(user, passwd string) bool) { + proxy.OnRequest().Do(Basic(realm, f)) + proxy.OnRequest().HandleConnect(BasicConnect(realm, f)) +} diff --git a/vendor/github.com/elazarl/goproxy/ext/auth/basic_test.go b/vendor/github.com/elazarl/goproxy/ext/auth/basic_test.go new file mode 100644 index 00000000..792d789b --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ext/auth/basic_test.go @@ -0,0 +1,175 @@ +package auth_test + +import ( + "encoding/base64" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "os/exec" + "os/signal" + "sync/atomic" + "testing" + + "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/ext/auth" +) + +type ConstantHanlder string + +func (h ConstantHanlder) ServeHTTP(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, string(h)) +} + +func oneShotProxy(proxy *goproxy.ProxyHttpServer) (client *http.Client, s *httptest.Server) { + s = httptest.NewServer(proxy) + + proxyUrl, _ := url.Parse(s.URL) + tr := &http.Transport{Proxy: http.ProxyURL(proxyUrl)} + client = &http.Client{Transport: tr} + return +} + +func times(n int, s string) string { + r := make([]byte, 0, n*len(s)) + for i := 0; i < n; i++ { + r = append(r, s...) + } + return string(r) +} + +func TestBasicConnectAuthWithCurl(t *testing.T) { + expected := ":c>" + background := httptest.NewTLSServer(ConstantHanlder(expected)) + defer background.Close() + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().HandleConnect(auth.BasicConnect("my_realm", func(user, passwd string) bool { + return user == "user" && passwd == "open sesame" + })) + _, proxyserver := oneShotProxy(proxy) + defer proxyserver.Close() + + cmd := exec.Command("curl", + "--silent", "--show-error", "--insecure", + "-x", proxyserver.URL, + "-U", "user:open sesame", + "-p", + "--url", background.URL+"/[1-3]", + ) + out, err := cmd.CombinedOutput() // if curl got error, it'll show up in stderr + if err != nil { + t.Fatal(err, string(out)) + } + finalexpected := times(3, expected) + if string(out) != finalexpected { + t.Error("Expected", finalexpected, "got", string(out)) + } +} + +func TestBasicAuthWithCurl(t *testing.T) { + expected := ":c>" + background := httptest.NewServer(ConstantHanlder(expected)) + defer background.Close() + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().Do(auth.Basic("my_realm", func(user, passwd string) bool { + return user == "user" && passwd == "open sesame" + })) + _, proxyserver := oneShotProxy(proxy) + defer proxyserver.Close() + + cmd := exec.Command("curl", + "--silent", "--show-error", + "-x", proxyserver.URL, + "-U", "user:open sesame", + "--url", background.URL+"/[1-3]", + ) + out, err := cmd.CombinedOutput() // if curl got error, it'll show up in stderr + if err != nil { + t.Fatal(err, string(out)) + } + finalexpected := times(3, expected) + if string(out) != finalexpected { + t.Error("Expected", finalexpected, "got", string(out)) + } +} + +func TestBasicAuth(t *testing.T) { + expected := "hello" + background := httptest.NewServer(ConstantHanlder(expected)) + defer background.Close() + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().Do(auth.Basic("my_realm", func(user, passwd string) bool { + return user == "user" && passwd == "open sesame" + })) + client, proxyserver := oneShotProxy(proxy) + defer proxyserver.Close() + + // without auth + resp, err := client.Get(background.URL) + if err != nil { + t.Fatal(err) + } + if resp.Header.Get("Proxy-Authenticate") != "Basic realm=my_realm" { + t.Error("Expected Proxy-Authenticate header got", resp.Header.Get("Proxy-Authenticate")) + } + if resp.StatusCode != 407 { + t.Error("Expected status 407 Proxy Authentication Required, got", resp.Status) + } + + // with auth + req, err := http.NewRequest("GET", background.URL, nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Proxy-Authorization", + "Basic "+base64.StdEncoding.EncodeToString([]byte("user:open sesame"))) + resp, err = client.Do(req) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != 200 { + t.Error("Expected status 200 OK, got", resp.Status) + } + msg, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + if string(msg) != "hello" { + t.Errorf("Expected '%s', actual '%s'", expected, string(msg)) + } +} + +func TestWithBrowser(t *testing.T) { + // an easy way to check if auth works with webserver + // to test, run with + // $ go test -run TestWithBrowser -- server + // configure a browser to use the printed proxy address, use the proxy + // and exit with Ctrl-C. It will throw error if your haven't acutally used the proxy + if os.Args[len(os.Args)-1] != "server" { + return + } + proxy := goproxy.NewProxyHttpServer() + println("proxy localhost port 8082") + access := int32(0) + proxy.OnRequest().Do(auth.Basic("my_realm", func(user, passwd string) bool { + atomic.AddInt32(&access, 1) + return user == "user" && passwd == "1234" + })) + l, err := net.Listen("tcp", "localhost:8082") + if err != nil { + t.Fatal(err) + } + ch := make(chan os.Signal) + signal.Notify(ch, os.Interrupt) + go func() { + <-ch + l.Close() + }() + http.Serve(l, proxy) + if access <= 0 { + t.Error("No one accessed the proxy") + } +} diff --git a/vendor/github.com/elazarl/goproxy/ext/html/cp1255.html b/vendor/github.com/elazarl/goproxy/ext/html/cp1255.html new file mode 100644 index 00000000..6bf33e81 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ext/html/cp1255.html @@ -0,0 +1,585 @@ + + + + + + + + + +ãó äáéú ùì ùä"í + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + +
+
+ + +rss +
+ +
+ + + + +
+
+
+  àúøé 2012á ðôúçå ìñèåãðèéí. öååú ùä"í îàçì ìëí ñîñèø îåöìç. +(4.3.12) +
+
+ + + + + + +
+ + +
+
+ +
+ + +
+ + +
+ + + + + + + diff --git a/vendor/github.com/elazarl/goproxy/ext/html/cp1255.txt b/vendor/github.com/elazarl/goproxy/ext/html/cp1255.txt new file mode 100644 index 00000000..ef904ced --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ext/html/cp1255.txt @@ -0,0 +1 @@ +ãó \ No newline at end of file diff --git a/vendor/github.com/elazarl/goproxy/ext/html/html.go b/vendor/github.com/elazarl/goproxy/ext/html/html.go new file mode 100644 index 00000000..e8397aa4 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ext/html/html.go @@ -0,0 +1,104 @@ +// extension to goproxy that will allow you to easily filter web browser related content. +package goproxy_html + +import ( + "bytes" + "errors" + "io" + "io/ioutil" + "net/http" + "strings" + + "github.com/rogpeppe/go-charset/charset" + _ "github.com/rogpeppe/go-charset/data" + "github.com/elazarl/goproxy" +) + +var IsHtml goproxy.RespCondition = goproxy.ContentTypeIs("text/html") + +var IsCss goproxy.RespCondition = goproxy.ContentTypeIs("text/css") + +var IsJavaScript goproxy.RespCondition = goproxy.ContentTypeIs("text/javascript", + "application/javascript") + +var IsJson goproxy.RespCondition = goproxy.ContentTypeIs("text/json") + +var IsXml goproxy.RespCondition = goproxy.ContentTypeIs("text/xml") + +var IsWebRelatedText goproxy.RespCondition = goproxy.ContentTypeIs("text/html", + "text/css", + "text/javascript", "application/javascript", + "text/xml", + "text/json") + +// HandleString will receive a function that filters a string, and will convert the +// request body to a utf8 string, according to the charset specified in the Content-Type +// header. +// guessing Html charset encoding from the tags is not yet implemented. +func HandleString(f func(s string, ctx *goproxy.ProxyCtx) string) goproxy.RespHandler { + return HandleStringReader(func(r io.Reader, ctx *goproxy.ProxyCtx) io.Reader { + b, err := ioutil.ReadAll(r) + if err != nil { + ctx.Warnf("Cannot read string from resp body: %v", err) + return r + } + return bytes.NewBufferString(f(string(b), ctx)) + }) +} + +// Will receive an input stream which would convert the response to utf-8 +// The given function must close the reader r, in order to close the response body. +func HandleStringReader(f func(r io.Reader, ctx *goproxy.ProxyCtx) io.Reader) goproxy.RespHandler { + return goproxy.FuncRespHandler(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + if ctx.Error != nil { + return nil + } + charsetName := ctx.Charset() + if charsetName == "" { + charsetName = "utf-8" + } + + if strings.ToLower(charsetName) != "utf-8" { + r, err := charset.NewReader(charsetName, resp.Body) + if err != nil { + ctx.Warnf("Cannot convert from %v to utf-8: %v", charsetName, err) + return resp + } + tr, err := charset.TranslatorTo(charsetName) + if err != nil { + ctx.Warnf("Can't translate to %v from utf-8: %v", charsetName, err) + return resp + } + if err != nil { + ctx.Warnf("Cannot translate to %v: %v", charsetName, err) + return resp + } + newr := charset.NewTranslatingReader(f(r, ctx), tr) + resp.Body = &readFirstCloseBoth{ioutil.NopCloser(newr), resp.Body} + } else { + //no translation is needed, already at utf-8 + resp.Body = &readFirstCloseBoth{ioutil.NopCloser(f(resp.Body, ctx)), resp.Body} + } + return resp + }) +} + +type readFirstCloseBoth struct { + r io.ReadCloser + c io.Closer +} + +func (rfcb *readFirstCloseBoth) Read(b []byte) (nr int, err error) { + return rfcb.r.Read(b) +} +func (rfcb *readFirstCloseBoth) Close() error { + err1 := rfcb.r.Close() + err2 := rfcb.c.Close() + if err1 != nil && err2 != nil { + return errors.New(err1.Error() + ", " + err2.Error()) + } + if err1 != nil { + return err1 + } + return err2 +} diff --git a/vendor/github.com/elazarl/goproxy/ext/html/html_test.go b/vendor/github.com/elazarl/goproxy/ext/html/html_test.go new file mode 100644 index 00000000..9c876f75 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ext/html/html_test.go @@ -0,0 +1,60 @@ +package goproxy_html_test + +import ( + "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/ext/html" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +type ConstantServer int + +func (s ConstantServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=iso-8859-8") + //w.Header().Set("Content-Type","text/plain; charset=cp-1255") + w.Write([]byte{0xe3, 0xf3}) +} + +func TestCharset(t *testing.T) { + s := httptest.NewServer(ConstantServer(1)) + defer s.Close() + + ch := make(chan string, 2) + proxy := goproxy.NewProxyHttpServer() + proxy.OnResponse().Do(goproxy_html.HandleString( + func(s string, ctx *goproxy.ProxyCtx) string { + ch <- s + return s + })) + proxyServer := httptest.NewServer(proxy) + defer proxyServer.Close() + + proxyUrl, _ := url.Parse(proxyServer.URL) + client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}} + + resp, err := client.Get(s.URL + "/cp1255.txt") + if err != nil { + t.Fatal("GET:", err) + } + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal("readAll:", err) + } + resp.Body.Close() + + inHandleString := "" + select { + case inHandleString = <-ch: + default: + } + + if len(b) != 2 || b[0] != 0xe3 || b[1] != 0xf3 { + t.Error("Did not translate back to 0xe3,0xf3, instead", b) + } + if inHandleString != "דף" { + t.Error("HandleString did not convert DALET & PEH SOFIT (דף) from ISO-8859-8 to utf-8, got", []byte(inHandleString)) + } +} diff --git a/vendor/github.com/elazarl/goproxy/ext/image/image.go b/vendor/github.com/elazarl/goproxy/ext/image/image.go new file mode 100644 index 00000000..3dc26ff3 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/ext/image/image.go @@ -0,0 +1,78 @@ +package goproxy_image + +import ( + "bytes" + "image" + _ "image/gif" + "image/jpeg" + "image/png" + "io/ioutil" + "net/http" + . "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/regretable" +) + +var RespIsImage = ContentTypeIs("image/gif", + "image/jpeg", + "image/pjpeg", + "application/octet-stream", + "image/png") + +// "image/tiff" tiff support is in external package, and rarely used, so we omitted it + +func HandleImage(f func(img image.Image, ctx *ProxyCtx) image.Image) RespHandler { + return FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response { + if !RespIsImage.HandleResp(resp, ctx) { + return resp + } + if resp.StatusCode != 200 { + // we might get 304 - not modified response without data + return resp + } + contentType := resp.Header.Get("Content-Type") + + const kb = 1024 + regret := regretable.NewRegretableReaderCloserSize(resp.Body, 16*kb) + resp.Body = regret + img, imgType, err := image.Decode(resp.Body) + if err != nil { + regret.Regret() + ctx.Warnf("%s: %s", ctx.Req.Method+" "+ctx.Req.URL.String()+" Image from "+ctx.Req.RequestURI+"content type"+ + contentType+"cannot be decoded returning original image", err) + return resp + } + result := f(img, ctx) + buf := bytes.NewBuffer([]byte{}) + switch contentType { + // No gif image encoder in go - convert to png + case "image/gif", "image/png": + if err := png.Encode(buf, result); err != nil { + ctx.Warnf("Cannot encode image, returning orig %v %v", ctx.Req.URL.String(), err) + return resp + } + resp.Header.Set("Content-Type", "image/png") + case "image/jpeg", "image/pjpeg": + if err := jpeg.Encode(buf, result, nil); err != nil { + ctx.Warnf("Cannot encode image, returning orig %v %v", ctx.Req.URL.String(), err) + return resp + } + case "application/octet-stream": + switch imgType { + case "jpeg": + if err := jpeg.Encode(buf, result, nil); err != nil { + ctx.Warnf("Cannot encode image as jpeg, returning orig %v %v", ctx.Req.URL.String(), err) + return resp + } + case "png", "gif": + if err := png.Encode(buf, result); err != nil { + ctx.Warnf("Cannot encode image as png, returning orig %v %v", ctx.Req.URL.String(), err) + return resp + } + } + default: + panic("unhandlable type" + contentType) + } + resp.Body = ioutil.NopCloser(buf) + return resp + }) +} diff --git a/vendor/github.com/elazarl/goproxy/https.go b/vendor/github.com/elazarl/goproxy/https.go new file mode 100644 index 00000000..b3e0e3ba --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/https.go @@ -0,0 +1,411 @@ +package goproxy + +import ( + "bufio" + "crypto/tls" + "errors" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "regexp" + "strconv" + "strings" + "sync" + "sync/atomic" +) + +type ConnectActionLiteral int + +const ( + ConnectAccept = iota + ConnectReject + ConnectMitm + ConnectHijack + ConnectHTTPMitm + ConnectProxyAuthHijack +) + +var ( + OkConnect = &ConnectAction{Action: ConnectAccept, TLSConfig: TLSConfigFromCA(&GoproxyCa)} + MitmConnect = &ConnectAction{Action: ConnectMitm, TLSConfig: TLSConfigFromCA(&GoproxyCa)} + HTTPMitmConnect = &ConnectAction{Action: ConnectHTTPMitm, TLSConfig: TLSConfigFromCA(&GoproxyCa)} + RejectConnect = &ConnectAction{Action: ConnectReject, TLSConfig: TLSConfigFromCA(&GoproxyCa)} + httpsRegexp = regexp.MustCompile(`^https:\/\/`) +) + +type ConnectAction struct { + Action ConnectActionLiteral + Hijack func(req *http.Request, client net.Conn, ctx *ProxyCtx) + TLSConfig func(host string, ctx *ProxyCtx) (*tls.Config, error) +} + +func stripPort(s string) string { + ix := strings.IndexRune(s, ':') + if ix == -1 { + return s + } + return s[:ix] +} + +func (proxy *ProxyHttpServer) dial(network, addr string) (c net.Conn, err error) { + if proxy.Tr.Dial != nil { + return proxy.Tr.Dial(network, addr) + } + return net.Dial(network, addr) +} + +func (proxy *ProxyHttpServer) connectDial(network, addr string) (c net.Conn, err error) { + if proxy.ConnectDial == nil { + return proxy.dial(network, addr) + } + return proxy.ConnectDial(network, addr) +} + +func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request) { + ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy} + + hij, ok := w.(http.Hijacker) + if !ok { + panic("httpserver does not support hijacking") + } + + proxyClient, _, e := hij.Hijack() + if e != nil { + panic("Cannot hijack connection " + e.Error()) + } + + ctx.Logf("Running %d CONNECT handlers", len(proxy.httpsHandlers)) + todo, host := OkConnect, r.URL.Host + for i, h := range proxy.httpsHandlers { + newtodo, newhost := h.HandleConnect(host, ctx) + + // If found a result, break the loop immediately + if newtodo != nil { + todo, host = newtodo, newhost + ctx.Logf("on %dth handler: %v %s", i, todo, host) + break + } + } + switch todo.Action { + case ConnectAccept: + if !hasPort.MatchString(host) { + host += ":80" + } + targetSiteCon, err := proxy.connectDial("tcp", host) + if err != nil { + httpError(proxyClient, ctx, err) + return + } + ctx.Logf("Accepting CONNECT to %s", host) + proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) + + targetTCP, targetOK := targetSiteCon.(*net.TCPConn) + proxyClientTCP, clientOK := proxyClient.(*net.TCPConn) + if targetOK && clientOK { + go copyAndClose(ctx, targetTCP, proxyClientTCP) + go copyAndClose(ctx, proxyClientTCP, targetTCP) + } else { + go func() { + var wg sync.WaitGroup + wg.Add(2) + go copyOrWarn(ctx, targetSiteCon, proxyClient, &wg) + go copyOrWarn(ctx, proxyClient, targetSiteCon, &wg) + wg.Wait() + proxyClient.Close() + targetSiteCon.Close() + + }() + } + + case ConnectHijack: + ctx.Logf("Hijacking CONNECT to %s", host) + proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) + todo.Hijack(r, proxyClient, ctx) + case ConnectHTTPMitm: + proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) + ctx.Logf("Assuming CONNECT is plain HTTP tunneling, mitm proxying it") + targetSiteCon, err := proxy.connectDial("tcp", host) + if err != nil { + ctx.Warnf("Error dialing to %s: %s", host, err.Error()) + return + } + for { + client := bufio.NewReader(proxyClient) + remote := bufio.NewReader(targetSiteCon) + req, err := http.ReadRequest(client) + if err != nil && err != io.EOF { + ctx.Warnf("cannot read request of MITM HTTP client: %+#v", err) + } + if err != nil { + return + } + req, resp := proxy.filterRequest(req, ctx) + if resp == nil { + if err := req.Write(targetSiteCon); err != nil { + httpError(proxyClient, ctx, err) + return + } + resp, err = http.ReadResponse(remote, req) + if err != nil { + httpError(proxyClient, ctx, err) + return + } + defer resp.Body.Close() + } + resp = proxy.filterResponse(resp, ctx) + if err := resp.Write(proxyClient); err != nil { + httpError(proxyClient, ctx, err) + return + } + } + case ConnectMitm: + proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) + ctx.Logf("Assuming CONNECT is TLS, mitm proxying it") + // this goes in a separate goroutine, so that the net/http server won't think we're + // still handling the request even after hijacking the connection. Those HTTP CONNECT + // request can take forever, and the server will be stuck when "closed". + // TODO: Allow Server.Close() mechanism to shut down this connection as nicely as possible + tlsConfig := defaultTLSConfig + if todo.TLSConfig != nil { + var err error + tlsConfig, err = todo.TLSConfig(host, ctx) + if err != nil { + httpError(proxyClient, ctx, err) + return + } + } + go func() { + //TODO: cache connections to the remote website + rawClientTls := tls.Server(proxyClient, tlsConfig) + if err := rawClientTls.Handshake(); err != nil { + ctx.Warnf("Cannot handshake client %v %v", r.Host, err) + return + } + defer rawClientTls.Close() + clientTlsReader := bufio.NewReader(rawClientTls) + for !isEof(clientTlsReader) { + req, err := http.ReadRequest(clientTlsReader) + var ctx = &ProxyCtx{Req: req, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy} + if err != nil && err != io.EOF { + return + } + if err != nil { + ctx.Warnf("Cannot read TLS request from mitm'd client %v %v", r.Host, err) + return + } + req.RemoteAddr = r.RemoteAddr // since we're converting the request, need to carry over the original connecting IP as well + ctx.Logf("req %v", r.Host) + + if !httpsRegexp.MatchString(req.URL.String()) { + req.URL, err = url.Parse("https://" + r.Host + req.URL.String()) + } + + // Bug fix which goproxy fails to provide request + // information URL in the context when does HTTPS MITM + ctx.Req = req + + req, resp := proxy.filterRequest(req, ctx) + if resp == nil { + if err != nil { + ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path) + return + } + removeProxyHeaders(ctx, req) + resp, err = ctx.RoundTrip(req) + if err != nil { + ctx.Warnf("Cannot read TLS response from mitm'd server %v", err) + return + } + ctx.Logf("resp %v", resp.Status) + } + resp = proxy.filterResponse(resp, ctx) + defer resp.Body.Close() + + text := resp.Status + statusCode := strconv.Itoa(resp.StatusCode) + " " + if strings.HasPrefix(text, statusCode) { + text = text[len(statusCode):] + } + // always use 1.1 to support chunked encoding + if _, err := io.WriteString(rawClientTls, "HTTP/1.1"+" "+statusCode+text+"\r\n"); err != nil { + ctx.Warnf("Cannot write TLS response HTTP status from mitm'd client: %v", err) + return + } + // Since we don't know the length of resp, return chunked encoded response + // TODO: use a more reasonable scheme + resp.Header.Del("Content-Length") + resp.Header.Set("Transfer-Encoding", "chunked") + // Force connection close otherwise chrome will keep CONNECT tunnel open forever + resp.Header.Set("Connection", "close") + if err := resp.Header.Write(rawClientTls); err != nil { + ctx.Warnf("Cannot write TLS response header from mitm'd client: %v", err) + return + } + if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil { + ctx.Warnf("Cannot write TLS response header end from mitm'd client: %v", err) + return + } + chunked := newChunkedWriter(rawClientTls) + if _, err := io.Copy(chunked, resp.Body); err != nil { + ctx.Warnf("Cannot write TLS response body from mitm'd client: %v", err) + return + } + if err := chunked.Close(); err != nil { + ctx.Warnf("Cannot write TLS chunked EOF from mitm'd client: %v", err) + return + } + if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil { + ctx.Warnf("Cannot write TLS response chunked trailer from mitm'd client: %v", err) + return + } + } + ctx.Logf("Exiting on EOF") + }() + case ConnectProxyAuthHijack: + proxyClient.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\n")) + todo.Hijack(r, proxyClient, ctx) + case ConnectReject: + if ctx.Resp != nil { + if err := ctx.Resp.Write(proxyClient); err != nil { + ctx.Warnf("Cannot write response that reject http CONNECT: %v", err) + } + } + proxyClient.Close() + } +} + +func httpError(w io.WriteCloser, ctx *ProxyCtx, err error) { + if _, err := io.WriteString(w, "HTTP/1.1 502 Bad Gateway\r\n\r\n"); err != nil { + ctx.Warnf("Error responding to client: %s", err) + } + if err := w.Close(); err != nil { + ctx.Warnf("Error closing client connection: %s", err) + } +} + +func copyOrWarn(ctx *ProxyCtx, dst io.Writer, src io.Reader, wg *sync.WaitGroup) { + if _, err := io.Copy(dst, src); err != nil { + ctx.Warnf("Error copying to client: %s", err) + } + wg.Done() +} + +func copyAndClose(ctx *ProxyCtx, dst, src *net.TCPConn) { + if _, err := io.Copy(dst, src); err != nil { + ctx.Warnf("Error copying to client: %s", err) + } + + dst.CloseWrite() + src.CloseRead() +} + +func dialerFromEnv(proxy *ProxyHttpServer) func(network, addr string) (net.Conn, error) { + https_proxy := os.Getenv("HTTPS_PROXY") + if https_proxy == "" { + https_proxy = os.Getenv("https_proxy") + } + if https_proxy == "" { + return nil + } + return proxy.NewConnectDialToProxy(https_proxy) +} + +func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(network, addr string) (net.Conn, error) { + u, err := url.Parse(https_proxy) + if err != nil { + return nil + } + if u.Scheme == "" || u.Scheme == "http" { + if strings.IndexRune(u.Host, ':') == -1 { + u.Host += ":80" + } + return func(network, addr string) (net.Conn, error) { + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: addr}, + Host: addr, + Header: make(http.Header), + } + c, err := proxy.dial(network, u.Host) + if err != nil { + return nil, err + } + connectReq.Write(c) + // Read response. + // Okay to use and discard buffered reader here, because + // TLS server will not speak until spoken to. + br := bufio.NewReader(c) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + c.Close() + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + resp, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + c.Close() + return nil, errors.New("proxy refused connection" + string(resp)) + } + return c, nil + } + } + if u.Scheme == "https" { + if strings.IndexRune(u.Host, ':') == -1 { + u.Host += ":443" + } + return func(network, addr string) (net.Conn, error) { + c, err := proxy.dial(network, u.Host) + if err != nil { + return nil, err + } + c = tls.Client(c, proxy.Tr.TLSClientConfig) + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: addr}, + Host: addr, + Header: make(http.Header), + } + connectReq.Write(c) + // Read response. + // Okay to use and discard buffered reader here, because + // TLS server will not speak until spoken to. + br := bufio.NewReader(c) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + c.Close() + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 500)) + if err != nil { + return nil, err + } + c.Close() + return nil, errors.New("proxy refused connection" + string(body)) + } + return c, nil + } + } + return nil +} + +func TLSConfigFromCA(ca *tls.Certificate) func(host string, ctx *ProxyCtx) (*tls.Config, error) { + return func(host string, ctx *ProxyCtx) (*tls.Config, error) { + config := *defaultTLSConfig + ctx.Logf("signing for %s", stripPort(host)) + cert, err := signHost(*ca, []string{stripPort(host)}) + if err != nil { + ctx.Warnf("Cannot sign host certificate with provided CA: %s", err) + return nil, err + } + config.Certificates = append(config.Certificates, cert) + return &config, nil + } +} diff --git a/vendor/github.com/elazarl/goproxy/key.pem b/vendor/github.com/elazarl/goproxy/key.pem new file mode 100644 index 00000000..2ea1dca4 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAnhDL4fqGGhjWzRBFy8iHGuNIdo79FtoWPevCpyek6AWrTuBF +0j3dzRMUpAkemC/p94tGES9f9iWUVi7gnfmUz1lxhjiqUoW5K1xfwmbx+qmC2YAw +HM+yq2oOLwz1FAYoQ3NT0gU6cJXtIB6Hjmxwy4jfDPzCuMFwfvOq4eS+pRJhnPTf +m31XpZOsfJMS9PjD6UU5U3ZsD/oMAjGuMGIXoOGgmqeFrRJm0N+/vtenAYbcSED+ +qiGGJisOu5grvMl0RJAvjgvDMw+6lWKCpqV+/5gd9CNuFP3nUhW6tbY0mBHIETrZ +0uuUdh21P20JMKt34ok0wn6On2ECN0i7UGv+SJ9TgXj7hksxH1R6OLQaSQ8qxh3I +yeqPSnQ+iDK8/WXiqZug8iYxi1qgW5iYxiV5uAL0s3XRsv3Urj6Mu3QjVie0TOuq +AmhawnO1gPDnjc3NLLlb79yrhdFiC2rVvRFbC5SKzB7OYyh7IdnwFAl7bEyMA6WU +BIN+prw4rdYAEcmnLjNSudQGIy48hPMP8W4PHgLkjDCULryAcBluU2qkFkJfScUK +0qNg5wjZKjkdtDY4LxAX7MZW524dRKiTiFLLYEF9nWl+/OKoF561YnAW9qkYHjic +geFYo0q+o7Es0jLt75MZGJY6iasBYzXxVJH0tlsHGkkrs8tLNapglhNEJkcCAwEA +AQKCAgAwSuNvxHHqUUJ3XoxkiXy1u1EtX9x1eeYnvvs2xMb+WJURQTYz2NEGUdkR +kPO2/ZSXHAcpQvcnpi2e8y2PNmy/uQ0VPATVt6NuWweqxncR5W5j82U/uDlXY8y3 +lVbfak4s5XRri0tikHvlP06dNgZ0OPok5qi7d+Zd8yZ3Y8LXfjkykiIrSG1Z2jdt +zCWTkNmSUKMGG/1CGFxI41Lb12xuq+C8v4f469Fb6bCUpyCQN9rffHQSGLH6wVb7 ++68JO+d49zCATpmx5RFViMZwEcouXxRvvc9pPHXLP3ZPBD8nYu9kTD220mEGgWcZ +3L9dDlZPcSocbjw295WMvHz2QjhrDrb8gXwdpoRyuyofqgCyNxSnEC5M13SjOxtf +pjGzjTqh0kDlKXg2/eTkd9xIHjVhFYiHIEeITM/lHCfWwBCYxViuuF7pSRPzTe8U +C440b62qZSPMjVoquaMg+qx0n9fKSo6n1FIKHypv3Kue2G0WhDeK6u0U288vQ1t4 +Ood3Qa13gZ+9hwDLbM/AoBfVBDlP/tpAwa7AIIU1ZRDNbZr7emFdctx9B6kLINv3 +4PDOGM2xrjOuACSGMq8Zcu7LBz35PpIZtviJOeKNwUd8/xHjWC6W0itgfJb5I1Nm +V6Vj368pGlJx6Se26lvXwyyrc9pSw6jSAwARBeU4YkNWpi4i6QKCAQEA0T7u3P/9 +jZJSnDN1o2PXymDrJulE61yguhc/QSmLccEPZe7or06/DmEhhKuCbv+1MswKDeag +/1JdFPGhL2+4G/f/9BK3BJPdcOZSz7K6Ty8AMMBf8AehKTcSBqwkJWcbEvpHpKJ6 +eDqn1B6brXTNKMT6fEEXCuZJGPBpNidyLv/xXDcN7kCOo3nGYKfB5OhFpNiL63tw ++LntU56WESZwEqr8Pf80uFvsyXQK3a5q5HhIQtxl6tqQuPlNjsDBvCqj0x72mmaJ +ZVsVWlv7khUrCwAXz7Y8K7mKKBd2ekF5hSbryfJsxFyvEaWUPhnJpTKV85lAS+tt +FQuIp9TvKYlRQwKCAQEAwWJN8jysapdhi67jO0HtYOEl9wwnF4w6XtiOYtllkMmC +06/e9h7RsRyWPMdu3qRDPUYFaVDy6+dpUDSQ0+E2Ot6AHtVyvjeUTIL651mFIo/7 +OSUCEc+HRo3SfPXdPhSQ2thNTxl6y9XcFacuvbthgr70KXbvC4k6IEmdpf/0Kgs9 +7QTZCG26HDrEZ2q9yMRlRaL2SRD+7Y2xra7gB+cQGFj6yn0Wd/07er49RqMXidQf +KR2oYfev2BDtHXoSZFfhFGHlOdLvWRh90D4qZf4vQ+g/EIMgcNSoxjvph1EShmKt +sjhTHtoHuu+XmEQvIewk2oCI+JvofBkcnpFrVvUUrQKCAQAaTIufETmgCo0BfuJB +N/JOSGIl0NnNryWwXe2gVgVltbsmt6FdL0uKFiEtWJUbOF5g1Q5Kcvs3O/XhBQGa +QbNlKIVt+tAv7hm97+Tmn/MUsraWagdk1sCluns0hXxBizT27KgGhDlaVRz05yfv +5CdJAYDuDwxDXXBAhy7iFJEgYSDH00+X61tCJrMNQOh4ycy/DEyBu1EWod+3S85W +t3sMjZsIe8P3i+4137Th6eMbdha2+JaCrxfTd9oMoCN5b+6JQXIDM/H+4DTN15PF +540yY7+aZrAnWrmHknNcqFAKsTqfdi2/fFqwoBwCtiEG91WreU6AfEWIiJuTZIru +sIibAoIBAAqIwlo5t+KukF+9jR9DPh0S5rCIdvCvcNaN0WPNF91FPN0vLWQW1bFi +L0TsUDvMkuUZlV3hTPpQxsnZszH3iK64RB5p3jBCcs+gKu7DT59MXJEGVRCHT4Um +YJryAbVKBYIGWl++sZO8+JotWzx2op8uq7o+glMMjKAJoo7SXIiVyC/LHc95urOi +9+PySphPKn0anXPpexmRqGYfqpCDo7rPzgmNutWac80B4/CfHb8iUPg6Z1u+1FNe +yKvcZHgW2Wn00znNJcCitufLGyAnMofudND/c5rx2qfBx7zZS7sKUQ/uRYjes6EZ +QBbJUA/2/yLv8YYpaAaqj4aLwV8hRpkCggEBAIh3e25tr3avCdGgtCxS7Y1blQ2c +ue4erZKmFP1u8wTNHQ03T6sECZbnIfEywRD/esHpclfF3kYAKDRqIP4K905Rb0iH +759ZWt2iCbqZznf50XTvptdmjm5KxvouJzScnQ52gIV6L+QrCKIPelLBEIqCJREh +pmcjjocD/UCCSuHgbAYNNnO/JdhnSylz1tIg26I+2iLNyeTKIepSNlsBxnkLmqM1 +cj/azKBaT04IOMLaN8xfSqitJYSraWMVNgGJM5vfcVaivZnNh0lZBv+qu6YkdM88 +4/avCJ8IutT+FcMM+GbGazOm5ALWqUyhrnbLGc4CQMPfe7Il6NxwcrOxT8w= +-----END RSA PRIVATE KEY----- diff --git a/vendor/github.com/elazarl/goproxy/proxy.go b/vendor/github.com/elazarl/goproxy/proxy.go new file mode 100644 index 00000000..fefb3bb0 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/proxy.go @@ -0,0 +1,162 @@ +package goproxy + +import ( + "bufio" + "io" + "log" + "net" + "net/http" + "os" + "regexp" + "sync/atomic" +) + +// The basic proxy type. Implements http.Handler. +type ProxyHttpServer struct { + // session variable must be aligned in i386 + // see http://golang.org/src/pkg/sync/atomic/doc.go#L41 + sess int64 + // setting Verbose to true will log information on each request sent to the proxy + Verbose bool + Logger *log.Logger + NonproxyHandler http.Handler + reqHandlers []ReqHandler + respHandlers []RespHandler + httpsHandlers []HttpsHandler + Tr *http.Transport + // ConnectDial will be used to create TCP connections for CONNECT requests + // if nil Tr.Dial will be used + ConnectDial func(network string, addr string) (net.Conn, error) +} + +var hasPort = regexp.MustCompile(`:\d+$`) + +func copyHeaders(dst, src http.Header) { + for k, _ := range dst { + dst.Del(k) + } + for k, vs := range src { + for _, v := range vs { + dst.Add(k, v) + } + } +} + +func isEof(r *bufio.Reader) bool { + _, err := r.Peek(1) + if err == io.EOF { + return true + } + return false +} + +func (proxy *ProxyHttpServer) filterRequest(r *http.Request, ctx *ProxyCtx) (req *http.Request, resp *http.Response) { + req = r + for _, h := range proxy.reqHandlers { + req, resp = h.Handle(r, ctx) + // non-nil resp means the handler decided to skip sending the request + // and return canned response instead. + if resp != nil { + break + } + } + return +} +func (proxy *ProxyHttpServer) filterResponse(respOrig *http.Response, ctx *ProxyCtx) (resp *http.Response) { + resp = respOrig + for _, h := range proxy.respHandlers { + ctx.Resp = resp + resp = h.Handle(resp, ctx) + } + return +} + +func removeProxyHeaders(ctx *ProxyCtx, r *http.Request) { + r.RequestURI = "" // this must be reset when serving a request with the client + ctx.Logf("Sending request %v %v", r.Method, r.URL.String()) + // If no Accept-Encoding header exists, Transport will add the headers it can accept + // and would wrap the response body with the relevant reader. + r.Header.Del("Accept-Encoding") + // curl can add that, see + // https://jdebp.eu./FGA/web-proxy-connection-header.html + r.Header.Del("Proxy-Connection") + r.Header.Del("Proxy-Authenticate") + r.Header.Del("Proxy-Authorization") + // Connection, Authenticate and Authorization are single hop Header: + // http://www.w3.org/Protocols/rfc2616/rfc2616.txt + // 14.10 Connection + // The Connection general-header field allows the sender to specify + // options that are desired for that particular connection and MUST NOT + // be communicated by proxies over further connections. + r.Header.Del("Connection") +} + +// Standard net/http function. Shouldn't be used directly, http.Serve will use it. +func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + //r.Header["X-Forwarded-For"] = w.RemoteAddr() + if r.Method == "CONNECT" { + proxy.handleHttps(w, r) + } else { + ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy} + + var err error + ctx.Logf("Got request %v %v %v %v", r.URL.Path, r.Host, r.Method, r.URL.String()) + if !r.URL.IsAbs() { + proxy.NonproxyHandler.ServeHTTP(w, r) + return + } + r, resp := proxy.filterRequest(r, ctx) + + if resp == nil { + removeProxyHeaders(ctx, r) + resp, err = ctx.RoundTrip(r) + if err != nil { + ctx.Error = err + resp = proxy.filterResponse(nil, ctx) + if resp == nil { + ctx.Logf("error read response %v %v:", r.URL.Host, err.Error()) + http.Error(w, err.Error(), 500) + return + } + } + ctx.Logf("Received response %v", resp.Status) + } + origBody := resp.Body + resp = proxy.filterResponse(resp, ctx) + defer origBody.Close() + ctx.Logf("Copying response to client %v [%d]", resp.Status, resp.StatusCode) + // http.ResponseWriter will take care of filling the correct response length + // Setting it now, might impose wrong value, contradicting the actual new + // body the user returned. + // We keep the original body to remove the header only if things changed. + // This will prevent problems with HEAD requests where there's no body, yet, + // the Content-Length header should be set. + if origBody != resp.Body { + resp.Header.Del("Content-Length") + } + copyHeaders(w.Header(), resp.Header) + w.WriteHeader(resp.StatusCode) + nr, err := io.Copy(w, resp.Body) + if err := resp.Body.Close(); err != nil { + ctx.Warnf("Can't close response body %v", err) + } + ctx.Logf("Copied %v bytes to client error=%v", nr, err) + } +} + +// New proxy server, logs to StdErr by default +func NewProxyHttpServer() *ProxyHttpServer { + proxy := ProxyHttpServer{ + Logger: log.New(os.Stderr, "", log.LstdFlags), + reqHandlers: []ReqHandler{}, + respHandlers: []RespHandler{}, + httpsHandlers: []HttpsHandler{}, + NonproxyHandler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + http.Error(w, "This is a proxy server. Does not respond to non-proxy requests.", 500) + }), + Tr: &http.Transport{TLSClientConfig: tlsClientSkipVerify, + Proxy: http.ProxyFromEnvironment}, + } + proxy.ConnectDial = dialerFromEnv(&proxy) + return &proxy +} diff --git a/vendor/github.com/elazarl/goproxy/proxy_test.go b/vendor/github.com/elazarl/goproxy/proxy_test.go new file mode 100644 index 00000000..45c57552 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/proxy_test.go @@ -0,0 +1,836 @@ +package goproxy_test + +import ( + "bufio" + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "image" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "os/exec" + "strings" + "testing" + + "github.com/elazarl/goproxy" + "github.com/elazarl/goproxy/ext/image" +) + +var acceptAllCerts = &tls.Config{InsecureSkipVerify: true} + +var noProxyClient = &http.Client{Transport: &http.Transport{TLSClientConfig: acceptAllCerts}} + +var https = httptest.NewTLSServer(nil) +var srv = httptest.NewServer(nil) +var fs = httptest.NewServer(http.FileServer(http.Dir("."))) + +type QueryHandler struct{} + +func (QueryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if err := req.ParseForm(); err != nil { + panic(err) + } + io.WriteString(w, req.Form.Get("result")) +} + +func init() { + http.DefaultServeMux.Handle("/bobo", ConstantHanlder("bobo")) + http.DefaultServeMux.Handle("/query", QueryHandler{}) +} + +type ConstantHanlder string + +func (h ConstantHanlder) ServeHTTP(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, string(h)) +} + +func get(url string, client *http.Client) ([]byte, error) { + resp, err := client.Get(url) + if err != nil { + return nil, err + } + txt, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + return txt, nil +} + +func getOrFail(url string, client *http.Client, t *testing.T) []byte { + txt, err := get(url, client) + if err != nil { + t.Fatal("Can't fetch url", url, err) + } + return txt +} + +func localFile(url string) string { return fs.URL + "/" + url } +func localTls(url string) string { return https.URL + url } + +func TestSimpleHttpReqWithProxy(t *testing.T) { + client, s := oneShotProxy(goproxy.NewProxyHttpServer(), t) + defer s.Close() + + if r := string(getOrFail(srv.URL+"/bobo", client, t)); r != "bobo" { + t.Error("proxy server does not serve constant handlers", r) + } + if r := string(getOrFail(srv.URL+"/bobo", client, t)); r != "bobo" { + t.Error("proxy server does not serve constant handlers", r) + } + + if string(getOrFail(https.URL+"/bobo", client, t)) != "bobo" { + t.Error("TLS server does not serve constant handlers, when proxy is used") + } +} + +func oneShotProxy(proxy *goproxy.ProxyHttpServer, t *testing.T) (client *http.Client, s *httptest.Server) { + s = httptest.NewServer(proxy) + + proxyUrl, _ := url.Parse(s.URL) + tr := &http.Transport{TLSClientConfig: acceptAllCerts, Proxy: http.ProxyURL(proxyUrl)} + client = &http.Client{Transport: tr} + return +} + +func TestSimpleHook(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest(goproxy.SrcIpIs("127.0.0.1")).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + req.URL.Path = "/bobo" + return req, nil + }) + client, l := oneShotProxy(proxy, t) + defer l.Close() + + if result := string(getOrFail(srv.URL+("/momo"), client, t)); result != "bobo" { + t.Error("Redirecting all requests from 127.0.0.1 to bobo, didn't work." + + " (Might break if Go's client sets RemoteAddr to IPv6 address). Got: " + + result) + } +} + +func TestAlwaysHook(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + req.URL.Path = "/bobo" + return req, nil + }) + client, l := oneShotProxy(proxy, t) + defer l.Close() + + if result := string(getOrFail(srv.URL+("/momo"), client, t)); result != "bobo" { + t.Error("Redirecting all requests from 127.0.0.1 to bobo, didn't work." + + " (Might break if Go's client sets RemoteAddr to IPv6 address). Got: " + + result) + } +} + +func TestReplaceResponse(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + resp.StatusCode = http.StatusOK + resp.Body = ioutil.NopCloser(bytes.NewBufferString("chico")) + return resp + }) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + if result := string(getOrFail(srv.URL+("/momo"), client, t)); result != "chico" { + t.Error("hooked response, should be chico, instead:", result) + } +} + +func TestReplaceReponseForUrl(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnResponse(goproxy.UrlIs("/koko")).DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + resp.StatusCode = http.StatusOK + resp.Body = ioutil.NopCloser(bytes.NewBufferString("chico")) + return resp + }) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + if result := string(getOrFail(srv.URL+("/koko"), client, t)); result != "chico" { + t.Error("hooked 'koko', should be chico, instead:", result) + } + if result := string(getOrFail(srv.URL+("/bobo"), client, t)); result != "bobo" { + t.Error("still, bobo should stay as usual, instead:", result) + } +} + +func TestOneShotFileServer(t *testing.T) { + client, l := oneShotProxy(goproxy.NewProxyHttpServer(), t) + defer l.Close() + + file := "test_data/panda.png" + info, err := os.Stat(file) + if err != nil { + t.Fatal("Cannot find", file) + } + if resp, err := client.Get(fs.URL + "/" + file); err == nil { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal("got", string(b)) + } + if int64(len(b)) != info.Size() { + t.Error("Expected Length", file, info.Size(), "actually", len(b), "starts", string(b[:10])) + } + } else { + t.Fatal("Cannot read from fs server", err) + } +} + +func TestContentType(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnResponse(goproxy.ContentTypeIs("image/png")).DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + resp.Header.Set("X-Shmoopi", "1") + return resp + }) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + for _, file := range []string{"test_data/panda.png", "test_data/football.png"} { + if resp, err := client.Get(localFile(file)); err != nil || resp.Header.Get("X-Shmoopi") != "1" { + if err == nil { + t.Error("pngs should have X-Shmoopi header = 1, actually", resp.Header.Get("X-Shmoopi")) + } else { + t.Error("error reading png", err) + } + } + } + + file := "baby.jpg" + if resp, err := client.Get(localFile(file)); err != nil || resp.Header.Get("X-Shmoopi") != "" { + if err == nil { + t.Error("Non png images should NOT have X-Shmoopi header at all", resp.Header.Get("X-Shmoopi")) + } else { + t.Error("error reading png", err) + } + } +} + +func getImage(file string, t *testing.T) image.Image { + newimage, err := ioutil.ReadFile(file) + if err != nil { + t.Fatal("Cannot read file", file, err) + } + img, _, err := image.Decode(bytes.NewReader(newimage)) + if err != nil { + t.Fatal("Cannot decode image", file, err) + } + return img +} + +func readAll(r io.Reader, t *testing.T) []byte { + b, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal("Cannot read", err) + } + return b +} +func readFile(file string, t *testing.T) []byte { + b, err := ioutil.ReadFile(file) + if err != nil { + t.Fatal("Cannot read", err) + } + return b +} +func fatalOnErr(err error, msg string, t *testing.T) { + if err != nil { + t.Fatal(msg, err) + } +} +func panicOnErr(err error, msg string) { + if err != nil { + println(err.Error() + ":-" + msg) + os.Exit(-1) + } +} + +func compareImage(eImg, aImg image.Image, t *testing.T) { + if eImg.Bounds().Dx() != aImg.Bounds().Dx() || eImg.Bounds().Dy() != aImg.Bounds().Dy() { + t.Error("image sizes different") + return + } + for i := 0; i < eImg.Bounds().Dx(); i++ { + for j := 0; j < eImg.Bounds().Dy(); j++ { + er, eg, eb, ea := eImg.At(i, j).RGBA() + ar, ag, ab, aa := aImg.At(i, j).RGBA() + if er != ar || eg != ag || eb != ab || ea != aa { + t.Error("images different at", i, j, "vals\n", er, eg, eb, ea, "\n", ar, ag, ab, aa, aa) + return + } + } + } +} + +func TestConstantImageHandler(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + //panda := getImage("panda.png", t) + football := getImage("test_data/football.png", t) + proxy.OnResponse().Do(goproxy_image.HandleImage(func(img image.Image, ctx *goproxy.ProxyCtx) image.Image { + return football + })) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + resp, err := client.Get(localFile("test_data/panda.png")) + if err != nil { + t.Fatal("Cannot get panda.png", err) + } + + img, _, err := image.Decode(resp.Body) + if err != nil { + t.Error("decode", err) + } else { + compareImage(football, img, t) + } +} + +func TestImageHandler(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + football := getImage("test_data/football.png", t) + + proxy.OnResponse(goproxy.UrlIs("/test_data/panda.png")).Do(goproxy_image.HandleImage(func(img image.Image, ctx *goproxy.ProxyCtx) image.Image { + return football + })) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + resp, err := client.Get(localFile("test_data/panda.png")) + if err != nil { + t.Fatal("Cannot get panda.png", err) + } + + img, _, err := image.Decode(resp.Body) + if err != nil { + t.Error("decode", err) + } else { + compareImage(football, img, t) + } + + // and again + resp, err = client.Get(localFile("test_data/panda.png")) + if err != nil { + t.Fatal("Cannot get panda.png", err) + } + + img, _, err = image.Decode(resp.Body) + if err != nil { + t.Error("decode", err) + } else { + compareImage(football, img, t) + } +} + +func TestChangeResp(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + resp.Body.Read([]byte{0}) + resp.Body = ioutil.NopCloser(new(bytes.Buffer)) + return resp + }) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + resp, err := client.Get(localFile("test_data/panda.png")) + if err != nil { + t.Fatal(err) + } + ioutil.ReadAll(resp.Body) + _, err = client.Get(localFile("/bobo")) + if err != nil { + t.Fatal(err) + } +} +func TestReplaceImage(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + + panda := getImage("test_data/panda.png", t) + football := getImage("test_data/football.png", t) + + proxy.OnResponse(goproxy.UrlIs("/test_data/panda.png")).Do(goproxy_image.HandleImage(func(img image.Image, ctx *goproxy.ProxyCtx) image.Image { + return football + })) + proxy.OnResponse(goproxy.UrlIs("/test_data/football.png")).Do(goproxy_image.HandleImage(func(img image.Image, ctx *goproxy.ProxyCtx) image.Image { + return panda + })) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + imgByPandaReq, _, err := image.Decode(bytes.NewReader(getOrFail(localFile("test_data/panda.png"), client, t))) + fatalOnErr(err, "decode panda", t) + compareImage(football, imgByPandaReq, t) + + imgByFootballReq, _, err := image.Decode(bytes.NewReader(getOrFail(localFile("test_data/football.png"), client, t))) + fatalOnErr(err, "decode football", t) + compareImage(panda, imgByFootballReq, t) +} + +func getCert(c *tls.Conn, t *testing.T) []byte { + if err := c.Handshake(); err != nil { + t.Fatal("cannot handshake", err) + } + return c.ConnectionState().PeerCertificates[0].Raw +} + +func TestSimpleMitm(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest(goproxy.ReqHostIs(https.Listener.Addr().String())).HandleConnect(goproxy.AlwaysMitm) + proxy.OnRequest(goproxy.ReqHostIs("no such host exists")).HandleConnect(goproxy.AlwaysMitm) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + c, err := tls.Dial("tcp", https.Listener.Addr().String(), &tls.Config{InsecureSkipVerify: true}) + if err != nil { + t.Fatal("cannot dial to tcp server", err) + } + origCert := getCert(c, t) + c.Close() + + c2, err := net.Dial("tcp", l.Listener.Addr().String()) + if err != nil { + t.Fatal("dialing to proxy", err) + } + creq, err := http.NewRequest("CONNECT", https.URL, nil) + //creq,err := http.NewRequest("CONNECT","https://google.com:443",nil) + if err != nil { + t.Fatal("create new request", creq) + } + creq.Write(c2) + c2buf := bufio.NewReader(c2) + resp, err := http.ReadResponse(c2buf, creq) + if err != nil || resp.StatusCode != 200 { + t.Fatal("Cannot CONNECT through proxy", err) + } + c2tls := tls.Client(c2, &tls.Config{InsecureSkipVerify: true}) + proxyCert := getCert(c2tls, t) + + if bytes.Equal(proxyCert, origCert) { + t.Errorf("Certificate after mitm is not different\n%v\n%v", + base64.StdEncoding.EncodeToString(origCert), + base64.StdEncoding.EncodeToString(proxyCert)) + } + + if resp := string(getOrFail(https.URL+"/bobo", client, t)); resp != "bobo" { + t.Error("Wrong response when mitm", resp, "expected bobo") + } + if resp := string(getOrFail(https.URL+"/query?result=bar", client, t)); resp != "bar" { + t.Error("Wrong response when mitm", resp, "expected bar") + } +} + +func TestConnectHandler(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + althttps := httptest.NewTLSServer(ConstantHanlder("althttps")) + proxy.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) { + u, _ := url.Parse(althttps.URL) + return goproxy.OkConnect, u.Host + }) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + if resp := string(getOrFail(https.URL+"/alturl", client, t)); resp != "althttps" { + t.Error("Proxy should redirect CONNECT requests to local althttps server, expected 'althttps' got ", resp) + } +} + +func TestMitmIsFiltered(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + //proxy.Verbose = true + proxy.OnRequest(goproxy.ReqHostIs(https.Listener.Addr().String())).HandleConnect(goproxy.AlwaysMitm) + proxy.OnRequest(goproxy.UrlIs("/momo")).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + return nil, goproxy.TextResponse(req, "koko") + }) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + if resp := string(getOrFail(https.URL+"/momo", client, t)); resp != "koko" { + t.Error("Proxy should capture /momo to be koko and not", resp) + } + + if resp := string(getOrFail(https.URL+"/bobo", client, t)); resp != "bobo" { + t.Error("But still /bobo should be bobo and not", resp) + } +} + +func TestFirstHandlerMatches(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + return nil, goproxy.TextResponse(req, "koko") + }) + proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + panic("should never get here, previous response is no null") + }) + + client, l := oneShotProxy(proxy, t) + defer l.Close() + + if resp := string(getOrFail(srv.URL+"/", client, t)); resp != "koko" { + t.Error("should return always koko and not", resp) + } +} + +func constantHttpServer(content []byte) (addr string) { + l, err := net.Listen("tcp", "localhost:0") + panicOnErr(err, "listen") + go func() { + c, err := l.Accept() + panicOnErr(err, "accept") + buf := bufio.NewReader(c) + _, err = http.ReadRequest(buf) + panicOnErr(err, "readReq") + c.Write(content) + c.Close() + l.Close() + }() + return l.Addr().String() +} + +func TestIcyResponse(t *testing.T) { + // TODO: fix this test + return // skip for now + s := constantHttpServer([]byte("ICY 200 OK\r\n\r\nblablabla")) + proxy := goproxy.NewProxyHttpServer() + proxy.Verbose = true + _, l := oneShotProxy(proxy, t) + defer l.Close() + req, err := http.NewRequest("GET", "http://"+s, nil) + panicOnErr(err, "newReq") + proxyip := l.URL[len("http://"):] + println("got ip: " + proxyip) + c, err := net.Dial("tcp", proxyip) + panicOnErr(err, "dial") + defer c.Close() + req.WriteProxy(c) + raw, err := ioutil.ReadAll(c) + panicOnErr(err, "readAll") + if string(raw) != "ICY 200 OK\r\n\r\nblablabla" { + t.Error("Proxy did not send the malformed response received") + } +} + +type VerifyNoProxyHeaders struct { + *testing.T +} + +func (v VerifyNoProxyHeaders) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Connection") != "" || r.Header.Get("Proxy-Connection") != "" || + r.Header.Get("Proxy-Authenticate") != "" || r.Header.Get("Proxy-Authorization") != "" { + v.Error("Got Connection header from goproxy", r.Header) + } +} + +func TestNoProxyHeaders(t *testing.T) { + s := httptest.NewServer(VerifyNoProxyHeaders{t}) + client, l := oneShotProxy(goproxy.NewProxyHttpServer(), t) + defer l.Close() + req, err := http.NewRequest("GET", s.URL, nil) + panicOnErr(err, "bad request") + req.Header.Add("Connection", "close") + req.Header.Add("Proxy-Connection", "close") + req.Header.Add("Proxy-Authenticate", "auth") + req.Header.Add("Proxy-Authorization", "auth") + client.Do(req) +} + +func TestNoProxyHeadersHttps(t *testing.T) { + s := httptest.NewTLSServer(VerifyNoProxyHeaders{t}) + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) + client, l := oneShotProxy(proxy, t) + defer l.Close() + req, err := http.NewRequest("GET", s.URL, nil) + panicOnErr(err, "bad request") + req.Header.Add("Connection", "close") + req.Header.Add("Proxy-Connection", "close") + client.Do(req) +} + +func TestHeadReqHasContentLength(t *testing.T) { + client, l := oneShotProxy(goproxy.NewProxyHttpServer(), t) + defer l.Close() + + resp, err := client.Head(localFile("test_data/panda.png")) + panicOnErr(err, "resp to HEAD") + if resp.Header.Get("Content-Length") == "" { + t.Error("Content-Length should exist on HEAD requests") + } +} + +func TestChunkedResponse(t *testing.T) { + l, err := net.Listen("tcp", ":10234") + panicOnErr(err, "listen") + defer l.Close() + go func() { + for i := 0; i < 2; i++ { + c, err := l.Accept() + panicOnErr(err, "accept") + _, err = http.ReadRequest(bufio.NewReader(c)) + panicOnErr(err, "readrequest") + io.WriteString(c, "HTTP/1.1 200 OK\r\n"+ + "Content-Type: text/plain\r\n"+ + "Transfer-Encoding: chunked\r\n\r\n"+ + "25\r\n"+ + "This is the data in the first chunk\r\n\r\n"+ + "1C\r\n"+ + "and this is the second one\r\n\r\n"+ + "3\r\n"+ + "con\r\n"+ + "8\r\n"+ + "sequence\r\n0\r\n\r\n") + c.Close() + } + }() + + c, err := net.Dial("tcp", "localhost:10234") + panicOnErr(err, "dial") + defer c.Close() + req, _ := http.NewRequest("GET", "/", nil) + req.Write(c) + resp, err := http.ReadResponse(bufio.NewReader(c), req) + panicOnErr(err, "readresp") + b, err := ioutil.ReadAll(resp.Body) + panicOnErr(err, "readall") + expected := "This is the data in the first chunk\r\nand this is the second one\r\nconsequence" + if string(b) != expected { + t.Errorf("Got `%v` expected `%v`", string(b), expected) + } + + proxy := goproxy.NewProxyHttpServer() + proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + panicOnErr(ctx.Error, "error reading output") + b, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + panicOnErr(err, "readall onresp") + if enc := resp.Header.Get("Transfer-Encoding"); enc != "" { + t.Fatal("Chunked response should be received as plaintext", enc) + } + resp.Body = ioutil.NopCloser(bytes.NewBufferString(strings.Replace(string(b), "e", "E", -1))) + return resp + }) + + client, s := oneShotProxy(proxy, t) + defer s.Close() + + resp, err = client.Get("http://localhost:10234/") + panicOnErr(err, "client.Get") + b, err = ioutil.ReadAll(resp.Body) + panicOnErr(err, "readall proxy") + if string(b) != strings.Replace(expected, "e", "E", -1) { + t.Error("expected", expected, "w/ e->E. Got", string(b)) + } +} + +func TestGoproxyThroughProxy(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy2 := goproxy.NewProxyHttpServer() + doubleString := func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + b, err := ioutil.ReadAll(resp.Body) + panicOnErr(err, "readAll resp") + resp.Body = ioutil.NopCloser(bytes.NewBufferString(string(b) + " " + string(b))) + return resp + } + proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) + proxy.OnResponse().DoFunc(doubleString) + + _, l := oneShotProxy(proxy, t) + defer l.Close() + + proxy2.ConnectDial = proxy2.NewConnectDialToProxy(l.URL) + + client, l2 := oneShotProxy(proxy2, t) + defer l2.Close() + if r := string(getOrFail(https.URL+"/bobo", client, t)); r != "bobo bobo" { + t.Error("Expected bobo doubled twice, got", r) + } + +} + +func TestGoproxyHijackConnect(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest(goproxy.ReqHostIs(srv.Listener.Addr().String())). + HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) { + t.Logf("URL %+#v\nSTR %s", req.URL, req.URL.String()) + resp, err := http.Get("http:" + req.URL.String() + "/bobo") + panicOnErr(err, "http.Get(CONNECT url)") + panicOnErr(resp.Write(client), "resp.Write(client)") + resp.Body.Close() + client.Close() + }) + client, l := oneShotProxy(proxy, t) + defer l.Close() + proxyAddr := l.Listener.Addr().String() + conn, err := net.Dial("tcp", proxyAddr) + panicOnErr(err, "conn "+proxyAddr) + buf := bufio.NewReader(conn) + writeConnect(conn) + readConnectResponse(buf) + if txt := readResponse(buf); txt != "bobo" { + t.Error("Expected bobo for CONNECT /foo, got", txt) + } + + if r := string(getOrFail(https.URL+"/bobo", client, t)); r != "bobo" { + t.Error("Expected bobo would keep working with CONNECT", r) + } +} + +func readResponse(buf *bufio.Reader) string { + req, err := http.NewRequest("GET", srv.URL, nil) + panicOnErr(err, "NewRequest") + resp, err := http.ReadResponse(buf, req) + panicOnErr(err, "resp.Read") + defer resp.Body.Close() + txt, err := ioutil.ReadAll(resp.Body) + panicOnErr(err, "resp.Read") + return string(txt) +} + +func writeConnect(w io.Writer) { + req, err := http.NewRequest("CONNECT", srv.URL[len("http://"):], nil) + panicOnErr(err, "NewRequest") + req.Write(w) + panicOnErr(err, "req(CONNECT).Write") +} + +func readConnectResponse(buf *bufio.Reader) { + _, err := buf.ReadString('\n') + panicOnErr(err, "resp.Read connect resp") + _, err = buf.ReadString('\n') + panicOnErr(err, "resp.Read connect resp") +} + +func TestCurlMinusP(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) { + return goproxy.HTTPMitmConnect, host + }) + called := false + proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + called = true + return req, nil + }) + _, l := oneShotProxy(proxy, t) + defer l.Close() + cmd := exec.Command("curl", "-p", "-sS", "--proxy", l.URL, srv.URL+"/bobo") + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + if string(output) != "bobo" { + t.Error("Expected bobo, got", string(output)) + } + if !called { + t.Error("handler not called") + } +} + +func TestSelfRequest(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + _, l := oneShotProxy(proxy, t) + defer l.Close() + if !strings.Contains(string(getOrFail(l.URL, http.DefaultClient, t)), "non-proxy") { + t.Fatal("non proxy requests should fail") + } +} + +func TestHasGoproxyCA(t *testing.T) { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) + s := httptest.NewServer(proxy) + + proxyUrl, _ := url.Parse(s.URL) + goproxyCA := x509.NewCertPool() + goproxyCA.AddCert(goproxy.GoproxyCa.Leaf) + + tr := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: goproxyCA}, Proxy: http.ProxyURL(proxyUrl)} + client := &http.Client{Transport: tr} + + if resp := string(getOrFail(https.URL+"/bobo", client, t)); resp != "bobo" { + t.Error("Wrong response when mitm", resp, "expected bobo") + } +} + +func TestHttpsMitmURLRewrite(t *testing.T) { + scheme := "https" + + testCases := []struct { + Host string + RawPath string + AddOpaque bool + }{ + { + Host: "example.com", + RawPath: "/blah/v1/data/realtime", + AddOpaque: true, + }, + { + Host: "example.com:443", + RawPath: "/blah/v1/data/realtime?encodedURL=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile", + }, + { + Host: "example.com:443", + RawPath: "/blah/v1/data/realtime?unencodedURL=https://www.googleapis.com/auth/userinfo.profile", + }, + } + + for _, tc := range testCases { + proxy := goproxy.NewProxyHttpServer() + proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) + + proxy.OnRequest(goproxy.DstHostIs(tc.Host)).DoFunc( + func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + return nil, goproxy.TextResponse(req, "Dummy response") + }) + + client, s := oneShotProxy(proxy, t) + defer s.Close() + + fullURL := scheme + "://" + tc.Host + tc.RawPath + req, err := http.NewRequest("GET", fullURL, nil) + if err != nil { + t.Fatal(err) + } + + if tc.AddOpaque { + req.URL.Scheme = scheme + req.URL.Opaque = "//" + tc.Host + tc.RawPath + } + + resp, err := client.Do(req) + + if err != nil { + t.Fatal(err) + } + + b, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + t.Fatal(err) + } + + body := string(b) + if body != "Dummy response" { + t.Errorf("Expected proxy to return dummy body content but got %s", body) + } + + if resp.StatusCode != http.StatusAccepted { + t.Errorf("Expected status: %d, got: %d", http.StatusAccepted, resp.StatusCode) + } + } +} diff --git a/vendor/github.com/elazarl/goproxy/regretable/regretreader.go b/vendor/github.com/elazarl/goproxy/regretable/regretreader.go new file mode 100644 index 00000000..1458af58 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/regretable/regretreader.go @@ -0,0 +1,97 @@ +package regretable + +import ( + "io" +) + +// A RegretableReader will allow you to read from a reader, and then +// to "regret" reading it, and push back everything you've read. +// For example, +// rb := NewRegretableReader(bytes.NewBuffer([]byte{1,2,3})) +// var b = make([]byte,1) +// rb.Read(b) // b[0] = 1 +// rb.Regret() +// ioutil.ReadAll(rb.Read) // returns []byte{1,2,3},nil +type RegretableReader struct { + reader io.Reader + overflow bool + r, w int + buf []byte +} + +var defaultBufferSize = 500 + +// Same as RegretableReader, but allows closing the underlying reader +type RegretableReaderCloser struct { + RegretableReader + c io.Closer +} + +// Closes the underlying readCloser, you cannot regret after closing the stream +func (rbc *RegretableReaderCloser) Close() error { + return rbc.c.Close() +} + +// initialize a RegretableReaderCloser with underlying readCloser rc +func NewRegretableReaderCloser(rc io.ReadCloser) *RegretableReaderCloser { + return &RegretableReaderCloser{*NewRegretableReader(rc), rc} +} + +// initialize a RegretableReaderCloser with underlying readCloser rc +func NewRegretableReaderCloserSize(rc io.ReadCloser, size int) *RegretableReaderCloser { + return &RegretableReaderCloser{*NewRegretableReaderSize(rc, size), rc} +} + +// The next read from the RegretableReader will be as if the underlying reader +// was never read (or from the last point forget is called). +func (rb *RegretableReader) Regret() { + if rb.overflow { + panic("regretting after overflow makes no sense") + } + rb.r = 0 +} + +// Will "forget" everything read so far. +// rb := NewRegretableReader(bytes.NewBuffer([]byte{1,2,3})) +// var b = make([]byte,1) +// rb.Read(b) // b[0] = 1 +// rb.Forget() +// rb.Read(b) // b[0] = 2 +// rb.Regret() +// ioutil.ReadAll(rb.Read) // returns []byte{2,3},nil +func (rb *RegretableReader) Forget() { + if rb.overflow { + panic("forgetting after overflow makes no sense") + } + rb.r = 0 + rb.w = 0 +} + +// initialize a RegretableReader with underlying reader r, whose buffer is size bytes long +func NewRegretableReaderSize(r io.Reader, size int) *RegretableReader { + return &RegretableReader{reader: r, buf: make([]byte, size) } +} + +// initialize a RegretableReader with underlying reader r +func NewRegretableReader(r io.Reader) *RegretableReader { + return NewRegretableReaderSize(r, defaultBufferSize) +} + +// reads from the underlying reader. Will buffer all input until Regret is called. +func (rb *RegretableReader) Read(p []byte) (n int, err error) { + if rb.overflow { + return rb.reader.Read(p) + } + if rb.r < rb.w { + n = copy(p, rb.buf[rb.r:rb.w]) + rb.r += n + return + } + n, err = rb.reader.Read(p) + bn := copy(rb.buf[rb.w:], p[:n]) + rb.w, rb.r = rb.w + bn, rb.w + n + if bn < n { + rb.overflow = true + } + return +} diff --git a/vendor/github.com/elazarl/goproxy/regretable/regretreader_test.go b/vendor/github.com/elazarl/goproxy/regretable/regretreader_test.go new file mode 100644 index 00000000..55fa752b --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/regretable/regretreader_test.go @@ -0,0 +1,174 @@ +package regretable_test + +import ( + . "github.com/elazarl/goproxy/regretable" + "bytes" + "io" + "io/ioutil" + "strings" + "testing" +) + +func TestRegretableReader(t *testing.T) { + buf := new(bytes.Buffer) + mb := NewRegretableReader(buf) + word := "12345678" + buf.WriteString(word) + + fivebytes := make([]byte, 5) + mb.Read(fivebytes) + mb.Regret() + + s, _ := ioutil.ReadAll(mb) + if string(s) != word { + t.Errorf("Uncommited read is gone, [%d,%d] actual '%v' expected '%v'\n", len(s), len(word), string(s), word) + } +} + +func TestRegretableEmptyRead(t *testing.T) { + buf := new(bytes.Buffer) + mb := NewRegretableReader(buf) + word := "12345678" + buf.WriteString(word) + + zero := make([]byte, 0) + mb.Read(zero) + mb.Regret() + + s, err := ioutil.ReadAll(mb) + if string(s) != word { + t.Error("Uncommited read is gone, actual:", string(s), "expected:", word, "err:", err) + } +} + +func TestRegretableAlsoEmptyRead(t *testing.T) { + buf := new(bytes.Buffer) + mb := NewRegretableReader(buf) + word := "12345678" + buf.WriteString(word) + + one := make([]byte, 1) + zero := make([]byte, 0) + five := make([]byte, 5) + mb.Read(one) + mb.Read(zero) + mb.Read(five) + mb.Regret() + + s, _ := ioutil.ReadAll(mb) + if string(s) != word { + t.Error("Uncommited read is gone", string(s), "expected", word) + } +} + +func TestRegretableRegretBeforeRead(t *testing.T) { + buf := new(bytes.Buffer) + mb := NewRegretableReader(buf) + word := "12345678" + buf.WriteString(word) + + five := make([]byte, 5) + mb.Regret() + mb.Read(five) + + s, err := ioutil.ReadAll(mb) + if string(s) != "678" { + t.Error("Uncommited read is gone", string(s), len(string(s)), "expected", "678", len("678"), "err:", err) + } +} + +func TestRegretableFullRead(t *testing.T) { + buf := new(bytes.Buffer) + mb := NewRegretableReader(buf) + word := "12345678" + buf.WriteString(word) + + twenty := make([]byte, 20) + mb.Read(twenty) + mb.Regret() + + s, _ := ioutil.ReadAll(mb) + if string(s) != word { + t.Error("Uncommited read is gone", string(s), len(string(s)), "expected", word, len(word)) + } +} + +func assertEqual(t *testing.T, expected, actual string) { + if expected!=actual { + t.Fatal("Expected", expected, "actual", actual) + } +} + +func assertReadAll(t *testing.T, r io.Reader) string { + s, err := ioutil.ReadAll(r) + if err!=nil { + t.Fatal("error when reading", err) + } + return string(s) +} + +func TestRegretableRegretTwice(t *testing.T) { + buf := new(bytes.Buffer) + mb := NewRegretableReader(buf) + word := "12345678" + buf.WriteString(word) + + assertEqual(t, word, assertReadAll(t, mb)) + mb.Regret() + assertEqual(t, word, assertReadAll(t, mb)) + mb.Regret() + assertEqual(t, word, assertReadAll(t, mb)) +} + +type CloseCounter struct { + r io.Reader + closed int +} + +func (cc *CloseCounter) Read(b []byte) (int, error) { + return cc.r.Read(b) +} + +func (cc *CloseCounter) Close() error { + cc.closed++ + return nil +} + +func assert(t *testing.T, b bool, msg string) { + if !b { + t.Errorf("Assertion Error: %s", msg) + } +} + +func TestRegretableCloserSizeRegrets(t *testing.T) { + defer func() { + if r := recover(); r == nil || !strings.Contains(r.(string), "regret") { + t.Error("Did not panic when regretting overread buffer:", r) + } + }() + buf := new(bytes.Buffer) + buf.WriteString("123456") + mb := NewRegretableReaderCloserSize(ioutil.NopCloser(buf), 3) + mb.Read(make([]byte, 4)) + mb.Regret() +} + +func TestRegretableCloserRegretsClose(t *testing.T) { + buf := new(bytes.Buffer) + cc := &CloseCounter{buf, 0} + mb := NewRegretableReaderCloser(cc) + word := "12345678" + buf.WriteString(word) + + mb.Read([]byte{0}) + mb.Close() + if cc.closed != 1 { + t.Error("RegretableReaderCloser ignores Close") + } + mb.Regret() + mb.Close() + if cc.closed != 2 { + t.Error("RegretableReaderCloser does ignore Close after regret") + } + // TODO(elazar): return an error if client issues Close more than once after regret +} diff --git a/vendor/github.com/elazarl/goproxy/responses.go b/vendor/github.com/elazarl/goproxy/responses.go new file mode 100644 index 00000000..b304b882 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/responses.go @@ -0,0 +1,38 @@ +package goproxy + +import ( + "bytes" + "io/ioutil" + "net/http" +) + +// Will generate a valid http response to the given request the response will have +// the given contentType, and http status. +// Typical usage, refuse to process requests to local addresses: +// +// proxy.OnRequest(IsLocalHost()).DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request,*http.Response) { +// return nil,NewResponse(r,goproxy.ContentTypeHtml,http.StatusUnauthorized, +// `Can't use proxy for local addresses`) +// }) +func NewResponse(r *http.Request, contentType string, status int, body string) *http.Response { + resp := &http.Response{} + resp.Request = r + resp.TransferEncoding = r.TransferEncoding + resp.Header = make(http.Header) + resp.Header.Add("Content-Type", contentType) + resp.StatusCode = status + buf := bytes.NewBufferString(body) + resp.ContentLength = int64(buf.Len()) + resp.Body = ioutil.NopCloser(buf) + return resp +} + +const ( + ContentTypeText = "text/plain" + ContentTypeHtml = "text/html" +) + +// Alias for NewResponse(r,ContentTypeText,http.StatusAccepted,text) +func TextResponse(r *http.Request, text string) *http.Response { + return NewResponse(r, ContentTypeText, http.StatusAccepted, text) +} diff --git a/vendor/github.com/elazarl/goproxy/signer.go b/vendor/github.com/elazarl/goproxy/signer.go new file mode 100644 index 00000000..f6d99fc7 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/signer.go @@ -0,0 +1,87 @@ +package goproxy + +import ( + "crypto/rsa" + "crypto/sha1" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "math/big" + "net" + "runtime" + "sort" + "time" +) + +func hashSorted(lst []string) []byte { + c := make([]string, len(lst)) + copy(c, lst) + sort.Strings(c) + h := sha1.New() + for _, s := range c { + h.Write([]byte(s + ",")) + } + return h.Sum(nil) +} + +func hashSortedBigInt(lst []string) *big.Int { + rv := new(big.Int) + rv.SetBytes(hashSorted(lst)) + return rv +} + +var goproxySignerVersion = ":goroxy1" + +func signHost(ca tls.Certificate, hosts []string) (cert tls.Certificate, err error) { + var x509ca *x509.Certificate + + // Use the provided ca and not the global GoproxyCa for certificate generation. + if x509ca, err = x509.ParseCertificate(ca.Certificate[0]); err != nil { + return + } + start := time.Unix(0, 0) + end, err := time.Parse("2006-01-02", "2049-12-31") + if err != nil { + panic(err) + } + hash := hashSorted(append(hosts, goproxySignerVersion, ":"+runtime.Version())) + serial := new(big.Int) + serial.SetBytes(hash) + template := x509.Certificate{ + // TODO(elazar): instead of this ugly hack, just encode the certificate and hash the binary form. + SerialNumber: serial, + Issuer: x509ca.Subject, + Subject: pkix.Name{ + Organization: []string{"GoProxy untrusted MITM proxy Inc"}, + }, + NotBefore: start, + NotAfter: end, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + var csprng CounterEncryptorRand + if csprng, err = NewCounterEncryptorRandFromKey(ca.PrivateKey, hash); err != nil { + return + } + var certpriv *rsa.PrivateKey + if certpriv, err = rsa.GenerateKey(&csprng, 1024); err != nil { + return + } + var derBytes []byte + if derBytes, err = x509.CreateCertificate(&csprng, &template, x509ca, &certpriv.PublicKey, ca.PrivateKey); err != nil { + return + } + return tls.Certificate{ + Certificate: [][]byte{derBytes, ca.Certificate[0]}, + PrivateKey: certpriv, + }, nil +} diff --git a/vendor/github.com/elazarl/goproxy/signer_test.go b/vendor/github.com/elazarl/goproxy/signer_test.go new file mode 100644 index 00000000..d0e24d29 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/signer_test.go @@ -0,0 +1,87 @@ +package goproxy + +import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "strings" + "testing" + "time" +) + +func orFatal(msg string, err error, t *testing.T) { + if err != nil { + t.Fatal(msg, err) + } +} + +type ConstantHanlder string + +func (h ConstantHanlder) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(h)) +} + +func getBrowser(args []string) string { + for i, arg := range args { + if arg == "-browser" && i+1 < len(arg) { + return args[i+1] + } + if strings.HasPrefix(arg, "-browser=") { + return arg[len("-browser="):] + } + } + return "" +} + +func TestSingerTls(t *testing.T) { + cert, err := signHost(GoproxyCa, []string{"example.com", "1.1.1.1", "localhost"}) + orFatal("singHost", err, t) + cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) + orFatal("ParseCertificate", err, t) + expected := "key verifies with Go" + server := httptest.NewUnstartedServer(ConstantHanlder(expected)) + defer server.Close() + server.TLS = &tls.Config{Certificates: []tls.Certificate{cert, GoproxyCa}} + server.TLS.BuildNameToCertificate() + server.StartTLS() + certpool := x509.NewCertPool() + certpool.AddCert(GoproxyCa.Leaf) + tr := &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: certpool}, + } + asLocalhost := strings.Replace(server.URL, "127.0.0.1", "localhost", -1) + req, err := http.NewRequest("GET", asLocalhost, nil) + orFatal("NewRequest", err, t) + resp, err := tr.RoundTrip(req) + orFatal("RoundTrip", err, t) + txt, err := ioutil.ReadAll(resp.Body) + orFatal("ioutil.ReadAll", err, t) + if string(txt) != expected { + t.Errorf("Expected '%s' got '%s'", expected, string(txt)) + } + browser := getBrowser(os.Args) + if browser != "" { + exec.Command(browser, asLocalhost).Run() + time.Sleep(10 * time.Second) + } +} + +func TestSingerX509(t *testing.T) { + cert, err := signHost(GoproxyCa, []string{"example.com", "1.1.1.1", "localhost"}) + orFatal("singHost", err, t) + cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) + orFatal("ParseCertificate", err, t) + certpool := x509.NewCertPool() + certpool.AddCert(GoproxyCa.Leaf) + orFatal("VerifyHostname", cert.Leaf.VerifyHostname("example.com"), t) + orFatal("CheckSignatureFrom", cert.Leaf.CheckSignatureFrom(GoproxyCa.Leaf), t) + _, err = cert.Leaf.Verify(x509.VerifyOptions{ + DNSName: "example.com", + Roots: certpool, + }) + orFatal("Verify", err, t) +} diff --git a/vendor/github.com/elazarl/goproxy/test_data/baby.jpg b/vendor/github.com/elazarl/goproxy/test_data/baby.jpg new file mode 100644 index 00000000..c377bb8e Binary files /dev/null and b/vendor/github.com/elazarl/goproxy/test_data/baby.jpg differ diff --git a/vendor/github.com/elazarl/goproxy/test_data/football.png b/vendor/github.com/elazarl/goproxy/test_data/football.png new file mode 100644 index 00000000..7eefac18 Binary files /dev/null and b/vendor/github.com/elazarl/goproxy/test_data/football.png differ diff --git a/vendor/github.com/elazarl/goproxy/test_data/panda.png b/vendor/github.com/elazarl/goproxy/test_data/panda.png new file mode 100644 index 00000000..cdfdb5e6 Binary files /dev/null and b/vendor/github.com/elazarl/goproxy/test_data/panda.png differ diff --git a/vendor/github.com/elazarl/goproxy/transport/roundtripper.go b/vendor/github.com/elazarl/goproxy/transport/roundtripper.go new file mode 100644 index 00000000..3651ad86 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/transport/roundtripper.go @@ -0,0 +1,19 @@ +package transport +import "net/http" +type RoundTripper interface { + // RoundTrip executes a single HTTP transaction, returning + // the Response for the request req. RoundTrip should not + // attempt to interpret the response. In particular, + // RoundTrip must return err == nil if it obtained a response, + // regardless of the response's HTTP status code. A non-nil + // err should be reserved for failure to obtain a response. + // Similarly, RoundTrip should not attempt to handle + // higher-level protocol details such as redirects, + // authentication, or cookies. + // + // RoundTrip should not modify the request, except for + // consuming the Body. The request's URL and Header fields + // are guaranteed to be initialized. + RoundTrip(*http.Request) (*http.Response, error) + DetailedRoundTrip(*http.Request) (*RoundTripDetails, *http.Response, error) +} diff --git a/vendor/github.com/elazarl/goproxy/transport/transport.go b/vendor/github.com/elazarl/goproxy/transport/transport.go new file mode 100644 index 00000000..fc1c82b1 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/transport/transport.go @@ -0,0 +1,789 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// HTTP client implementation. See RFC 2616. +// +// This is the low-level Transport implementation of RoundTripper. +// The high-level interface is in client.go. + +// This file is DEPRECATED and keep solely for backward compatibility. + +package transport + +import ( + "net/http" + "bufio" + "compress/gzip" + "crypto/tls" + "encoding/base64" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/url" + "os" + "strings" + "sync" +) + +// DefaultTransport is the default implementation of Transport and is +// used by DefaultClient. It establishes a new network connection for +// each call to Do and uses HTTP proxies as directed by the +// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy) +// environment variables. +var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment} + +// DefaultMaxIdleConnsPerHost is the default value of Transport's +// MaxIdleConnsPerHost. +const DefaultMaxIdleConnsPerHost = 2 + +// Transport is an implementation of RoundTripper that supports http, +// https, and http proxies (for either http or https with CONNECT). +// Transport can also cache connections for future re-use. +type Transport struct { + lk sync.Mutex + idleConn map[string][]*persistConn + altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper + + // TODO: tunable on global max cached connections + // TODO: tunable on timeout on cached connections + // TODO: optional pipelining + + // Proxy specifies a function to return a proxy for a given + // Request. If the function returns a non-nil error, the + // request is aborted with the provided error. + // If Proxy is nil or returns a nil *URL, no proxy is used. + Proxy func(*http.Request) (*url.URL, error) + + // Dial specifies the dial function for creating TCP + // connections. + // If Dial is nil, net.Dial is used. + Dial func(net, addr string) (c net.Conn, err error) + + // TLSClientConfig specifies the TLS configuration to use with + // tls.Client. If nil, the default configuration is used. + TLSClientConfig *tls.Config + + DisableKeepAlives bool + DisableCompression bool + + // MaxIdleConnsPerHost, if non-zero, controls the maximum idle + // (keep-alive) to keep to keep per-host. If zero, + // DefaultMaxIdleConnsPerHost is used. + MaxIdleConnsPerHost int +} + +// ProxyFromEnvironment returns the URL of the proxy to use for a +// given request, as indicated by the environment variables +// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy). +// An error is returned if the proxy environment is invalid. +// A nil URL and nil error are returned if no proxy is defined in the +// environment, or a proxy should not be used for the given request. +func ProxyFromEnvironment(req *http.Request) (*url.URL, error) { + proxy := getenvEitherCase("HTTP_PROXY") + if proxy == "" { + return nil, nil + } + if !useProxy(canonicalAddr(req.URL)) { + return nil, nil + } + proxyURL, err := url.Parse(proxy) + if err != nil || proxyURL.Scheme == "" { + if u, err := url.Parse("http://" + proxy); err == nil { + proxyURL = u + err = nil + } + } + if err != nil { + return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) + } + return proxyURL, nil +} + +// ProxyURL returns a proxy function (for use in a Transport) +// that always returns the same URL. +func ProxyURL(fixedURL *url.URL) func(*http.Request) (*url.URL, error) { + return func(*http.Request) (*url.URL, error) { + return fixedURL, nil + } +} + +// transportRequest is a wrapper around a *Request that adds +// optional extra headers to write. +type transportRequest struct { + *http.Request // original request, not to be mutated + extra http.Header // extra headers to write, or nil +} + +func (tr *transportRequest) extraHeaders() http.Header { + if tr.extra == nil { + tr.extra = make(http.Header) + } + return tr.extra +} + +type RoundTripDetails struct { + Host string + TCPAddr *net.TCPAddr + IsProxy bool + Error error +} + +func (t *Transport) DetailedRoundTrip(req *http.Request) (details *RoundTripDetails, resp *http.Response, err error) { + if req.URL == nil { + return nil, nil, errors.New("http: nil Request.URL") + } + if req.Header == nil { + return nil, nil, errors.New("http: nil Request.Header") + } + if req.URL.Scheme != "http" && req.URL.Scheme != "https" { + t.lk.Lock() + var rt RoundTripper + if t.altProto != nil { + rt = t.altProto[req.URL.Scheme] + } + t.lk.Unlock() + if rt == nil { + return nil, nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme} + } + return rt.DetailedRoundTrip(req) + } + treq := &transportRequest{Request: req} + cm, err := t.connectMethodForRequest(treq) + if err != nil { + return nil, nil, err + } + + // Get the cached or newly-created connection to either the + // host (for http or https), the http proxy, or the http proxy + // pre-CONNECTed to https server. In any case, we'll be ready + // to send it requests. + pconn, err := t.getConn(cm) + if err != nil { + return nil, nil, err + } + + resp, err = pconn.roundTrip(treq) + return &RoundTripDetails{pconn.host, pconn.ip, pconn.isProxy, err}, resp, err +} + +// RoundTrip implements the RoundTripper interface. +func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + _, resp, err = t.DetailedRoundTrip(req) + return +} + +// RegisterProtocol registers a new protocol with scheme. +// The Transport will pass requests using the given scheme to rt. +// It is rt's responsibility to simulate HTTP request semantics. +// +// RegisterProtocol can be used by other packages to provide +// implementations of protocol schemes like "ftp" or "file". +func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { + if scheme == "http" || scheme == "https" { + panic("protocol " + scheme + " already registered") + } + t.lk.Lock() + defer t.lk.Unlock() + if t.altProto == nil { + t.altProto = make(map[string]RoundTripper) + } + if _, exists := t.altProto[scheme]; exists { + panic("protocol " + scheme + " already registered") + } + t.altProto[scheme] = rt +} + +// CloseIdleConnections closes any connections which were previously +// connected from previous requests but are now sitting idle in +// a "keep-alive" state. It does not interrupt any connections currently +// in use. +func (t *Transport) CloseIdleConnections() { + t.lk.Lock() + defer t.lk.Unlock() + if t.idleConn == nil { + return + } + for _, conns := range t.idleConn { + for _, pconn := range conns { + pconn.close() + } + } + t.idleConn = make(map[string][]*persistConn) +} + +// +// Private implementation past this point. +// + +func getenvEitherCase(k string) string { + if v := os.Getenv(strings.ToUpper(k)); v != "" { + return v + } + return os.Getenv(strings.ToLower(k)) +} + +func (t *Transport) connectMethodForRequest(treq *transportRequest) (*connectMethod, error) { + cm := &connectMethod{ + targetScheme: treq.URL.Scheme, + targetAddr: canonicalAddr(treq.URL), + } + if t.Proxy != nil { + var err error + cm.proxyURL, err = t.Proxy(treq.Request) + if err != nil { + return nil, err + } + } + return cm, nil +} + +// proxyAuth returns the Proxy-Authorization header to set +// on requests, if applicable. +func (cm *connectMethod) proxyAuth() string { + if cm.proxyURL == nil { + return "" + } + if u := cm.proxyURL.User; u != nil { + return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String())) + } + return "" +} + +// putIdleConn adds pconn to the list of idle persistent connections awaiting +// a new request. +// If pconn is no longer needed or not in a good state, putIdleConn +// returns false. +func (t *Transport) putIdleConn(pconn *persistConn) bool { + t.lk.Lock() + defer t.lk.Unlock() + if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 { + pconn.close() + return false + } + if pconn.isBroken() { + return false + } + key := pconn.cacheKey + max := t.MaxIdleConnsPerHost + if max == 0 { + max = DefaultMaxIdleConnsPerHost + } + if len(t.idleConn[key]) >= max { + pconn.close() + return false + } + t.idleConn[key] = append(t.idleConn[key], pconn) + return true +} + +func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) { + t.lk.Lock() + defer t.lk.Unlock() + if t.idleConn == nil { + t.idleConn = make(map[string][]*persistConn) + } + key := cm.String() + for { + pconns, ok := t.idleConn[key] + if !ok { + return nil + } + if len(pconns) == 1 { + pconn = pconns[0] + delete(t.idleConn, key) + } else { + // 2 or more cached connections; pop last + // TODO: queue? + pconn = pconns[len(pconns)-1] + t.idleConn[key] = pconns[0 : len(pconns)-1] + } + if !pconn.isBroken() { + return + } + } + return +} + +func (t *Transport) dial(network, addr string) (c net.Conn, raddr string, ip *net.TCPAddr, err error) { + if t.Dial != nil { + ip, err = net.ResolveTCPAddr("tcp", addr) + if err!=nil { + return + } + c, err = t.Dial(network, addr) + raddr = addr + return + } + addri, err := net.ResolveTCPAddr("tcp", addr) + if err!=nil { + return + } + c, err = net.DialTCP("tcp", nil, addri) + raddr = addr + ip = addri + return +} + +// getConn dials and creates a new persistConn to the target as +// specified in the connectMethod. This includes doing a proxy CONNECT +// and/or setting up TLS. If this doesn't return an error, the persistConn +// is ready to write requests to. +func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) { + if pc := t.getIdleConn(cm); pc != nil { + return pc, nil + } + + conn, raddr, ip, err := t.dial("tcp", cm.addr()) + if err != nil { + if cm.proxyURL != nil { + err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err) + } + return nil, err + } + + pa := cm.proxyAuth() + + pconn := &persistConn{ + t: t, + cacheKey: cm.String(), + conn: conn, + reqch: make(chan requestAndChan, 50), + host: raddr, + ip: ip, + } + + switch { + case cm.proxyURL == nil: + // Do nothing. + case cm.targetScheme == "http": + pconn.isProxy = true + if pa != "" { + pconn.mutateHeaderFunc = func(h http.Header) { + h.Set("Proxy-Authorization", pa) + } + } + case cm.targetScheme == "https": + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: cm.targetAddr}, + Host: cm.targetAddr, + Header: make(http.Header), + } + if pa != "" { + connectReq.Header.Set("Proxy-Authorization", pa) + } + connectReq.Write(conn) + + // Read response. + // Okay to use and discard buffered reader here, because + // TLS server will not speak until spoken to. + br := bufio.NewReader(conn) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + conn.Close() + return nil, err + } + if resp.StatusCode != 200 { + f := strings.SplitN(resp.Status, " ", 2) + conn.Close() + return nil, errors.New(f[1]) + } + } + + if cm.targetScheme == "https" { + // Initiate TLS and check remote host name against certificate. + conn = tls.Client(conn, t.TLSClientConfig) + if err = conn.(*tls.Conn).Handshake(); err != nil { + return nil, err + } + if t.TLSClientConfig == nil || !t.TLSClientConfig.InsecureSkipVerify { + if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != nil { + return nil, err + } + } + pconn.conn = conn + } + + pconn.br = bufio.NewReader(pconn.conn) + pconn.bw = bufio.NewWriter(pconn.conn) + go pconn.readLoop() + return pconn, nil +} + +// useProxy returns true if requests to addr should use a proxy, +// according to the NO_PROXY or no_proxy environment variable. +// addr is always a canonicalAddr with a host and port. +func useProxy(addr string) bool { + if len(addr) == 0 { + return true + } + host, _, err := net.SplitHostPort(addr) + if err != nil { + return false + } + if host == "localhost" { + return false + } + if ip := net.ParseIP(host); ip != nil { + if ip.IsLoopback() { + return false + } + } + + no_proxy := getenvEitherCase("NO_PROXY") + if no_proxy == "*" { + return false + } + + addr = strings.ToLower(strings.TrimSpace(addr)) + if hasPort(addr) { + addr = addr[:strings.LastIndex(addr, ":")] + } + + for _, p := range strings.Split(no_proxy, ",") { + p = strings.ToLower(strings.TrimSpace(p)) + if len(p) == 0 { + continue + } + if hasPort(p) { + p = p[:strings.LastIndex(p, ":")] + } + if addr == p || (p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:])) { + return false + } + } + return true +} + +// connectMethod is the map key (in its String form) for keeping persistent +// TCP connections alive for subsequent HTTP requests. +// +// A connect method may be of the following types: +// +// Cache key form Description +// ----------------- ------------------------- +// ||http|foo.com http directly to server, no proxy +// ||https|foo.com https directly to server, no proxy +// http://proxy.com|https|foo.com http to proxy, then CONNECT to foo.com +// http://proxy.com|http http to proxy, http to anywhere after that +// +// Note: no support to https to the proxy yet. +// +type connectMethod struct { + proxyURL *url.URL // nil for no proxy, else full proxy URL + targetScheme string // "http" or "https" + targetAddr string // Not used if proxy + http targetScheme (4th example in table) +} + +func (ck *connectMethod) String() string { + proxyStr := "" + if ck.proxyURL != nil { + proxyStr = ck.proxyURL.String() + } + return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|") +} + +// addr returns the first hop "host:port" to which we need to TCP connect. +func (cm *connectMethod) addr() string { + if cm.proxyURL != nil { + return canonicalAddr(cm.proxyURL) + } + return cm.targetAddr +} + +// tlsHost returns the host name to match against the peer's +// TLS certificate. +func (cm *connectMethod) tlsHost() string { + h := cm.targetAddr + if hasPort(h) { + h = h[:strings.LastIndex(h, ":")] + } + return h +} + +// persistConn wraps a connection, usually a persistent one +// (but may be used for non-keep-alive requests as well) +type persistConn struct { + t *Transport + cacheKey string // its connectMethod.String() + conn net.Conn + br *bufio.Reader // from conn + bw *bufio.Writer // to conn + reqch chan requestAndChan // written by roundTrip(); read by readLoop() + isProxy bool + + // mutateHeaderFunc is an optional func to modify extra + // headers on each outbound request before it's written. (the + // original Request given to RoundTrip is not modified) + mutateHeaderFunc func(http.Header) + + lk sync.Mutex // guards numExpectedResponses and broken + numExpectedResponses int + broken bool // an error has happened on this connection; marked broken so it's not reused. + + host string + ip *net.TCPAddr +} + +func (pc *persistConn) isBroken() bool { + pc.lk.Lock() + defer pc.lk.Unlock() + return pc.broken +} + +var remoteSideClosedFunc func(error) bool // or nil to use default + +func remoteSideClosed(err error) bool { + if err == io.EOF { + return true + } + if remoteSideClosedFunc != nil { + return remoteSideClosedFunc(err) + } + return false +} + +func (pc *persistConn) readLoop() { + alive := true + var lastbody io.ReadCloser // last response body, if any, read on this connection + + for alive { + pb, err := pc.br.Peek(1) + + pc.lk.Lock() + if pc.numExpectedResponses == 0 { + pc.closeLocked() + pc.lk.Unlock() + if len(pb) > 0 { + log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v", + string(pb), err) + } + return + } + pc.lk.Unlock() + + rc := <-pc.reqch + + // Advance past the previous response's body, if the + // caller hasn't done so. + if lastbody != nil { + lastbody.Close() // assumed idempotent + lastbody = nil + } + resp, err := http.ReadResponse(pc.br, rc.req) + + if err != nil { + pc.close() + } else { + hasBody := rc.req.Method != "HEAD" && resp.ContentLength != 0 + if rc.addedGzip && hasBody && resp.Header.Get("Content-Encoding") == "gzip" { + resp.Header.Del("Content-Encoding") + resp.Header.Del("Content-Length") + resp.ContentLength = -1 + gzReader, zerr := gzip.NewReader(resp.Body) + if zerr != nil { + pc.close() + err = zerr + } else { + resp.Body = &readFirstCloseBoth{&discardOnCloseReadCloser{gzReader}, resp.Body} + } + } + resp.Body = &bodyEOFSignal{body: resp.Body} + } + + if err != nil || resp.Close || rc.req.Close { + alive = false + } + + hasBody := resp != nil && resp.ContentLength != 0 + var waitForBodyRead chan bool + if alive { + if hasBody { + lastbody = resp.Body + waitForBodyRead = make(chan bool) + resp.Body.(*bodyEOFSignal).fn = func() { + if !pc.t.putIdleConn(pc) { + alive = false + } + waitForBodyRead <- true + } + } else { + // When there's no response body, we immediately + // reuse the TCP connection (putIdleConn), but + // we need to prevent ClientConn.Read from + // closing the Response.Body on the next + // loop, otherwise it might close the body + // before the client code has had a chance to + // read it (even though it'll just be 0, EOF). + lastbody = nil + + if !pc.t.putIdleConn(pc) { + alive = false + } + } + } + + rc.ch <- responseAndError{resp, err} + + // Wait for the just-returned response body to be fully consumed + // before we race and peek on the underlying bufio reader. + if waitForBodyRead != nil { + <-waitForBodyRead + } + } +} + +type responseAndError struct { + res *http.Response + err error +} + +type requestAndChan struct { + req *http.Request + ch chan responseAndError + + // did the Transport (as opposed to the client code) add an + // Accept-Encoding gzip header? only if it we set it do + // we transparently decode the gzip. + addedGzip bool +} + +func (pc *persistConn) roundTrip(req *transportRequest) (resp *http.Response, err error) { + if pc.mutateHeaderFunc != nil { + panic("mutateHeaderFunc not supported in modified Transport") + pc.mutateHeaderFunc(req.extraHeaders()) + } + + // Ask for a compressed version if the caller didn't set their + // own value for Accept-Encoding. We only attempted to + // uncompress the gzip stream if we were the layer that + // requested it. + requestedGzip := false + if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" { + // Request gzip only, not deflate. Deflate is ambiguous and + // not as universally supported anyway. + // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 + requestedGzip = true + req.extraHeaders().Set("Accept-Encoding", "gzip") + } + + pc.lk.Lock() + pc.numExpectedResponses++ + pc.lk.Unlock() + + // orig: err = req.Request.write(pc.bw, pc.isProxy, req.extra) + if pc.isProxy { + err = req.Request.WriteProxy(pc.bw) + } else { + err = req.Request.Write(pc.bw) + } + if err != nil { + pc.close() + return + } + pc.bw.Flush() + + ch := make(chan responseAndError, 1) + pc.reqch <- requestAndChan{req.Request, ch, requestedGzip} + re := <-ch + pc.lk.Lock() + pc.numExpectedResponses-- + pc.lk.Unlock() + + return re.res, re.err +} + +func (pc *persistConn) close() { + pc.lk.Lock() + defer pc.lk.Unlock() + pc.closeLocked() +} + +func (pc *persistConn) closeLocked() { + pc.broken = true + pc.conn.Close() + pc.mutateHeaderFunc = nil +} + +var portMap = map[string]string{ + "http": "80", + "https": "443", +} + +// canonicalAddr returns url.Host but always with a ":port" suffix +func canonicalAddr(url *url.URL) string { + addr := url.Host + if !hasPort(addr) { + return addr + ":" + portMap[url.Scheme] + } + return addr +} + +func responseIsKeepAlive(res *http.Response) bool { + // TODO: implement. for now just always shutting down the connection. + return false +} + +// bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most +// once, right before the final Read() or Close() call returns, but after +// EOF has been seen. +type bodyEOFSignal struct { + body io.ReadCloser + fn func() + isClosed bool +} + +func (es *bodyEOFSignal) Read(p []byte) (n int, err error) { + n, err = es.body.Read(p) + if es.isClosed && n > 0 { + panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725") + } + if err == io.EOF && es.fn != nil { + es.fn() + es.fn = nil + } + return +} + +func (es *bodyEOFSignal) Close() (err error) { + if es.isClosed { + return nil + } + es.isClosed = true + err = es.body.Close() + if err == nil && es.fn != nil { + es.fn() + es.fn = nil + } + return +} + +type readFirstCloseBoth struct { + io.ReadCloser + io.Closer +} + +func (r *readFirstCloseBoth) Close() error { + if err := r.ReadCloser.Close(); err != nil { + r.Closer.Close() + return err + } + if err := r.Closer.Close(); err != nil { + return err + } + return nil +} + +// discardOnCloseReadCloser consumes all its input on Close. +type discardOnCloseReadCloser struct { + io.ReadCloser +} + +func (d *discardOnCloseReadCloser) Close() error { + io.Copy(ioutil.Discard, d.ReadCloser) // ignore errors; likely invalid or already closed + return d.ReadCloser.Close() +} diff --git a/vendor/github.com/elazarl/goproxy/transport/util.go b/vendor/github.com/elazarl/goproxy/transport/util.go new file mode 100644 index 00000000..af0eda1e --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/transport/util.go @@ -0,0 +1,15 @@ +package transport + +import ( + "fmt" + "strings" +) + +type badStringError struct { + what string + str string +} + +func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) } + +func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } diff --git a/vendor/github.com/google/go-github/.gitignore b/vendor/github.com/google/go-github/.gitignore new file mode 100644 index 00000000..3515c4b9 --- /dev/null +++ b/vendor/github.com/google/go-github/.gitignore @@ -0,0 +1,2 @@ +*.test +coverage.out diff --git a/vendor/github.com/google/go-github/.travis.yml b/vendor/github.com/google/go-github/.travis.yml new file mode 100644 index 00000000..f575d459 --- /dev/null +++ b/vendor/github.com/google/go-github/.travis.yml @@ -0,0 +1,29 @@ +sudo: false +language: go +go: + - 1.9.x + - 1.8.x + - 1.7.x + - master +matrix: + allow_failures: + - go: master + fast_finish: true +env: + secure: "IrnPmy/rkIP6Nrbqji+u7MCAibQlA6WvPLEllmDQ2yZP/uIe3wLwwYbTu9BOkgzoLA+f8PA6u3pp/RhY/rtaM4NzHAO2nVfGIv9UHUQ3NGq0DYS6rODjVKhq7vkhELoagRewyqFVN4rE0LnExkknRMgjQfRke6/DA7u7Xm8JyhY=" # COVERALLS_TOKEN +install: + - go get golang.org/x/tools/cmd/cover + - go get github.com/mattn/goveralls + - # Do not install go-github yet, since we want it to happen inside the script step. +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d -s .) + - go generate -x ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) # Check that go generate ./... produces a zero diff; clean up any changes afterwards. + - go tool vet . + - go test -v -race ./... + - go test -v -tags=integration -run=^$ ./test/integration # Check that integration test builds successfully, but don't run any of the tests (they hit live GitHub API). + + # Generate test coverage report. This must be after all other tests. + #- rm github/github-accessors.go # exclude generated code + #- go test -v -covermode=count -coverprofile=coverage.out ./github + #- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN diff --git a/vendor/github.com/google/go-github/AUTHORS b/vendor/github.com/google/go-github/AUTHORS new file mode 100644 index 00000000..548ff158 --- /dev/null +++ b/vendor/github.com/google/go-github/AUTHORS @@ -0,0 +1,171 @@ +# This is the official list of go-github authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control or +# https://github.com/google/go-github/graphs/contributors. +# +# Authors who wish to be recognized in this file should add themselves (or +# their employer, as appropriate). + +178inaba +Abhinav Gupta +Ahmed Hagy +Ainsley Chong +Akeda Bagus +Alec Thomas +Aleks Clark +Alex Bramley +Alexander Harkness +Allen Sun +Amey Sakhadeo +Andreas Garnæs +Andrew Ryabchun +Andy Hume +Andy Lindeman +Anshuman Bhartiya +Antoine Pelisse +Anubha Kushwaha +Aravind +Arıl Bozoluk +Austin Dizzy +Beshr Kayali +Beyang Liu +Billy Lynch +Björn Häuser +Brad Harris +Bradley Falzon +Brian Egizi +Bryan Boreham +Cami Diez +Carlos Alexandro Becker +chandresh-pancholi +Charlie Yan +Chris King +Chris Roche +Chris Schaefer +Christoph Sassenberg +Colin Misare +Craig Peterson +Cristian Maglie +Daehyeok Mun +Daniel Leavitt +Dave Du Cros +Dave Henderson +David Deng +Dennis Webb +Diego Lapiduz +Dmitri Shuralyov +dmnlk +Don Petersen +Doug Turner +Drew Fradette +Eli Uriegas +Elliott Beach +erwinvaneyk +Fabrice +Filippo Valsorda +Florian Forster +Francesc Gil +Francis +Fredrik Jönsson +Garrett Squire +Georgy Buranov +Gnahz +Google Inc. +griffin_stewie +Guz Alexander +Hanno Hecker +Hari haran +haya14busa +Huy Tr +huydx +i2bskn +Isao Jonas +isqua +Jameel Haffejee +Jan Kosecki +Jeremy Morris +Jihoon Chung +Jimmi Dyson +Joe Tsai +John Barton +John Engelman +jpbelanger-mtl +Juan Basso +Julien Rostand +Justin Abrahms +jzhoucliqr +Katrina Owen +Keita Urashima +Kevin Burke +Konrad Malawski +Kookheon Kwon +Krzysztof Kowalczyk +Kshitij Saraogi +kyokomi +Lucas Alcantara +Luke Evers +Luke Kysow +Luke Roberts +Luke Young +Maksim Zhylinski +Martin-Louis Bright +Mat Geist +Matt Brender +Matt Landis +Maxime Bury +Michael Tiller +MichaÅ‚ Glapa +Nathan VanBenschoten +Neil O'Toole +Nick Miyake +Nick Spragg +Nikhita Raghunath +Noah Zoschke +ns-cweber +OndÅ™ej Kupka +Panagiotis Moustafellos +Parker Moore +Pavel Shtanko +Petr Shevtsov +Pierre Carrier +Piotr Zurek +Quinn Slack +Rackspace US, Inc. +RaviTeja Pothana +rc1140 +Red Hat, Inc. +Rob Figueiredo +Ronak Jain +Ruben Vereecken +Ryan Lower +Sahil Dua +saisi +Sam Minnée +Sander van Harmelen +Sarasa Kisaragi +Sean Wang +Sebastian Mandrean +Sebastian Mæland Pedersen +Sevki +Shawn Catanzarite +Shawn Smith +sona-tar +SoundCloud, Ltd. +Stian Eikeland +Thomas Bruyelle +Timothée Peignier +Trey Tacon +ttacon +Varadarajan Aravamudhan +Victor Castell +Victor Vrantchan +Vlad Ungureanu +Will Maier +William Bailey +Yann Malet +Yannick Utard +Yicheng Qin +Yumikiyo Osanai +Zach Latta diff --git a/vendor/github.com/google/go-github/CONTRIBUTING.md b/vendor/github.com/google/go-github/CONTRIBUTING.md new file mode 100644 index 00000000..8da635a6 --- /dev/null +++ b/vendor/github.com/google/go-github/CONTRIBUTING.md @@ -0,0 +1,114 @@ +# How to contribute # + +We'd love to accept your patches and contributions to this project. There are +a just a few small guidelines you need to follow. + + +## Contributor License Agreement ## + +Contributions to any Google project must be accompanied by a Contributor +License Agreement. This is not a copyright **assignment**, it simply gives +Google permission to use and redistribute your contributions as part of the +project. Head over to to see your current +agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + + +## Submitting a patch ## + + 1. It's generally best to start by opening a new issue describing the bug or + feature you're intending to fix. Even if you think it's relatively minor, + it's helpful to know what people are working on. Mention in the initial + issue that you are planning to work on that bug or feature so that it can + be assigned to you. + + 1. Follow the normal process of [forking][] the project, and setup a new + branch to work in. It's important that each group of changes be done in + separate branches in order to ensure that a pull request only includes the + commits related to that bug or feature. + + 1. Go makes it very simple to ensure properly formatted code, so always run + `go fmt` on your code before committing it. You should also run + [golint][] over your code. As noted in the [golint readme][], it's not + strictly necessary that your code be completely "lint-free", but this will + help you find common style issues. + + 1. Any significant changes should almost always be accompanied by tests. The + project already has good test coverage, so look at some of the existing + tests if you're unsure how to go about it. [gocov][] and [gocov-html][] + are invaluable tools for seeing which parts of your code aren't being + exercised by your tests. + + 1. Please run: + * `go generate github.com/google/go-github/...` + * `go test github.com/google/go-github/...` + * `go vet github.com/google/go-github/...` + + 1. Do your best to have [well-formed commit messages][] for each change. + This provides consistency throughout the project, and ensures that commit + messages are able to be formatted properly by various git tools. + + 1. Finally, push the commits to your fork and submit a [pull request][]. + +[forking]: https://help.github.com/articles/fork-a-repo +[golint]: https://github.com/golang/lint +[golint readme]: https://github.com/golang/lint/blob/master/README.md +[gocov]: https://github.com/axw/gocov +[gocov-html]: https://github.com/matm/gocov-html +[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits +[pull request]: https://help.github.com/articles/creating-a-pull-request + + +## Other notes on code organization ## + +Currently, everything is defined in the main `github` package, with API methods +broken into separate service objects. These services map directly to how +the [GitHub API documentation][] is organized, so use that as your guide for +where to put new methods. + +Code is organized in files also based pretty closely on the GitHub API +documentation, following the format `{service}_{api}.go`. For example, methods +defined at live in +[repos_hooks.go][]. + +[GitHub API documentation]: https://developer.github.com/v3/ +[repos_hooks.go]: https://github.com/google/go-github/blob/master/github/repos_hooks.go + + +## Maintainer's Guide ## + +(These notes are mostly only for people merging in pull requests.) + +**Verify CLAs.** CLAs must be on file for the pull request submitter and commit +author(s). Google's CLA verification system should handle this automatically +and will set commit statuses as appropriate. If there's ever any question about +a pull request, ask [willnorris](https://github.com/willnorris). + +**Always try to maintain a clean, linear git history.** With very few +exceptions, running `git log` should not show a bunch of branching and merging. + +Never use the GitHub "merge" button, since it always creates a merge commit. +Instead, check out the pull request locally ([these git aliases +help][git-aliases]), then cherry-pick or rebase them onto master. If there are +small cleanup commits, especially as a result of addressing code review +comments, these should almost always be squashed down to a single commit. Don't +bother squashing commits that really deserve to be separate though. If needed, +feel free to amend additional small changes to the code or commit message that +aren't worth going through code review for. + +If you made any changes like squashing commits, rebasing onto master, etc, then +GitHub won't recognize that this is the same commit in order to mark the pull +request as "merged". So instead, amend the commit message to include a line +"Fixes #0", referencing the pull request number. This would be in addition to +any other "Fixes" lines for closing related issues. If you forget to do this, +you can also leave a comment on the pull request [like this][rebase-comment]. +If you made any other changes, it's worth noting that as well, [like +this][modified-comment]. + +[git-aliases]: https://github.com/willnorris/dotfiles/blob/d640d010c23b1116bdb3d4dc12088ed26120d87d/git/.gitconfig#L13-L15 +[rebase-comment]: https://github.com/google/go-github/pull/277#issuecomment-183035491 +[modified-comment]: https://github.com/google/go-github/pull/280#issuecomment-184859046 diff --git a/vendor/github.com/google/go-github/LICENSE b/vendor/github.com/google/go-github/LICENSE new file mode 100644 index 00000000..53d5374a --- /dev/null +++ b/vendor/github.com/google/go-github/LICENSE @@ -0,0 +1,341 @@ +Copyright (c) 2013 The go-github AUTHORS. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------- + +Some documentation is taken from the GitHub Developer site +, which is available under the following Creative +Commons Attribution 3.0 License. This applies only to the go-github source +code and would not apply to any compiled binaries. + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY +BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may be + recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed + in Section 1(f) below, which, by reason of the selection and + arrangement of their contents, constitute intellectual creations, in + which the Work is included in its entirety in unmodified form along + with one or more other contributions, each constituting separate and + independent works in themselves, which together are assembled into a + collective whole. A work that constitutes a Collection will not be + considered an Adaptation (as defined above) for the purposes of this + License. + c. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or + other transfer of ownership. + d. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + e. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work + or if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + f. "Work" means the literary and/or artistic work offered under the terms + of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, + pamphlet and other writing; a lecture, address, sermon or other work + of the same nature; a dramatic or dramatico-musical work; a + choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving or + lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied + art; an illustration, map, plan, sketch or three-dimensional work + relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the + extent it is protected as a copyrightable work; or a work performed by + a variety or circus performer to the extent it is not otherwise + considered a literary or artistic work. + g. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + h. "Publicly Perform" means to perform public recitations of the Work and + to communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a + place individually chosen by them; to perform the Work to the public + by any means or process and the communication to the public of the + performances of the Work, including by public digital performance; to + broadcast and rebroadcast the Work by any means including signs, + sounds or images. + i. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to + clearly label, demarcate or otherwise identify that changes were made + to the original Work. For example, a translation could be marked "The + original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor + reserves the exclusive right to collect such royalties for any + exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor waives the + exclusive right to collect such royalties for any exercise by You + of the rights granted under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to + collect royalties, whether individually or, in the event that the + Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License. + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights in +other media and formats. Subject to Section 8(f), all rights not expressly +granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms + on the Work that restrict the terms of this License or the ability of + the recipient of the Work to exercise the rights granted to that + recipient under the terms of the License. You may not sublicense the + Work. You must keep intact all notices that refer to this License and + to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Work, You may not impose any effective technological + measures on the Work that restrict the ability of a recipient of the + Work from You to exercise the rights granted to that recipient under + the terms of the License. This Section 4(a) applies to the Work as + incorporated in a Collection, but this does not require the Collection + apart from the Work itself to be made subject to the terms of this + License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit + as required by Section 4(b), as requested. If You create an + Adaptation, upon notice from any Licensor You must, to the extent + practicable, remove from the Adaptation any credit as required by + Section 4(b), as requested. + b. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party + or parties; (ii) the title of the Work if supplied; (iii) to the + extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and (iv) , consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by + this Section 4 (b) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of these + credits and in a manner at least as prominent as the credits for the + other contributing authors. For the avoidance of doubt, You may only + use the credit required by this Section for the purpose of attribution + in the manner set out above and, by exercising Your rights under this + License, You may not implicitly or explicitly assert or imply any + connection with, sponsorship or endorsement by the Original Author, + Licensor and/or Attribution Parties, as appropriate, of You or Your + use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution + Parties. + c. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify or + take other derogatory action in relation to the Work which would be + prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION +OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, + the Licensor offers to the recipient a license to the Work on the same + terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 + and the Universal Copyright Convention (as revised on July 24, 1971). + These rights and subject matter take effect in the relevant + jurisdiction in which the License terms are sought to be enforced + according to the corresponding provisions of the implementation of + those treaty provisions in the applicable national law. If the + standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such + additional rights are deemed to be included in the License; this + License is not intended to restrict the license of any rights under + applicable law. + + +Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the + Work is licensed under the CCPL, Creative Commons does not authorize + the use by either party of the trademark "Creative Commons" or any + related trademark or logo of Creative Commons without the prior + written consent of Creative Commons. Any permitted use will be in + compliance with Creative Commons' then-current trademark usage + guidelines, as may be published on its website or otherwise made + available upon request from time to time. For the avoidance of doubt, + this trademark restriction does not form part of this License. + + Creative Commons may be contacted at http://creativecommons.org/. diff --git a/vendor/github.com/google/go-github/README.md b/vendor/github.com/google/go-github/README.md new file mode 100644 index 00000000..f416c81d --- /dev/null +++ b/vendor/github.com/google/go-github/README.md @@ -0,0 +1,244 @@ +# go-github # + +go-github is a Go client library for accessing the [GitHub API v3][]. + +**Documentation:** [![GoDoc](https://godoc.org/github.com/google/go-github/github?status.svg)](https://godoc.org/github.com/google/go-github/github) +**Mailing List:** [go-github@googlegroups.com](https://groups.google.com/group/go-github) +**Build Status:** [![Build Status](https://travis-ci.org/google/go-github.svg?branch=master)](https://travis-ci.org/google/go-github) +**Test Coverage:** [![Test Coverage](https://coveralls.io/repos/google/go-github/badge.svg?branch=master)](https://coveralls.io/r/google/go-github?branch=master) + +go-github requires Go version 1.7 or greater. + +If you're interested in using the [GraphQL API v4][], the recommended library is +[shurcooL/githubql][]. + +## Usage ## + +```go +import "github.com/google/go-github/github" +``` + +Construct a new GitHub client, then use the various services on the client to +access different parts of the GitHub API. For example: + +```go +client := github.NewClient(nil) + +// list all organizations for user "willnorris" +orgs, _, err := client.Organizations.List(ctx, "willnorris", nil) +``` + +Some API methods have optional parameters that can be passed. For example: + +```go +client := github.NewClient(nil) + +// list public repositories for org "github" +opt := &github.RepositoryListByOrgOptions{Type: "public"} +repos, _, err := client.Repositories.ListByOrg(ctx, "github", opt) +``` + +The services of a client divide the API into logical chunks and correspond to +the structure of the GitHub API documentation at +https://developer.github.com/v3/. + +### Authentication ### + +The go-github library does not directly handle authentication. Instead, when +creating a new client, pass an `http.Client` that can handle authentication for +you. The easiest and recommended way to do this is using the [oauth2][] +library, but you can always use any other library that provides an +`http.Client`. If you have an OAuth2 access token (for example, a [personal +API token][]), you can use it with the oauth2 library using: + +```go +import "golang.org/x/oauth2" + +func main() { + ctx := context.Background() + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: "... your access token ..."}, + ) + tc := oauth2.NewClient(ctx, ts) + + client := github.NewClient(tc) + + // list all repositories for the authenticated user + repos, _, err := client.Repositories.List(ctx, "", nil) +} +``` + +Note that when using an authenticated Client, all calls made by the client will +include the specified OAuth token. Therefore, authenticated clients should +almost never be shared between different users. + +See the [oauth2 docs][] for complete instructions on using that library. + +For API methods that require HTTP Basic Authentication, use the +[`BasicAuthTransport`](https://godoc.org/github.com/google/go-github/github#BasicAuthTransport). + +GitHub Apps authentication can be provided by the [ghinstallation](https://github.com/bradleyfalzon/ghinstallation) +package. + +```go +import "github.com/bradleyfalzon/ghinstallation" + +func main() { + // Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99. + itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem") + if err != nil { + // Handle error. + } + + // Use installation transport with client. + client := github.NewClient(&http.Client{Transport: itr}) + + // Use client... +} +``` + +### Rate Limiting ### + +GitHub imposes a rate limit on all API clients. Unauthenticated clients are +limited to 60 requests per hour, while authenticated clients can make up to +5,000 requests per hour. The Search API has a custom rate limit. Unauthenticated +clients are limited to 10 requests per minute, while authenticated clients +can make up to 30 requests per minute. To receive the higher rate limit when +making calls that are not issued on behalf of a user, +use `UnauthenticatedRateLimitedTransport`. + +The returned `Response.Rate` value contains the rate limit information +from the most recent API call. If a recent enough response isn't +available, you can use `RateLimits` to fetch the most up-to-date rate +limit data for the client. + +To detect an API rate limit error, you can check if its type is `*github.RateLimitError`: + +```go +repos, _, err := client.Repositories.List(ctx, "", nil) +if _, ok := err.(*github.RateLimitError); ok { + log.Println("hit rate limit") +} +``` + +Learn more about GitHub rate limiting at +https://developer.github.com/v3/#rate-limiting. + +### Accepted Status ### + +Some endpoints may return a 202 Accepted status code, meaning that the +information required is not yet ready and was scheduled to be gathered on +the GitHub side. Methods known to behave like this are documented specifying +this behavior. + +To detect this condition of error, you can check if its type is +`*github.AcceptedError`: + +```go +stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo) +if _, ok := err.(*github.AcceptedError); ok { + log.Println("scheduled on GitHub side") +} +``` + +### Conditional Requests ### + +The GitHub API has good support for conditional requests which will help +prevent you from burning through your rate limit, as well as help speed up your +application. `go-github` does not handle conditional requests directly, but is +instead designed to work with a caching `http.Transport`. We recommend using +https://github.com/gregjones/httpcache for that. + +Learn more about GitHub conditional requests at +https://developer.github.com/v3/#conditional-requests. + +### Creating and Updating Resources ### + +All structs for GitHub resources use pointer values for all non-repeated fields. +This allows distinguishing between unset fields and those set to a zero-value. +Helper functions have been provided to easily create these pointers for string, +bool, and int values. For example: + +```go +// create a new private repository named "foo" +repo := &github.Repository{ + Name: github.String("foo"), + Private: github.Bool(true), +} +client.Repositories.Create(ctx, "", repo) +``` + +Users who have worked with protocol buffers should find this pattern familiar. + +### Pagination ### + +All requests for resource collections (repos, pull requests, issues, etc.) +support pagination. Pagination options are described in the +`github.ListOptions` struct and passed to the list methods directly or as an +embedded type of a more specific list options struct (for example +`github.PullRequestListOptions`). Pages information is available via the +`github.Response` struct. + +```go +client := github.NewClient(nil) + +opt := &github.RepositoryListByOrgOptions{ + ListOptions: github.ListOptions{PerPage: 10}, +} +// get all pages of results +var allRepos []*github.Repository +for { + repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt) + if err != nil { + return err + } + allRepos = append(allRepos, repos...) + if resp.NextPage == 0 { + break + } + opt.Page = resp.NextPage +} +``` + +For complete usage of go-github, see the full [package docs][]. + +[GitHub API v3]: https://developer.github.com/v3/ +[oauth2]: https://github.com/golang/oauth2 +[oauth2 docs]: https://godoc.org/golang.org/x/oauth2 +[personal API token]: https://github.com/blog/1509-personal-api-tokens +[package docs]: https://godoc.org/github.com/google/go-github/github +[GraphQL API v4]: https://developer.github.com/v4/ +[shurcooL/githubql]: https://github.com/shurcooL/githubql + +### Google App Engine ### + +Go on App Engine Classic (which as of this writing uses Go 1.6) can not use +the `"context"` import and still relies on `"golang.org/x/net/context"`. +As a result, if you wish to continue to use `go-github` on App Engine Classic, +you will need to rewrite all the `"context"` imports using the following command: + + gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go + +See `with_appengine.go` for more details. + +### Integration Tests ### + +You can run integration tests from the `test` directory. See the integration tests [README](test/README.md). + +## Roadmap ## + +This library is being initially developed for an internal application at +Google, so API methods will likely be implemented in the order that they are +needed by that application. You can track the status of implementation in +[this Google spreadsheet][roadmap]. Eventually, I would like to cover the entire +GitHub API, so contributions are of course [always welcome][contributing]. The +calling pattern is pretty well established, so adding new methods is relatively +straightforward. + +[roadmap]: https://docs.google.com/spreadsheet/ccc?key=0ApoVX4GOiXr-dGNKN1pObFh6ek1DR2FKUjBNZ1FmaEE&usp=sharing +[contributing]: CONTRIBUTING.md + +## License ## + +This library is distributed under the BSD-style license found in the [LICENSE](./LICENSE) +file. diff --git a/vendor/github.com/google/go-github/example/appengine/app.go b/vendor/github.com/google/go-github/example/appengine/app.go new file mode 100644 index 00000000..4d6cc30c --- /dev/null +++ b/vendor/github.com/google/go-github/example/appengine/app.go @@ -0,0 +1,49 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package demo provides an app that shows how to use the github package on +// Google App Engine. +package demo + +import ( + "fmt" + "net/http" + "os" + + "github.com/google/go-github/github" + "golang.org/x/oauth2" + "google.golang.org/appengine" + "google.golang.org/appengine/log" +) + +func init() { + http.HandleFunc("/", handler) +} + +func handler(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + ctx := appengine.NewContext(r) + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: os.Getenv("GITHUB_AUTH_TOKEN")}, + ) + tc := oauth2.NewClient(ctx, ts) + client := github.NewClient(tc) + + commits, _, err := client.Repositories.ListCommits(ctx, "google", "go-github", nil) + if err != nil { + log.Errorf(ctx, "ListCommits: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + for _, commit := range commits { + fmt.Fprintln(w, commit.GetHTMLURL()) + } +} diff --git a/vendor/github.com/google/go-github/example/appengine/app.yaml b/vendor/github.com/google/go-github/example/appengine/app.yaml new file mode 100644 index 00000000..dca235fa --- /dev/null +++ b/vendor/github.com/google/go-github/example/appengine/app.yaml @@ -0,0 +1,14 @@ +# Copyright 2017 The go-github AUTHORS. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +runtime: go +api_version: go1 + +handlers: +- url: /.* + script: _go_app + +env_variables: + GITHUB_AUTH_TOKEN: "-your-auth-token-here-" diff --git a/vendor/github.com/google/go-github/example/basicauth/main.go b/vendor/github.com/google/go-github/example/basicauth/main.go new file mode 100644 index 00000000..f67c3d66 --- /dev/null +++ b/vendor/github.com/google/go-github/example/basicauth/main.go @@ -0,0 +1,55 @@ +// Copyright 2015 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The basicauth command demonstrates using the github.BasicAuthTransport, +// including handling two-factor authentication. This won't currently work for +// accounts that use SMS to receive one-time passwords. +package main + +import ( + "bufio" + "context" + "fmt" + "os" + "strings" + "syscall" + + "github.com/google/go-github/github" + "golang.org/x/crypto/ssh/terminal" +) + +func main() { + r := bufio.NewReader(os.Stdin) + fmt.Print("GitHub Username: ") + username, _ := r.ReadString('\n') + + fmt.Print("GitHub Password: ") + bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin)) + password := string(bytePassword) + + tp := github.BasicAuthTransport{ + Username: strings.TrimSpace(username), + Password: strings.TrimSpace(password), + } + + client := github.NewClient(tp.Client()) + ctx := context.Background() + user, _, err := client.Users.Get(ctx, "") + + // Is this a two-factor auth error? If so, prompt for OTP and try again. + if _, ok := err.(*github.TwoFactorAuthError); ok { + fmt.Print("\nGitHub OTP: ") + otp, _ := r.ReadString('\n') + tp.OTP = strings.TrimSpace(otp) + user, _, err = client.Users.Get(ctx, "") + } + + if err != nil { + fmt.Printf("\nerror: %v\n", err) + return + } + + fmt.Printf("\n%v\n", github.Stringify(user)) +} diff --git a/vendor/github.com/google/go-github/github/activity.go b/vendor/github.com/google/go-github/github/activity.go new file mode 100644 index 00000000..d6c992c7 --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity.go @@ -0,0 +1,69 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import "context" + +// ActivityService handles communication with the activity related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/activity/ +type ActivityService service + +// FeedLink represents a link to a related resource. +type FeedLink struct { + HRef *string `json:"href,omitempty"` + Type *string `json:"type,omitempty"` +} + +// Feeds represents timeline resources in Atom format. +type Feeds struct { + TimelineURL *string `json:"timeline_url,omitempty"` + UserURL *string `json:"user_url,omitempty"` + CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"` + CurrentUserURL *string `json:"current_user_url,omitempty"` + CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"` + CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"` + CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"` + Links *struct { + Timeline *FeedLink `json:"timeline,omitempty"` + User *FeedLink `json:"user,omitempty"` + CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` + CurrentUser *FeedLink `json:"current_user,omitempty"` + CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` + CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` + CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` + } `json:"_links,omitempty"` +} + +// ListFeeds lists all the feeds available to the authenticated user. +// +// GitHub provides several timeline resources in Atom format: +// Timeline: The GitHub global public timeline +// User: The public timeline for any user, using URI template +// Current user public: The public timeline for the authenticated user +// Current user: The private timeline for the authenticated user +// Current user actor: The private timeline for activity created by the +// authenticated user +// Current user organizations: The private timeline for the organizations +// the authenticated user is a member of. +// +// Note: Private feeds are only returned when authenticating via Basic Auth +// since current feed URIs use the older, non revocable auth tokens. +func (s *ActivityService) ListFeeds(ctx context.Context) (*Feeds, *Response, error) { + req, err := s.client.NewRequest("GET", "feeds", nil) + if err != nil { + return nil, nil, err + } + + f := &Feeds{} + resp, err := s.client.Do(ctx, req, f) + if err != nil { + return nil, resp, err + } + + return f, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/activity_events.go b/vendor/github.com/google/go-github/github/activity_events.go new file mode 100644 index 00000000..f337fcd2 --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_events.go @@ -0,0 +1,324 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "time" +) + +// Event represents a GitHub event. +type Event struct { + Type *string `json:"type,omitempty"` + Public *bool `json:"public,omitempty"` + RawPayload *json.RawMessage `json:"payload,omitempty"` + Repo *Repository `json:"repo,omitempty"` + Actor *User `json:"actor,omitempty"` + Org *Organization `json:"org,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + ID *string `json:"id,omitempty"` +} + +func (e Event) String() string { + return Stringify(e) +} + +// ParsePayload parses the event payload. For recognized event types, +// a value of the corresponding struct type will be returned. +func (e *Event) ParsePayload() (payload interface{}, err error) { + switch *e.Type { + case "CommitCommentEvent": + payload = &CommitCommentEvent{} + case "CreateEvent": + payload = &CreateEvent{} + case "DeleteEvent": + payload = &DeleteEvent{} + case "DeploymentEvent": + payload = &DeploymentEvent{} + case "DeploymentStatusEvent": + payload = &DeploymentStatusEvent{} + case "ForkEvent": + payload = &ForkEvent{} + case "GollumEvent": + payload = &GollumEvent{} + case "InstallationEvent": + payload = &InstallationEvent{} + case "InstallationRepositoriesEvent": + payload = &InstallationRepositoriesEvent{} + case "IssueCommentEvent": + payload = &IssueCommentEvent{} + case "IssuesEvent": + payload = &IssuesEvent{} + case "LabelEvent": + payload = &LabelEvent{} + case "MarketplacePurchaseEvent": + payload = &MarketplacePurchaseEvent{} + case "MemberEvent": + payload = &MemberEvent{} + case "MembershipEvent": + payload = &MembershipEvent{} + case "MilestoneEvent": + payload = &MilestoneEvent{} + case "OrganizationEvent": + payload = &OrganizationEvent{} + case "OrgBlockEvent": + payload = &OrgBlockEvent{} + case "PageBuildEvent": + payload = &PageBuildEvent{} + case "PingEvent": + payload = &PingEvent{} + case "ProjectEvent": + payload = &ProjectEvent{} + case "ProjectCardEvent": + payload = &ProjectCardEvent{} + case "ProjectColumnEvent": + payload = &ProjectColumnEvent{} + case "PublicEvent": + payload = &PublicEvent{} + case "PullRequestEvent": + payload = &PullRequestEvent{} + case "PullRequestReviewEvent": + payload = &PullRequestReviewEvent{} + case "PullRequestReviewCommentEvent": + payload = &PullRequestReviewCommentEvent{} + case "PushEvent": + payload = &PushEvent{} + case "ReleaseEvent": + payload = &ReleaseEvent{} + case "RepositoryEvent": + payload = &RepositoryEvent{} + case "StatusEvent": + payload = &StatusEvent{} + case "TeamEvent": + payload = &TeamEvent{} + case "TeamAddEvent": + payload = &TeamAddEvent{} + case "WatchEvent": + payload = &WatchEvent{} + } + err = json.Unmarshal(*e.RawPayload, &payload) + return payload, err +} + +// Payload returns the parsed event payload. For recognized event types, +// a value of the corresponding struct type will be returned. +// +// Deprecated: Use ParsePayload instead, which returns an error +// rather than panics if JSON unmarshaling raw payload fails. +func (e *Event) Payload() (payload interface{}) { + var err error + payload, err = e.ParsePayload() + if err != nil { + panic(err) + } + return payload +} + +// ListEvents drinks from the firehose of all public events across GitHub. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events +func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]*Event, *Response, error) { + u, err := addOptions("events", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*Event + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListRepositoryEvents lists events for a repository. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-repository-events +func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/events", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*Event + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListIssueEventsForRepository lists issue events for a repository. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository +func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*IssueEvent + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListEventsForRepoNetwork lists public events for a network of repositories. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories +func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { + u := fmt.Sprintf("networks/%v/%v/events", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*Event + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListEventsForOrganization lists public events for an organization. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization +func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opt *ListOptions) ([]*Event, *Response, error) { + u := fmt.Sprintf("orgs/%v/events", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*Event + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListEventsPerformedByUser lists the events performed by a user. If publicOnly is +// true, only public events will be returned. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user +func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { + var u string + if publicOnly { + u = fmt.Sprintf("users/%v/events/public", user) + } else { + u = fmt.Sprintf("users/%v/events", user) + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*Event + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListEventsReceivedByUser lists the events received by a user. If publicOnly is +// true, only public events will be returned. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received +func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { + var u string + if publicOnly { + u = fmt.Sprintf("users/%v/received_events/public", user) + } else { + u = fmt.Sprintf("users/%v/received_events", user) + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*Event + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListUserEventsForOrganization provides the user’s organization dashboard. You +// must be authenticated as the user to view this. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-for-an-organization +func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opt *ListOptions) ([]*Event, *Response, error) { + u := fmt.Sprintf("users/%v/events/orgs/%v", user, org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*Event + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/activity_events_test.go b/vendor/github.com/google/go-github/github/activity_events_test.go new file mode 100644 index 00000000..0b01c4a0 --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_events_test.go @@ -0,0 +1,349 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestActivityService_ListEvents(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListEvents(context.Background(), opt) + if err != nil { + t.Errorf("Activities.ListEvents returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Activities.ListEvents returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListRepositoryEvents(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListRepositoryEvents(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Activities.ListRepositoryEvents returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Activities.ListRepositoryEvents returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListRepositoryEvents_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.ListRepositoryEvents(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestActivityService_ListIssueEventsForRepository(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListIssueEventsForRepository(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Activities.ListIssueEventsForRepository returned error: %v", err) + } + + want := []*IssueEvent{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Activities.ListIssueEventsForRepository returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListIssueEventsForRepository_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.ListIssueEventsForRepository(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestActivityService_ListEventsForRepoNetwork(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/networks/o/r/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListEventsForRepoNetwork(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Activities.ListEventsForRepoNetwork returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Activities.ListEventsForRepoNetwork returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListEventsForRepoNetwork_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.ListEventsForRepoNetwork(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestActivityService_ListEventsForOrganization(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListEventsForOrganization(context.Background(), "o", opt) + if err != nil { + t.Errorf("Activities.ListEventsForOrganization returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Activities.ListEventsForOrganization returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListEventsForOrganization_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.ListEventsForOrganization(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "u", false, opt) + if err != nil { + t.Errorf("Events.ListPerformedByUser returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListEventsPerformedByUser_publicOnly(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/events/public", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + events, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "u", true, nil) + if err != nil { + t.Errorf("Events.ListPerformedByUser returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListEventsPerformedByUser_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "%", false, nil) + testURLParseError(t, err) +} + +func TestActivityService_ListEventsReceivedByUser_all(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/received_events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "u", false, opt) + if err != nil { + t.Errorf("Events.ListReceivedByUser returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Events.ListReceivedUser returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListEventsReceivedByUser_publicOnly(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/received_events/public", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + events, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "u", true, nil) + if err != nil { + t.Errorf("Events.ListReceivedByUser returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Events.ListReceivedByUser returned %+v, want %+v", events, want) + } +} + +func TestActivityService_ListEventsReceivedByUser_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "%", false, nil) + testURLParseError(t, err) +} + +func TestActivityService_ListUserEventsForOrganization(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/events/orgs/o", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) + }) + + opt := &ListOptions{Page: 2} + events, _, err := client.Activity.ListUserEventsForOrganization(context.Background(), "o", "u", opt) + if err != nil { + t.Errorf("Activities.ListUserEventsForOrganization returned error: %v", err) + } + + want := []*Event{{ID: String("1")}, {ID: String("2")}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Activities.ListUserEventsForOrganization returned %+v, want %+v", events, want) + } +} + +func TestActivityService_EventParsePayload_typed(t *testing.T) { + raw := []byte(`{"type": "PushEvent","payload":{"push_id": 1}}`) + var event *Event + if err := json.Unmarshal(raw, &event); err != nil { + t.Fatalf("Unmarshal Event returned error: %v", err) + } + + want := &PushEvent{PushID: Int64(1)} + got, err := event.ParsePayload() + if err != nil { + t.Fatalf("ParsePayload returned unexpected error: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want) + } +} + +// TestEvent_Payload_untyped checks that unrecognized events are parsed to an +// interface{} value (instead of being discarded or throwing an error), for +// forward compatibility with new event types. +func TestActivityService_EventParsePayload_untyped(t *testing.T) { + raw := []byte(`{"type": "UnrecognizedEvent","payload":{"field": "val"}}`) + var event *Event + if err := json.Unmarshal(raw, &event); err != nil { + t.Fatalf("Unmarshal Event returned error: %v", err) + } + + want := map[string]interface{}{"field": "val"} + got, err := event.ParsePayload() + if err != nil { + t.Fatalf("ParsePayload returned unexpected error: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want) + } +} + +func TestActivityService_EventParsePayload_installation(t *testing.T) { + raw := []byte(`{"type": "PullRequestEvent","payload":{"installation":{"id":1}}}`) + var event *Event + if err := json.Unmarshal(raw, &event); err != nil { + t.Fatalf("Unmarshal Event returned error: %v", err) + } + + want := &PullRequestEvent{Installation: &Installation{ID: Int64(1)}} + got, err := event.ParsePayload() + if err != nil { + t.Fatalf("ParsePayload returned unexpected error: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want) + } +} diff --git a/vendor/github.com/google/go-github/github/activity_notifications.go b/vendor/github.com/google/go-github/github/activity_notifications.go new file mode 100644 index 00000000..45c8b2ae --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_notifications.go @@ -0,0 +1,223 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// Notification identifies a GitHub notification for a user. +type Notification struct { + ID *string `json:"id,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Subject *NotificationSubject `json:"subject,omitempty"` + + // Reason identifies the event that triggered the notification. + // + // GitHub API docs: https://developer.github.com/v3/activity/notifications/#notification-reasons + Reason *string `json:"reason,omitempty"` + + Unread *bool `json:"unread,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + LastReadAt *time.Time `json:"last_read_at,omitempty"` + URL *string `json:"url,omitempty"` +} + +// NotificationSubject identifies the subject of a notification. +type NotificationSubject struct { + Title *string `json:"title,omitempty"` + URL *string `json:"url,omitempty"` + LatestCommentURL *string `json:"latest_comment_url,omitempty"` + Type *string `json:"type,omitempty"` +} + +// NotificationListOptions specifies the optional parameters to the +// ActivityService.ListNotifications method. +type NotificationListOptions struct { + All bool `url:"all,omitempty"` + Participating bool `url:"participating,omitempty"` + Since time.Time `url:"since,omitempty"` + Before time.Time `url:"before,omitempty"` + + ListOptions +} + +// ListNotifications lists all notifications for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications +func (s *ActivityService) ListNotifications(ctx context.Context, opt *NotificationListOptions) ([]*Notification, *Response, error) { + u := fmt.Sprintf("notifications") + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var notifications []*Notification + resp, err := s.client.Do(ctx, req, ¬ifications) + if err != nil { + return nil, resp, err + } + + return notifications, resp, nil +} + +// ListRepositoryNotifications lists all notifications in a given repository +// for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository +func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opt *NotificationListOptions) ([]*Notification, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var notifications []*Notification + resp, err := s.client.Do(ctx, req, ¬ifications) + if err != nil { + return nil, resp, err + } + + return notifications, resp, nil +} + +type markReadOptions struct { + LastReadAt time.Time `json:"last_read_at,omitempty"` +} + +// MarkNotificationsRead marks all notifications up to lastRead as read. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-as-read +func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead time.Time) (*Response, error) { + opts := &markReadOptions{ + LastReadAt: lastRead, + } + req, err := s.client.NewRequest("PUT", "notifications", opts) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// MarkRepositoryNotificationsRead marks all notifications up to lastRead in +// the specified repository as read. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository +func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) { + opts := &markReadOptions{ + LastReadAt: lastRead, + } + u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) + req, err := s.client.NewRequest("PUT", u, opts) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// GetThread gets the specified notification thread. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread +func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notification, *Response, error) { + u := fmt.Sprintf("notifications/threads/%v", id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + notification := new(Notification) + resp, err := s.client.Do(ctx, req, notification) + if err != nil { + return nil, resp, err + } + + return notification, resp, nil +} + +// MarkThreadRead marks the specified thread as read. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read +func (s *ActivityService) MarkThreadRead(ctx context.Context, id string) (*Response, error) { + u := fmt.Sprintf("notifications/threads/%v", id) + + req, err := s.client.NewRequest("PATCH", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// GetThreadSubscription checks to see if the authenticated user is subscribed +// to a thread. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription +func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) (*Subscription, *Response, error) { + u := fmt.Sprintf("notifications/threads/%v/subscription", id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + sub := new(Subscription) + resp, err := s.client.Do(ctx, req, sub) + if err != nil { + return nil, resp, err + } + + return sub, resp, nil +} + +// SetThreadSubscription sets the subscription for the specified thread for the +// authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription +func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, subscription *Subscription) (*Subscription, *Response, error) { + u := fmt.Sprintf("notifications/threads/%v/subscription", id) + + req, err := s.client.NewRequest("PUT", u, subscription) + if err != nil { + return nil, nil, err + } + + sub := new(Subscription) + resp, err := s.client.Do(ctx, req, sub) + if err != nil { + return nil, resp, err + } + + return sub, resp, nil +} + +// DeleteThreadSubscription deletes the subscription for the specified thread +// for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription +func (s *ActivityService) DeleteThreadSubscription(ctx context.Context, id string) (*Response, error) { + u := fmt.Sprintf("notifications/threads/%v/subscription", id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/activity_notifications_test.go b/vendor/github.com/google/go-github/github/activity_notifications_test.go new file mode 100644 index 00000000..292e3e24 --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_notifications_test.go @@ -0,0 +1,204 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestActivityService_ListNotification(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "all": "true", + "participating": "true", + "since": "2006-01-02T15:04:05Z", + "before": "2007-03-04T15:04:05Z", + }) + + fmt.Fprint(w, `[{"id":"1", "subject":{"title":"t"}}]`) + }) + + opt := &NotificationListOptions{ + All: true, + Participating: true, + Since: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC), + Before: time.Date(2007, 03, 04, 15, 04, 05, 0, time.UTC), + } + notifications, _, err := client.Activity.ListNotifications(context.Background(), opt) + if err != nil { + t.Errorf("Activity.ListNotifications returned error: %v", err) + } + + want := []*Notification{{ID: String("1"), Subject: &NotificationSubject{Title: String("t")}}} + if !reflect.DeepEqual(notifications, want) { + t.Errorf("Activity.ListNotifications returned %+v, want %+v", notifications, want) + } +} + +func TestActivityService_ListRepositoryNotification(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":"1"}]`) + }) + + notifications, _, err := client.Activity.ListRepositoryNotifications(context.Background(), "o", "r", nil) + if err != nil { + t.Errorf("Activity.ListRepositoryNotifications returned error: %v", err) + } + + want := []*Notification{{ID: String("1")}} + if !reflect.DeepEqual(notifications, want) { + t.Errorf("Activity.ListRepositoryNotifications returned %+v, want %+v", notifications, want) + } +} + +func TestActivityService_MarkNotificationsRead(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n") + + w.WriteHeader(http.StatusResetContent) + }) + + _, err := client.Activity.MarkNotificationsRead(context.Background(), time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)) + if err != nil { + t.Errorf("Activity.MarkNotificationsRead returned error: %v", err) + } +} + +func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n") + + w.WriteHeader(http.StatusResetContent) + }) + + _, err := client.Activity.MarkRepositoryNotificationsRead(context.Background(), "o", "r", time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)) + if err != nil { + t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err) + } +} + +func TestActivityService_GetThread(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":"1"}`) + }) + + notification, _, err := client.Activity.GetThread(context.Background(), "1") + if err != nil { + t.Errorf("Activity.GetThread returned error: %v", err) + } + + want := &Notification{ID: String("1")} + if !reflect.DeepEqual(notification, want) { + t.Errorf("Activity.GetThread returned %+v, want %+v", notification, want) + } +} + +func TestActivityService_MarkThreadRead(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + w.WriteHeader(http.StatusResetContent) + }) + + _, err := client.Activity.MarkThreadRead(context.Background(), "1") + if err != nil { + t.Errorf("Activity.MarkThreadRead returned error: %v", err) + } +} + +func TestActivityService_GetThreadSubscription(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"subscribed":true}`) + }) + + sub, _, err := client.Activity.GetThreadSubscription(context.Background(), "1") + if err != nil { + t.Errorf("Activity.GetThreadSubscription returned error: %v", err) + } + + want := &Subscription{Subscribed: Bool(true)} + if !reflect.DeepEqual(sub, want) { + t.Errorf("Activity.GetThreadSubscription returned %+v, want %+v", sub, want) + } +} + +func TestActivityService_SetThreadSubscription(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Subscription{Subscribed: Bool(true)} + + mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { + v := new(Subscription) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ignored":true}`) + }) + + sub, _, err := client.Activity.SetThreadSubscription(context.Background(), "1", input) + if err != nil { + t.Errorf("Activity.SetThreadSubscription returned error: %v", err) + } + + want := &Subscription{Ignored: Bool(true)} + if !reflect.DeepEqual(sub, want) { + t.Errorf("Activity.SetThreadSubscription returned %+v, want %+v", sub, want) + } +} + +func TestActivityService_DeleteThreadSubscription(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Activity.DeleteThreadSubscription(context.Background(), "1") + if err != nil { + t.Errorf("Activity.DeleteThreadSubscription returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/activity_star.go b/vendor/github.com/google/go-github/github/activity_star.go new file mode 100644 index 00000000..d5b06712 --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_star.go @@ -0,0 +1,135 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// StarredRepository is returned by ListStarred. +type StarredRepository struct { + StarredAt *Timestamp `json:"starred_at,omitempty"` + Repository *Repository `json:"repo,omitempty"` +} + +// Stargazer represents a user that has starred a repository. +type Stargazer struct { + StarredAt *Timestamp `json:"starred_at,omitempty"` + User *User `json:"user,omitempty"` +} + +// ListStargazers lists people who have starred the specified repo. +// +// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-stargazers +func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Stargazer, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeStarringPreview) + + var stargazers []*Stargazer + resp, err := s.client.Do(ctx, req, &stargazers) + if err != nil { + return nil, resp, err + } + + return stargazers, resp, nil +} + +// ActivityListStarredOptions specifies the optional parameters to the +// ActivityService.ListStarred method. +type ActivityListStarredOptions struct { + // How to sort the repository list. Possible values are: created, updated, + // pushed, full_name. Default is "full_name". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort repositories. Possible values are: asc, desc. + // Default is "asc" when sort is "full_name", otherwise default is "desc". + Direction string `url:"direction,omitempty"` + + ListOptions +} + +// ListStarred lists all the repos starred by a user. Passing the empty string +// will list the starred repositories for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred +func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/starred", user) + } else { + u = "user/starred" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeStarringPreview) + + var repos []*StarredRepository + resp, err := s.client.Do(ctx, req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// IsStarred checks if a repository is starred by authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository +func (s *ActivityService) IsStarred(ctx context.Context, owner, repo string) (bool, *Response, error) { + u := fmt.Sprintf("user/starred/%v/%v", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + resp, err := s.client.Do(ctx, req, nil) + starred, err := parseBoolResponse(err) + return starred, resp, err +} + +// Star a repository as the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository +func (s *ActivityService) Star(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("user/starred/%v/%v", owner, repo) + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// Unstar a repository as the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository +func (s *ActivityService) Unstar(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("user/starred/%v/%v", owner, repo) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/activity_star_test.go b/vendor/github.com/google/go-github/github/activity_star_test.go new file mode 100644 index 00000000..80523f8e --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_star_test.go @@ -0,0 +1,184 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestActivityService_ListStargazers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeStarringPreview) + testFormValues(t, r, values{ + "page": "2", + }) + + fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","user":{"id":1}}]`) + }) + + stargazers, _, err := client.Activity.ListStargazers(context.Background(), "o", "r", &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Activity.ListStargazers returned error: %v", err) + } + + want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int64(1)}}} + if !reflect.DeepEqual(stargazers, want) { + t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want) + } +} + +func TestActivityService_ListStarred_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeStarringPreview) + fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":1}}]`) + }) + + repos, _, err := client.Activity.ListStarred(context.Background(), "", nil) + if err != nil { + t.Errorf("Activity.ListStarred returned error: %v", err) + } + + want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(1)}}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) + } +} + +func TestActivityService_ListStarred_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeStarringPreview) + testFormValues(t, r, values{ + "sort": "created", + "direction": "asc", + "page": "2", + }) + fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":2}}]`) + }) + + opt := &ActivityListStarredOptions{"created", "asc", ListOptions{Page: 2}} + repos, _, err := client.Activity.ListStarred(context.Background(), "u", opt) + if err != nil { + t.Errorf("Activity.ListStarred returned error: %v", err) + } + + want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(2)}}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) + } +} + +func TestActivityService_ListStarred_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.ListStarred(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestActivityService_IsStarred_hasStar(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + star, _, err := client.Activity.IsStarred(context.Background(), "o", "r") + if err != nil { + t.Errorf("Activity.IsStarred returned error: %v", err) + } + if want := true; star != want { + t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) + } +} + +func TestActivityService_IsStarred_noStar(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + star, _, err := client.Activity.IsStarred(context.Background(), "o", "r") + if err != nil { + t.Errorf("Activity.IsStarred returned error: %v", err) + } + if want := false; star != want { + t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) + } +} + +func TestActivityService_IsStarred_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Activity.IsStarred(context.Background(), "%", "%") + testURLParseError(t, err) +} + +func TestActivityService_Star(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + _, err := client.Activity.Star(context.Background(), "o", "r") + if err != nil { + t.Errorf("Activity.Star returned error: %v", err) + } +} + +func TestActivityService_Star_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Activity.Star(context.Background(), "%", "%") + testURLParseError(t, err) +} + +func TestActivityService_Unstar(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Activity.Unstar(context.Background(), "o", "r") + if err != nil { + t.Errorf("Activity.Unstar returned error: %v", err) + } +} + +func TestActivityService_Unstar_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Activity.Unstar(context.Background(), "%", "%") + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/activity_test.go b/vendor/github.com/google/go-github/github/activity_test.go new file mode 100644 index 00000000..92b42d3a --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_test.go @@ -0,0 +1,129 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "net/http" + "reflect" + "testing" +) + +func TestActivityService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/feeds", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + w.Write(feedsJSON) + }) + + got, _, err := client.Activity.ListFeeds(context.Background()) + if err != nil { + t.Errorf("Activity.ListFeeds returned error: %v", err) + } + if want := wantFeeds; !reflect.DeepEqual(got, want) { + t.Errorf("Activity.ListFeeds = %+v, want %+v", got, want) + } +} + +var feedsJSON = []byte(`{ + "timeline_url": "https://github.com/timeline", + "user_url": "https://github.com/{user}", + "current_user_public_url": "https://github.com/defunkt", + "current_user_url": "https://github.com/defunkt.private?token=abc123", + "current_user_actor_url": "https://github.com/defunkt.private.actor?token=abc123", + "current_user_organization_url": "", + "current_user_organization_urls": [ + "https://github.com/organizations/github/defunkt.private.atom?token=abc123" + ], + "_links": { + "timeline": { + "href": "https://github.com/timeline", + "type": "application/atom+xml" + }, + "user": { + "href": "https://github.com/{user}", + "type": "application/atom+xml" + }, + "current_user_public": { + "href": "https://github.com/defunkt", + "type": "application/atom+xml" + }, + "current_user": { + "href": "https://github.com/defunkt.private?token=abc123", + "type": "application/atom+xml" + }, + "current_user_actor": { + "href": "https://github.com/defunkt.private.actor?token=abc123", + "type": "application/atom+xml" + }, + "current_user_organization": { + "href": "", + "type": "" + }, + "current_user_organizations": [ + { + "href": "https://github.com/organizations/github/defunkt.private.atom?token=abc123", + "type": "application/atom+xml" + } + ] + } +}`) + +var wantFeeds = &Feeds{ + TimelineURL: String("https://github.com/timeline"), + UserURL: String("https://github.com/{user}"), + CurrentUserPublicURL: String("https://github.com/defunkt"), + CurrentUserURL: String("https://github.com/defunkt.private?token=abc123"), + CurrentUserActorURL: String("https://github.com/defunkt.private.actor?token=abc123"), + CurrentUserOrganizationURL: String(""), + CurrentUserOrganizationURLs: []string{ + "https://github.com/organizations/github/defunkt.private.atom?token=abc123", + }, + Links: &struct { + Timeline *FeedLink `json:"timeline,omitempty"` + User *FeedLink `json:"user,omitempty"` + CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` + CurrentUser *FeedLink `json:"current_user,omitempty"` + CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` + CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` + CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` + }{ + Timeline: &FeedLink{ + HRef: String("https://github.com/timeline"), + Type: String("application/atom+xml"), + }, + User: &FeedLink{ + HRef: String("https://github.com/{user}"), + Type: String("application/atom+xml"), + }, + CurrentUserPublic: &FeedLink{ + HRef: String("https://github.com/defunkt"), + Type: String("application/atom+xml"), + }, + CurrentUser: &FeedLink{ + HRef: String("https://github.com/defunkt.private?token=abc123"), + Type: String("application/atom+xml"), + }, + CurrentUserActor: &FeedLink{ + HRef: String("https://github.com/defunkt.private.actor?token=abc123"), + Type: String("application/atom+xml"), + }, + CurrentUserOrganization: &FeedLink{ + HRef: String(""), + Type: String(""), + }, + CurrentUserOrganizations: []FeedLink{ + { + HRef: String("https://github.com/organizations/github/defunkt.private.atom?token=abc123"), + Type: String("application/atom+xml"), + }, + }, + }, +} diff --git a/vendor/github.com/google/go-github/github/activity_watching.go b/vendor/github.com/google/go-github/github/activity_watching.go new file mode 100644 index 00000000..c749ca86 --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_watching.go @@ -0,0 +1,146 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Subscription identifies a repository or thread subscription. +type Subscription struct { + Subscribed *bool `json:"subscribed,omitempty"` + Ignored *bool `json:"ignored,omitempty"` + Reason *string `json:"reason,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + URL *string `json:"url,omitempty"` + + // only populated for repository subscriptions + RepositoryURL *string `json:"repository_url,omitempty"` + + // only populated for thread subscriptions + ThreadURL *string `json:"thread_url,omitempty"` +} + +// ListWatchers lists watchers of a particular repo. +// +// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-watchers +func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var watchers []*User + resp, err := s.client.Do(ctx, req, &watchers) + if err != nil { + return nil, resp, err + } + + return watchers, resp, nil +} + +// ListWatched lists the repositories the specified user is watching. Passing +// the empty string will fetch watched repos for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched +func (s *ActivityService) ListWatched(ctx context.Context, user string, opt *ListOptions) ([]*Repository, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/subscriptions", user) + } else { + u = "user/subscriptions" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var watched []*Repository + resp, err := s.client.Do(ctx, req, &watched) + if err != nil { + return nil, resp, err + } + + return watched, resp, nil +} + +// GetRepositorySubscription returns the subscription for the specified +// repository for the authenticated user. If the authenticated user is not +// watching the repository, a nil Subscription is returned. +// +// GitHub API docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription +func (s *ActivityService) GetRepositorySubscription(ctx context.Context, owner, repo string) (*Subscription, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + sub := new(Subscription) + resp, err := s.client.Do(ctx, req, sub) + if err != nil { + // if it's just a 404, don't return that as an error + _, err = parseBoolResponse(err) + return nil, resp, err + } + + return sub, resp, nil +} + +// SetRepositorySubscription sets the subscription for the specified repository +// for the authenticated user. +// +// To watch a repository, set subscription.Subscribed to true. +// To ignore notifications made within a repository, set subscription.Ignored to true. +// To stop watching a repository, use DeleteRepositorySubscription. +// +// GitHub API docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription +func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, repo string, subscription *Subscription) (*Subscription, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) + + req, err := s.client.NewRequest("PUT", u, subscription) + if err != nil { + return nil, nil, err + } + + sub := new(Subscription) + resp, err := s.client.Do(ctx, req, sub) + if err != nil { + return nil, resp, err + } + + return sub, resp, nil +} + +// DeleteRepositorySubscription deletes the subscription for the specified +// repository for the authenticated user. +// +// This is used to stop watching a repository. To control whether or not to +// receive notifications from a repository, use SetRepositorySubscription. +// +// GitHub API docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription +func (s *ActivityService) DeleteRepositorySubscription(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/activity_watching_test.go b/vendor/github.com/google/go-github/github/activity_watching_test.go new file mode 100644 index 00000000..b017d8d5 --- /dev/null +++ b/vendor/github.com/google/go-github/github/activity_watching_test.go @@ -0,0 +1,184 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestActivityService_ListWatchers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/subscribers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + + fmt.Fprint(w, `[{"id":1}]`) + }) + + watchers, _, err := client.Activity.ListWatchers(context.Background(), "o", "r", &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Activity.ListWatchers returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(watchers, want) { + t.Errorf("Activity.ListWatchers returned %+v, want %+v", watchers, want) + } +} + +func TestActivityService_ListWatched_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/subscriptions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + watched, _, err := client.Activity.ListWatched(context.Background(), "", &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Activity.ListWatched returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(watched, want) { + t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) + } +} + +func TestActivityService_ListWatched_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/subscriptions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + watched, _, err := client.Activity.ListWatched(context.Background(), "u", &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Activity.ListWatched returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(watched, want) { + t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) + } +} + +func TestActivityService_GetRepositorySubscription_true(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"subscribed":true}`) + }) + + sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r") + if err != nil { + t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) + } + + want := &Subscription{Subscribed: Bool(true)} + if !reflect.DeepEqual(sub, want) { + t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) + } +} + +func TestActivityService_GetRepositorySubscription_false(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r") + if err != nil { + t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) + } + + var want *Subscription + if !reflect.DeepEqual(sub, want) { + t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) + } +} + +func TestActivityService_GetRepositorySubscription_error(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusBadRequest) + }) + + _, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r") + if err == nil { + t.Errorf("Expected HTTP 400 response") + } +} + +func TestActivityService_SetRepositorySubscription(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Subscription{Subscribed: Bool(true)} + + mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { + v := new(Subscription) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ignored":true}`) + }) + + sub, _, err := client.Activity.SetRepositorySubscription(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Activity.SetRepositorySubscription returned error: %v", err) + } + + want := &Subscription{Ignored: Bool(true)} + if !reflect.DeepEqual(sub, want) { + t.Errorf("Activity.SetRepositorySubscription returned %+v, want %+v", sub, want) + } +} + +func TestActivityService_DeleteRepositorySubscription(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Activity.DeleteRepositorySubscription(context.Background(), "o", "r") + if err != nil { + t.Errorf("Activity.DeleteRepositorySubscription returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/admin.go b/vendor/github.com/google/go-github/github/admin.go new file mode 100644 index 00000000..2d96733a --- /dev/null +++ b/vendor/github.com/google/go-github/github/admin.go @@ -0,0 +1,101 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// AdminService handles communication with the admin related methods of the +// GitHub API. These API routes are normally only accessible for GitHub +// Enterprise installations. +// +// GitHub API docs: https://developer.github.com/v3/enterprise/ +type AdminService service + +// TeamLDAPMapping represents the mapping between a GitHub team and an LDAP group. +type TeamLDAPMapping struct { + ID *int64 `json:"id,omitempty"` + LDAPDN *string `json:"ldap_dn,omitempty"` + URL *string `json:"url,omitempty"` + Name *string `json:"name,omitempty"` + Slug *string `json:"slug,omitempty"` + Description *string `json:"description,omitempty"` + Privacy *string `json:"privacy,omitempty"` + Permission *string `json:"permission,omitempty"` + + MembersURL *string `json:"members_url,omitempty"` + RepositoriesURL *string `json:"repositories_url,omitempty"` +} + +func (m TeamLDAPMapping) String() string { + return Stringify(m) +} + +// UserLDAPMapping represents the mapping between a GitHub user and an LDAP user. +type UserLDAPMapping struct { + ID *int64 `json:"id,omitempty"` + LDAPDN *string `json:"ldap_dn,omitempty"` + Login *string `json:"login,omitempty"` + AvatarURL *string `json:"avatar_url,omitempty"` + GravatarID *string `json:"gravatar_id,omitempty"` + Type *string `json:"type,omitempty"` + SiteAdmin *bool `json:"site_admin,omitempty"` + + URL *string `json:"url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + FollowingURL *string `json:"following_url,omitempty"` + FollowersURL *string `json:"followers_url,omitempty"` + GistsURL *string `json:"gists_url,omitempty"` + OrganizationsURL *string `json:"organizations_url,omitempty"` + ReceivedEventsURL *string `json:"received_events_url,omitempty"` + ReposURL *string `json:"repos_url,omitempty"` + StarredURL *string `json:"starred_url,omitempty"` + SubscriptionsURL *string `json:"subscriptions_url,omitempty"` +} + +func (m UserLDAPMapping) String() string { + return Stringify(m) +} + +// UpdateUserLDAPMapping updates the mapping between a GitHub user and an LDAP user. +// +// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user +func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, mapping *UserLDAPMapping) (*UserLDAPMapping, *Response, error) { + u := fmt.Sprintf("admin/ldap/users/%v/mapping", user) + req, err := s.client.NewRequest("PATCH", u, mapping) + if err != nil { + return nil, nil, err + } + + m := new(UserLDAPMapping) + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// UpdateTeamLDAPMapping updates the mapping between a GitHub team and an LDAP group. +// +// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team +func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int64, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) { + u := fmt.Sprintf("admin/ldap/teams/%v/mapping", team) + req, err := s.client.NewRequest("PATCH", u, mapping) + if err != nil { + return nil, nil, err + } + + m := new(TeamLDAPMapping) + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/admin_stats.go b/vendor/github.com/google/go-github/github/admin_stats.go new file mode 100644 index 00000000..1550d250 --- /dev/null +++ b/vendor/github.com/google/go-github/github/admin_stats.go @@ -0,0 +1,171 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// AdminStats represents a variety of stats of a Github Enterprise +// installation. +type AdminStats struct { + Issues *IssueStats `json:"issues,omitempty"` + Hooks *HookStats `json:"hooks,omitempty"` + Milestones *MilestoneStats `json:"milestones,omitempty"` + Orgs *OrgStats `json:"orgs,omitempty"` + Comments *CommentStats `json:"comments,omitempty"` + Pages *PageStats `json:"pages,omitempty"` + Users *UserStats `json:"users,omitempty"` + Gists *GistStats `json:"gists,omitempty"` + Pulls *PullStats `json:"pulls,omitempty"` + Repos *RepoStats `json:"repos,omitempty"` +} + +func (s AdminStats) String() string { + return Stringify(s) +} + +// IssueStats represents the number of total, open and closed issues. +type IssueStats struct { + TotalIssues *int `json:"total_issues,omitempty"` + OpenIssues *int `json:"open_issues,omitempty"` + ClosedIssues *int `json:"closed_issues,omitempty"` +} + +func (s IssueStats) String() string { + return Stringify(s) +} + +// HookStats represents the number of total, active and inactive hooks. +type HookStats struct { + TotalHooks *int `json:"total_hooks,omitempty"` + ActiveHooks *int `json:"active_hooks,omitempty"` + InactiveHooks *int `json:"inactive_hooks,omitempty"` +} + +func (s HookStats) String() string { + return Stringify(s) +} + +// MilestoneStats represents the number of total, open and close milestones. +type MilestoneStats struct { + TotalMilestones *int `json:"total_milestones,omitempty"` + OpenMilestones *int `json:"open_milestones,omitempty"` + ClosedMilestones *int `json:"closed_milestones,omitempty"` +} + +func (s MilestoneStats) String() string { + return Stringify(s) +} + +// OrgStats represents the number of total, disabled organizations and the team +// and team member count. +type OrgStats struct { + TotalOrgs *int `json:"total_orgs,omitempty"` + DisabledOrgs *int `json:"disabled_orgs,omitempty"` + TotalTeams *int `json:"total_teams,omitempty"` + TotalTeamMembers *int `json:"total_team_members,omitempty"` +} + +func (s OrgStats) String() string { + return Stringify(s) +} + +// CommentStats represents the number of total comments on commits, gists, issues +// and pull requests. +type CommentStats struct { + TotalCommitComments *int `json:"total_commit_comments,omitempty"` + TotalGistComments *int `json:"total_gist_comments,omitempty"` + TotalIssueComments *int `json:"total_issue_comments,omitempty"` + TotalPullRequestComments *int `json:"total_pull_request_comments,omitempty"` +} + +func (s CommentStats) String() string { + return Stringify(s) +} + +// PageStats represents the total number of github pages. +type PageStats struct { + TotalPages *int `json:"total_pages,omitempty"` +} + +func (s PageStats) String() string { + return Stringify(s) +} + +// UserStats represents the number of total, admin and suspended users. +type UserStats struct { + TotalUsers *int `json:"total_users,omitempty"` + AdminUsers *int `json:"admin_users,omitempty"` + SuspendedUsers *int `json:"suspended_users,omitempty"` +} + +func (s UserStats) String() string { + return Stringify(s) +} + +//GistStats represents the number of total, private and public gists. +type GistStats struct { + TotalGists *int `json:"total_gists,omitempty"` + PrivateGists *int `json:"private_gists,omitempty"` + PublicGists *int `json:"public_gists,omitempty"` +} + +func (s GistStats) String() string { + return Stringify(s) +} + +// PullStats represents the number of total, merged, mergable and unmergeable +// pull-requests. +type PullStats struct { + TotalPulls *int `json:"total_pulls,omitempty"` + MergedPulls *int `json:"merged_pulls,omitempty"` + MergablePulls *int `json:"mergeable_pulls,omitempty"` + UnmergablePulls *int `json:"unmergeable_pulls,omitempty"` +} + +func (s PullStats) String() string { + return Stringify(s) +} + +// RepoStats represents the number of total, root, fork, organization repositories +// together with the total number of pushes and wikis. +type RepoStats struct { + TotalRepos *int `json:"total_repos,omitempty"` + RootRepos *int `json:"root_repos,omitempty"` + ForkRepos *int `json:"fork_repos,omitempty"` + OrgRepos *int `json:"org_repos,omitempty"` + TotalPushes *int `json:"total_pushes,omitempty"` + TotalWikis *int `json:"total_wikis,omitempty"` +} + +func (s RepoStats) String() string { + return Stringify(s) +} + +// GetAdminStats returns a variety of metrics about a Github Enterprise +// installation. +// +// Please note that this is only available to site administrators, +// otherwise it will error with a 404 not found (instead of 401 or 403). +// +// GitHub API docs: https://developer.github.com/v3/enterprise-admin/admin_stats/ +func (s *AdminService) GetAdminStats(ctx context.Context) (*AdminStats, *Response, error) { + u := fmt.Sprintf("enterprise/stats/all") + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + m := new(AdminStats) + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/admin_stats_test.go b/vendor/github.com/google/go-github/github/admin_stats_test.go new file mode 100644 index 00000000..9433adc3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/admin_stats_test.go @@ -0,0 +1,142 @@ +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestAdminService_GetAdminStats(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/enterprise/stats/all", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, ` +{ + "repos": { + "total_repos": 212, + "root_repos": 194, + "fork_repos": 18, + "org_repos": 51, + "total_pushes": 3082, + "total_wikis": 15 + }, + "hooks": { + "total_hooks": 27, + "active_hooks": 23, + "inactive_hooks": 4 + }, + "pages": { + "total_pages": 36 + }, + "orgs": { + "total_orgs": 33, + "disabled_orgs": 0, + "total_teams": 60, + "total_team_members": 314 + }, + "users": { + "total_users": 254, + "admin_users": 45, + "suspended_users": 21 + }, + "pulls": { + "total_pulls": 86, + "merged_pulls": 60, + "mergeable_pulls": 21, + "unmergeable_pulls": 3 + }, + "issues": { + "total_issues": 179, + "open_issues": 83, + "closed_issues": 96 + }, + "milestones": { + "total_milestones": 7, + "open_milestones": 6, + "closed_milestones": 1 + }, + "gists": { + "total_gists": 178, + "private_gists": 151, + "public_gists": 25 + }, + "comments": { + "total_commit_comments": 6, + "total_gist_comments": 28, + "total_issue_comments": 366, + "total_pull_request_comments": 30 + } +} +`) + }) + + stats, _, err := client.Admin.GetAdminStats(context.Background()) + if err != nil { + t.Errorf("AdminService.GetAdminStats returned error: %v", err) + } + + want := &AdminStats{ + Repos: &RepoStats{ + TotalRepos: Int(212), + RootRepos: Int(194), + ForkRepos: Int(18), + OrgRepos: Int(51), + TotalPushes: Int(3082), + TotalWikis: Int(15), + }, + Hooks: &HookStats{ + TotalHooks: Int(27), + ActiveHooks: Int(23), + InactiveHooks: Int(4), + }, + Pages: &PageStats{ + TotalPages: Int(36), + }, + Orgs: &OrgStats{ + TotalOrgs: Int(33), + DisabledOrgs: Int(0), + TotalTeams: Int(60), + TotalTeamMembers: Int(314), + }, + Users: &UserStats{ + TotalUsers: Int(254), + AdminUsers: Int(45), + SuspendedUsers: Int(21), + }, + Pulls: &PullStats{ + TotalPulls: Int(86), + MergedPulls: Int(60), + MergablePulls: Int(21), + UnmergablePulls: Int(3), + }, + Issues: &IssueStats{ + TotalIssues: Int(179), + OpenIssues: Int(83), + ClosedIssues: Int(96), + }, + Milestones: &MilestoneStats{ + TotalMilestones: Int(7), + OpenMilestones: Int(6), + ClosedMilestones: Int(1), + }, + Gists: &GistStats{ + TotalGists: Int(178), + PrivateGists: Int(151), + PublicGists: Int(25), + }, + Comments: &CommentStats{ + TotalCommitComments: Int(6), + TotalGistComments: Int(28), + TotalIssueComments: Int(366), + TotalPullRequestComments: Int(30), + }, + } + if !reflect.DeepEqual(stats, want) { + t.Errorf("AdminService.GetAdminStats returned %+v, want %+v", stats, want) + } +} diff --git a/vendor/github.com/google/go-github/github/admin_test.go b/vendor/github.com/google/go-github/github/admin_test.go new file mode 100644 index 00000000..8979686f --- /dev/null +++ b/vendor/github.com/google/go-github/github/admin_test.go @@ -0,0 +1,81 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestAdminService_UpdateUserLDAPMapping(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &UserLDAPMapping{ + LDAPDN: String("uid=asdf,ou=users,dc=github,dc=com"), + } + + mux.HandleFunc("/admin/ldap/users/u/mapping", func(w http.ResponseWriter, r *http.Request) { + v := new(UserLDAPMapping) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `{"id":1,"ldap_dn":"uid=asdf,ou=users,dc=github,dc=com"}`) + }) + + mapping, _, err := client.Admin.UpdateUserLDAPMapping(context.Background(), "u", input) + if err != nil { + t.Errorf("Admin.UpdateUserLDAPMapping returned error: %v", err) + } + + want := &UserLDAPMapping{ + ID: Int64(1), + LDAPDN: String("uid=asdf,ou=users,dc=github,dc=com"), + } + if !reflect.DeepEqual(mapping, want) { + t.Errorf("Admin.UpdateUserLDAPMapping returned %+v, want %+v", mapping, want) + } +} + +func TestAdminService_UpdateTeamLDAPMapping(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &TeamLDAPMapping{ + LDAPDN: String("cn=Enterprise Ops,ou=teams,dc=github,dc=com"), + } + + mux.HandleFunc("/admin/ldap/teams/1/mapping", func(w http.ResponseWriter, r *http.Request) { + v := new(TeamLDAPMapping) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `{"id":1,"ldap_dn":"cn=Enterprise Ops,ou=teams,dc=github,dc=com"}`) + }) + + mapping, _, err := client.Admin.UpdateTeamLDAPMapping(context.Background(), 1, input) + if err != nil { + t.Errorf("Admin.UpdateTeamLDAPMapping returned error: %v", err) + } + + want := &TeamLDAPMapping{ + ID: Int64(1), + LDAPDN: String("cn=Enterprise Ops,ou=teams,dc=github,dc=com"), + } + if !reflect.DeepEqual(mapping, want) { + t.Errorf("Admin.UpdateTeamLDAPMapping returned %+v, want %+v", mapping, want) + } +} diff --git a/vendor/github.com/google/go-github/github/apps.go b/vendor/github.com/google/go-github/github/apps.go new file mode 100644 index 00000000..740642e6 --- /dev/null +++ b/vendor/github.com/google/go-github/github/apps.go @@ -0,0 +1,169 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// AppsService provides access to the installation related functions +// in the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/apps/ +type AppsService service + +// App represents a GitHub App. +type App struct { + ID *int64 `json:"id,omitempty"` + Owner *User `json:"owner,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + ExternalURL *string `json:"external_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` +} + +// InstallationToken represents an installation token. +type InstallationToken struct { + Token *string `json:"token,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` +} + +// Get a single GitHub App. Passing the empty string will get +// the authenticated GitHub App. +// +// Note: appSlug is just the URL-friendly name of your GitHub App. +// You can find this on the settings page for your GitHub App +// (e.g., https://github.com/settings/apps/:app_slug). +// +// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app +func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) { + var u string + if appSlug != "" { + u = fmt.Sprintf("apps/%v", appSlug) + } else { + u = "app" + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeIntegrationPreview) + + app := new(App) + resp, err := s.client.Do(ctx, req, app) + if err != nil { + return nil, resp, err + } + + return app, resp, nil +} + +// ListInstallations lists the installations that the current GitHub App has. +// +// GitHub API docs: https://developer.github.com/v3/apps/#find-installations +func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { + u, err := addOptions("app/installations", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeIntegrationPreview) + + var i []*Installation + resp, err := s.client.Do(ctx, req, &i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} + +// GetInstallation returns the specified installation. +// +// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation +func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) { + u := fmt.Sprintf("app/installations/%v", id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeIntegrationPreview) + + i := new(Installation) + resp, err := s.client.Do(ctx, req, i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} + +// ListUserInstallations lists installations that are accessible to the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user +func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { + u, err := addOptions("user/installations", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeIntegrationPreview) + + var i struct { + Installations []*Installation `json:"installations"` + } + resp, err := s.client.Do(ctx, req, &i) + if err != nil { + return nil, resp, err + } + + return i.Installations, resp, nil +} + +// CreateInstallationToken creates a new installation token. +// +// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token +func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64) (*InstallationToken, *Response, error) { + u := fmt.Sprintf("installations/%v/access_tokens", id) + + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeIntegrationPreview) + + t := new(InstallationToken) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/apps_installation.go b/vendor/github.com/google/go-github/github/apps_installation.go new file mode 100644 index 00000000..af85cb87 --- /dev/null +++ b/vendor/github.com/google/go-github/github/apps_installation.go @@ -0,0 +1,114 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Installation represents a GitHub Apps installation. +type Installation struct { + ID *int64 `json:"id,omitempty"` + Account *User `json:"account,omitempty"` + AccessTokensURL *string `json:"access_tokens_url,omitempty"` + RepositoriesURL *string `json:"repositories_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +func (i Installation) String() string { + return Stringify(i) +} + +// ListRepos lists the repositories that are accessible to the authenticated installation. +// +// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories +func (s *AppsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repository, *Response, error) { + u, err := addOptions("installation/repositories", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeIntegrationPreview) + + var r struct { + Repositories []*Repository `json:"repositories"` + } + resp, err := s.client.Do(ctx, req, &r) + if err != nil { + return nil, resp, err + } + + return r.Repositories, resp, nil +} + +// ListUserRepos lists repositories that are accessible +// to the authenticated user for an installation. +// +// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation +func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opt *ListOptions) ([]*Repository, *Response, error) { + u := fmt.Sprintf("user/installations/%v/repositories", id) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeIntegrationPreview) + + var r struct { + Repositories []*Repository `json:"repositories"` + } + resp, err := s.client.Do(ctx, req, &r) + if err != nil { + return nil, resp, err + } + + return r.Repositories, resp, nil +} + +// AddRepository adds a single repository to an installation. +// +// GitHub API docs: https://developer.github.com/v3/apps/installations/#add-repository-to-installation +func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) (*Repository, *Response, error) { + u := fmt.Sprintf("apps/installations/%v/repositories/%v", instID, repoID) + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, nil, err + } + + r := new(Repository) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// RemoveRepository removes a single repository from an installation. +// +// GitHub docs: https://developer.github.com/v3/apps/installations/#remove-repository-from-installation +func (s *AppsService) RemoveRepository(ctx context.Context, instID, repoID int64) (*Response, error) { + u := fmt.Sprintf("apps/installations/%v/repositories/%v", instID, repoID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/apps_installation_test.go b/vendor/github.com/google/go-github/github/apps_installation_test.go new file mode 100644 index 00000000..01358c00 --- /dev/null +++ b/vendor/github.com/google/go-github/github/apps_installation_test.go @@ -0,0 +1,101 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestAppsService_ListRepos(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/installation/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `{"repositories": [{"id":1}]}`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + repositories, _, err := client.Apps.ListRepos(context.Background(), opt) + if err != nil { + t.Errorf("Apps.ListRepos returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(repositories, want) { + t.Errorf("Apps.ListRepos returned %+v, want %+v", repositories, want) + } +} + +func TestAppsService_ListUserRepos(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/installations/1/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `{"repositories": [{"id":1}]}`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + repositories, _, err := client.Apps.ListUserRepos(context.Background(), 1, opt) + if err != nil { + t.Errorf("Apps.ListUserRepos returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(repositories, want) { + t.Errorf("Apps.ListUserRepos returned %+v, want %+v", repositories, want) + } +} + +func TestAppsService_AddRepository(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/apps/installations/1/repositories/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`) + }) + + repo, _, err := client.Apps.AddRepository(context.Background(), 1, 1) + if err != nil { + t.Errorf("Apps.AddRepository returned error: %v", err) + } + + want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}} + if !reflect.DeepEqual(repo, want) { + t.Errorf("AddRepository returned %+v, want %+v", repo, want) + } +} + +func TestAppsService_RemoveRepository(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/apps/installations/1/repositories/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Apps.RemoveRepository(context.Background(), 1, 1) + if err != nil { + t.Errorf("Apps.RemoveRepository returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/apps_marketplace.go b/vendor/github.com/google/go-github/github/apps_marketplace.go new file mode 100644 index 00000000..089cdbf7 --- /dev/null +++ b/vendor/github.com/google/go-github/github/apps_marketplace.go @@ -0,0 +1,180 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// MarketplaceService handles communication with the marketplace related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/ +type MarketplaceService struct { + client *Client + // Stubbed controls whether endpoints that return stubbed data are used + // instead of production endpoints. Stubbed data is fake data that's useful + // for testing your GitHub Apps. Stubbed data is hard-coded and will not + // change based on actual subscriptions. + // + // GitHub API docs: https://developer.github.com/v3/apps/marketplace/ + Stubbed bool +} + +// MarketplacePlan represents a GitHub Apps Marketplace Listing Plan. +type MarketplacePlan struct { + URL *string `json:"url,omitempty"` + AccountsURL *string `json:"accounts_url,omitempty"` + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + MonthlyPriceInCents *int `json:"monthly_price_in_cents,omitempty"` + YearlyPriceInCents *int `json:"yearly_price_in_cents,omitempty"` + PriceModel *string `json:"price_model,omitempty"` + UnitName *string `json:"unit_name,omitempty"` + Bullets *[]string `json:"bullets,omitempty"` +} + +// MarketplacePurchase represents a GitHub Apps Marketplace Purchase. +type MarketplacePurchase struct { + BillingCycle *string `json:"billing_cycle,omitempty"` + NextBillingDate *string `json:"next_billing_date,omitempty"` + UnitCount *int `json:"unit_count,omitempty"` + Plan *MarketplacePlan `json:"plan,omitempty"` + Account *MarketplacePlanAccount `json:"account,omitempty"` +} + +// MarketplacePlanAccount represents a GitHub Account (user or organization) on a specific plan. +type MarketplacePlanAccount struct { + URL *string `json:"url,omitempty"` + Type *string `json:"type,omitempty"` + ID *int64 `json:"id,omitempty"` + Login *string `json:"login,omitempty"` + Email *string `json:"email,omitempty"` + OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"` + MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` +} + +// ListPlans lists all plans for your Marketplace listing. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing +func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([]*MarketplacePlan, *Response, error) { + uri := s.marketplaceURI("plans") + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var plans []*MarketplacePlan + resp, err := s.client.Do(ctx, req, &plans) + if err != nil { + return nil, resp, err + } + + return plans, resp, nil +} + +// ListPlanAccountsForPlan lists all GitHub accounts (user or organization) on a specific plan. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan +func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { + uri := s.marketplaceURI(fmt.Sprintf("plans/%v/accounts", planID)) + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var accounts []*MarketplacePlanAccount + resp, err := s.client.Do(ctx, req, &accounts) + if err != nil { + return nil, resp, err + } + + return accounts, resp, nil +} + +// ListPlanAccountsForAccount lists all GitHub accounts (user or organization) associated with an account. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing +func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { + uri := s.marketplaceURI(fmt.Sprintf("accounts/%v", accountID)) + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var accounts []*MarketplacePlanAccount + resp, err := s.client.Do(ctx, req, &accounts) + if err != nil { + return nil, resp, err + } + + return accounts, resp, nil +} + +// ListMarketplacePurchasesForUser lists all GitHub marketplace purchases made by a user. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases +func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opt *ListOptions) ([]*MarketplacePurchase, *Response, error) { + uri := "user/marketplace_purchases" + if s.Stubbed { + uri = "user/marketplace_purchases/stubbed" + } + + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var purchases []*MarketplacePurchase + resp, err := s.client.Do(ctx, req, &purchases) + if err != nil { + return nil, resp, err + } + + return purchases, resp, nil +} + +func (s *MarketplaceService) marketplaceURI(endpoint string) string { + url := "marketplace_listing" + if s.Stubbed { + url = "marketplace_listing/stubbed" + } + return url + "/" + endpoint +} diff --git a/vendor/github.com/google/go-github/github/apps_marketplace_test.go b/vendor/github.com/google/go-github/github/apps_marketplace_test.go new file mode 100644 index 00000000..91f133a3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/apps_marketplace_test.go @@ -0,0 +1,202 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestMarketplaceService_ListPlans(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/plans", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + plans, _, err := client.Marketplace.ListPlans(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListPlans returned error: %v", err) + } + + want := []*MarketplacePlan{{ID: Int64(1)}} + if !reflect.DeepEqual(plans, want) { + t.Errorf("Marketplace.ListPlans returned %+v, want %+v", plans, want) + } +} + +func TestMarketplaceService_Stubbed_ListPlans(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/stubbed/plans", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + plans, _, err := client.Marketplace.ListPlans(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListPlans (Stubbed) returned error: %v", err) + } + + want := []*MarketplacePlan{{ID: Int64(1)}} + if !reflect.DeepEqual(plans, want) { + t.Errorf("Marketplace.ListPlans (Stubbed) returned %+v, want %+v", plans, want) + } +} + +func TestMarketplaceService_ListPlanAccountsForPlan(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForPlan returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int64(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForPlan returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_Stubbed_ListPlanAccountsForPlan(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/stubbed/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int64(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_ListPlanAccountsForAccount(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/accounts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForAccount returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int64(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForAccount returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_Stubbed_ListPlanAccountsForAccount(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/stubbed/accounts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int64(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_ListMarketplacePurchasesForUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/marketplace_purchases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"billing_cycle":"monthly"}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err) + } + + want := []*MarketplacePurchase{{BillingCycle: String("monthly")}} + if !reflect.DeepEqual(purchases, want) { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want) + } +} + +func TestMarketplaceService_Stubbed_ListMarketplacePurchasesForUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/marketplace_purchases/stubbed", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"billing_cycle":"monthly"}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err) + } + + want := []*MarketplacePurchase{{BillingCycle: String("monthly")}} + if !reflect.DeepEqual(purchases, want) { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want) + } +} diff --git a/vendor/github.com/google/go-github/github/apps_test.go b/vendor/github.com/google/go-github/github/apps_test.go new file mode 100644 index 00000000..f306e1a9 --- /dev/null +++ b/vendor/github.com/google/go-github/github/apps_test.go @@ -0,0 +1,150 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestAppsService_Get_authenticatedApp(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + app, _, err := client.Apps.Get(context.Background(), "") + if err != nil { + t.Errorf("Apps.Get returned error: %v", err) + } + + want := &App{ID: Int64(1)} + if !reflect.DeepEqual(app, want) { + t.Errorf("Apps.Get returned %+v, want %+v", app, want) + } +} + +func TestAppsService_Get_specifiedApp(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/apps/a", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + fmt.Fprint(w, `{"html_url":"https://github.com/apps/a"}`) + }) + + app, _, err := client.Apps.Get(context.Background(), "a") + if err != nil { + t.Errorf("Apps.Get returned error: %v", err) + } + + want := &App{HTMLURL: String("https://github.com/apps/a")} + if !reflect.DeepEqual(app, want) { + t.Errorf("Apps.Get returned %+v, want %+v", *app.HTMLURL, *want.HTMLURL) + } +} + +func TestAppsService_ListInstallations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/app/installations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + installations, _, err := client.Apps.ListInstallations(context.Background(), opt) + if err != nil { + t.Errorf("Apps.ListInstallations returned error: %v", err) + } + + want := []*Installation{{ID: Int64(1)}} + if !reflect.DeepEqual(installations, want) { + t.Errorf("Apps.ListInstallations returned %+v, want %+v", installations, want) + } +} + +func TestAppsService_GetInstallation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/app/installations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + installation, _, err := client.Apps.GetInstallation(context.Background(), 1) + if err != nil { + t.Errorf("Apps.GetInstallation returned error: %v", err) + } + + want := &Installation{ID: Int64(1)} + if !reflect.DeepEqual(installation, want) { + t.Errorf("Apps.GetInstallation returned %+v, want %+v", installation, want) + } +} + +func TestAppsService_ListUserInstallations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/installations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `{"installations":[{"id":1}]}`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + installations, _, err := client.Apps.ListUserInstallations(context.Background(), opt) + if err != nil { + t.Errorf("Apps.ListUserInstallations returned error: %v", err) + } + + want := []*Installation{{ID: Int64(1)}} + if !reflect.DeepEqual(installations, want) { + t.Errorf("Apps.ListUserInstallations returned %+v, want %+v", installations, want) + } +} + +func TestAppsService_CreateInstallationToken(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/installations/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeIntegrationPreview) + fmt.Fprint(w, `{"token":"t"}`) + }) + + token, _, err := client.Apps.CreateInstallationToken(context.Background(), 1) + if err != nil { + t.Errorf("Apps.CreateInstallationToken returned error: %v", err) + } + + want := &InstallationToken{Token: String("t")} + if !reflect.DeepEqual(token, want) { + t.Errorf("Apps.CreateInstallationToken returned %+v, want %+v", token, want) + } +} diff --git a/vendor/github.com/google/go-github/github/authorizations.go b/vendor/github.com/google/go-github/github/authorizations.go new file mode 100644 index 00000000..190205b0 --- /dev/null +++ b/vendor/github.com/google/go-github/github/authorizations.go @@ -0,0 +1,435 @@ +// Copyright 2015 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Scope models a GitHub authorization scope. +// +// GitHub API docs: https://developer.github.com/v3/oauth/#scopes +type Scope string + +// This is the set of scopes for GitHub API V3 +const ( + ScopeNone Scope = "(no scope)" // REVISIT: is this actually returned, or just a documentation artifact? + ScopeUser Scope = "user" + ScopeUserEmail Scope = "user:email" + ScopeUserFollow Scope = "user:follow" + ScopePublicRepo Scope = "public_repo" + ScopeRepo Scope = "repo" + ScopeRepoDeployment Scope = "repo_deployment" + ScopeRepoStatus Scope = "repo:status" + ScopeDeleteRepo Scope = "delete_repo" + ScopeNotifications Scope = "notifications" + ScopeGist Scope = "gist" + ScopeReadRepoHook Scope = "read:repo_hook" + ScopeWriteRepoHook Scope = "write:repo_hook" + ScopeAdminRepoHook Scope = "admin:repo_hook" + ScopeAdminOrgHook Scope = "admin:org_hook" + ScopeReadOrg Scope = "read:org" + ScopeWriteOrg Scope = "write:org" + ScopeAdminOrg Scope = "admin:org" + ScopeReadPublicKey Scope = "read:public_key" + ScopeWritePublicKey Scope = "write:public_key" + ScopeAdminPublicKey Scope = "admin:public_key" + ScopeReadGPGKey Scope = "read:gpg_key" + ScopeWriteGPGKey Scope = "write:gpg_key" + ScopeAdminGPGKey Scope = "admin:gpg_key" +) + +// AuthorizationsService handles communication with the authorization related +// methods of the GitHub API. +// +// This service requires HTTP Basic Authentication; it cannot be accessed using +// an OAuth token. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/ +type AuthorizationsService service + +// Authorization represents an individual GitHub authorization. +type Authorization struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + Scopes []Scope `json:"scopes,omitempty"` + Token *string `json:"token,omitempty"` + TokenLastEight *string `json:"token_last_eight,omitempty"` + HashedToken *string `json:"hashed_token,omitempty"` + App *AuthorizationApp `json:"app,omitempty"` + Note *string `json:"note,omitempty"` + NoteURL *string `json:"note_url,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` + + // User is only populated by the Check and Reset methods. + User *User `json:"user,omitempty"` +} + +func (a Authorization) String() string { + return Stringify(a) +} + +// AuthorizationApp represents an individual GitHub app (in the context of authorization). +type AuthorizationApp struct { + URL *string `json:"url,omitempty"` + Name *string `json:"name,omitempty"` + ClientID *string `json:"client_id,omitempty"` +} + +func (a AuthorizationApp) String() string { + return Stringify(a) +} + +// Grant represents an OAuth application that has been granted access to an account. +type Grant struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + App *AuthorizationApp `json:"app,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Scopes []string `json:"scopes,omitempty"` +} + +func (g Grant) String() string { + return Stringify(g) +} + +// AuthorizationRequest represents a request to create an authorization. +type AuthorizationRequest struct { + Scopes []Scope `json:"scopes,omitempty"` + Note *string `json:"note,omitempty"` + NoteURL *string `json:"note_url,omitempty"` + ClientID *string `json:"client_id,omitempty"` + ClientSecret *string `json:"client_secret,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +func (a AuthorizationRequest) String() string { + return Stringify(a) +} + +// AuthorizationUpdateRequest represents a request to update an authorization. +// +// Note that for any one update, you must only provide one of the "scopes" +// fields. That is, you may provide only one of "Scopes", or "AddScopes", or +// "RemoveScopes". +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization +type AuthorizationUpdateRequest struct { + Scopes []string `json:"scopes,omitempty"` + AddScopes []string `json:"add_scopes,omitempty"` + RemoveScopes []string `json:"remove_scopes,omitempty"` + Note *string `json:"note,omitempty"` + NoteURL *string `json:"note_url,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +func (a AuthorizationUpdateRequest) String() string { + return Stringify(a) +} + +// List the authorizations for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations +func (s *AuthorizationsService) List(ctx context.Context, opt *ListOptions) ([]*Authorization, *Response, error) { + u := "authorizations" + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var auths []*Authorization + resp, err := s.client.Do(ctx, req, &auths) + if err != nil { + return nil, resp, err + } + return auths, resp, nil +} + +// Get a single authorization. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization +func (s *AuthorizationsService) Get(ctx context.Context, id int64) (*Authorization, *Response, error) { + u := fmt.Sprintf("authorizations/%d", id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + return a, resp, nil +} + +// Create a new authorization for the specified OAuth application. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization +func (s *AuthorizationsService) Create(ctx context.Context, auth *AuthorizationRequest) (*Authorization, *Response, error) { + u := "authorizations" + + req, err := s.client.NewRequest("POST", u, auth) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + return a, resp, nil +} + +// GetOrCreateForApp creates a new authorization for the specified OAuth +// application, only if an authorization for that application doesn’t already +// exist for the user. +// +// If a new token is created, the HTTP status code will be "201 Created", and +// the returned Authorization.Token field will be populated. If an existing +// token is returned, the status code will be "200 OK" and the +// Authorization.Token field will be empty. +// +// clientID is the OAuth Client ID with which to create the token. +// +// GitHub API docs: +// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app +// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app-and-fingerprint +func (s *AuthorizationsService) GetOrCreateForApp(ctx context.Context, clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) { + var u string + if auth.Fingerprint == nil || *auth.Fingerprint == "" { + u = fmt.Sprintf("authorizations/clients/%v", clientID) + } else { + u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint) + } + + req, err := s.client.NewRequest("PUT", u, auth) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + +// Edit a single authorization. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization +func (s *AuthorizationsService) Edit(ctx context.Context, id int64, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) { + u := fmt.Sprintf("authorizations/%d", id) + + req, err := s.client.NewRequest("PATCH", u, auth) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + +// Delete a single authorization. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization +func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("authorizations/%d", id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Check if an OAuth token is valid for a specific app. +// +// Note that this operation requires the use of BasicAuth, but where the +// username is the OAuth application clientID, and the password is its +// clientSecret. Invalid tokens will return a 404 Not Found. +// +// The returned Authorization.User field will be populated. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization +func (s *AuthorizationsService) Check(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { + u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + +// Reset is used to reset a valid OAuth token without end user involvement. +// Applications must save the "token" property in the response, because changes +// take effect immediately. +// +// Note that this operation requires the use of BasicAuth, but where the +// username is the OAuth application clientID, and the password is its +// clientSecret. Invalid tokens will return a 404 Not Found. +// +// The returned Authorization.User field will be populated. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization +func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { + u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) + + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + +// Revoke an authorization for an application. +// +// Note that this operation requires the use of BasicAuth, but where the +// username is the OAuth application clientID, and the password is its +// clientSecret. Invalid tokens will return a 404 Not Found. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application +func (s *AuthorizationsService) Revoke(ctx context.Context, clientID string, token string) (*Response, error) { + u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ListGrants lists the set of OAuth applications that have been granted +// access to a user's account. This will return one entry for each application +// that has been granted access to the account, regardless of the number of +// tokens an application has generated for the user. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-grants +func (s *AuthorizationsService) ListGrants(ctx context.Context, opt *ListOptions) ([]*Grant, *Response, error) { + u, err := addOptions("applications/grants", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + grants := []*Grant{} + resp, err := s.client.Do(ctx, req, &grants) + if err != nil { + return nil, resp, err + } + + return grants, resp, nil +} + +// GetGrant gets a single OAuth application grant. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant +func (s *AuthorizationsService) GetGrant(ctx context.Context, id int64) (*Grant, *Response, error) { + u := fmt.Sprintf("applications/grants/%d", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + grant := new(Grant) + resp, err := s.client.Do(ctx, req, grant) + if err != nil { + return nil, resp, err + } + + return grant, resp, nil +} + +// DeleteGrant deletes an OAuth application grant. Deleting an application's +// grant will also delete all OAuth tokens associated with the application for +// the user. +// +// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant +func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("applications/grants/%d", id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// CreateImpersonation creates an impersonation OAuth token. +// +// This requires admin permissions. With the returned Authorization.Token +// you can e.g. create or delete a user's public SSH key. NOTE: creating a +// new token automatically revokes an existing one. +// +// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#create-an-impersonation-oauth-token +func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, username string, authReq *AuthorizationRequest) (*Authorization, *Response, error) { + u := fmt.Sprintf("admin/users/%v/authorizations", username) + req, err := s.client.NewRequest("POST", u, authReq) + if err != nil { + return nil, nil, err + } + + a := new(Authorization) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + return a, resp, nil +} + +// DeleteImpersonation deletes an impersonation OAuth token. +// +// NOTE: there can be only one at a time. +// +// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#delete-an-impersonation-oauth-token +func (s *AuthorizationsService) DeleteImpersonation(ctx context.Context, username string) (*Response, error) { + u := fmt.Sprintf("admin/users/%v/authorizations", username) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/authorizations_test.go b/vendor/github.com/google/go-github/github/authorizations_test.go new file mode 100644 index 00000000..faf25749 --- /dev/null +++ b/vendor/github.com/google/go-github/github/authorizations_test.go @@ -0,0 +1,359 @@ +// Copyright 2015 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestAuthorizationsService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1", "per_page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + got, _, err := client.Authorizations.List(context.Background(), opt) + if err != nil { + t.Errorf("Authorizations.List returned error: %v", err) + } + + want := []*Authorization{{ID: Int64(1)}} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID) + } +} + +func TestAuthorizationsService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + got, _, err := client.Authorizations.Get(context.Background(), 1) + if err != nil { + t.Errorf("Authorizations.Get returned error: %v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Create(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &AuthorizationRequest{ + Note: String("test"), + } + + mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.Create(context.Background(), input) + if err != nil { + t.Errorf("Authorizations.Create returned error: %v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.Create returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &AuthorizationRequest{ + Note: String("test"), + } + + mux.HandleFunc("/authorizations/clients/id", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.GetOrCreateForApp(context.Background(), "id", input) + if err != nil { + t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &AuthorizationRequest{ + Note: String("test"), + Fingerprint: String("fp"), + } + + mux.HandleFunc("/authorizations/clients/id/fp", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.GetOrCreateForApp(context.Background(), "id", input) + if err != nil { + t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Edit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &AuthorizationUpdateRequest{ + Note: String("test"), + } + + mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { + v := new(AuthorizationUpdateRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.Edit(context.Background(), 1, input) + if err != nil { + t.Errorf("Authorizations.Edit returned error: %v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorization.Update returned %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Delete(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Authorizations.Delete(context.Background(), 1) + if err != nil { + t.Errorf("Authorizations.Delete returned error: %v", err) + } +} + +func TestAuthorizationsService_Check(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + got, _, err := client.Authorizations.Check(context.Background(), "id", "t") + if err != nil { + t.Errorf("Authorizations.Check returned error: %v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Reset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"ID":1}`) + }) + + got, _, err := client.Authorizations.Reset(context.Background(), "id", "t") + if err != nil { + t.Errorf("Authorizations.Reset returned error: %v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want) + } +} + +func TestAuthorizationsService_Revoke(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Authorizations.Revoke(context.Background(), "id", "t") + if err != nil { + t.Errorf("Authorizations.Revoke returned error: %v", err) + } +} + +func TestListGrants(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id": 1}]`) + }) + + got, _, err := client.Authorizations.ListGrants(context.Background(), nil) + if err != nil { + t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err) + } + + want := []*Grant{{ID: Int64(1)}} + if !reflect.DeepEqual(got, want) { + t.Errorf("OAuthAuthorizations.ListGrants = %+v, want %+v", got, want) + } +} + +func TestListGrants_withOptions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id": 1}]`) + }) + + _, _, err := client.Authorizations.ListGrants(context.Background(), &ListOptions{Page: 2}) + if err != nil { + t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err) + } +} + +func TestGetGrant(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id": 1}`) + }) + + got, _, err := client.Authorizations.GetGrant(context.Background(), 1) + if err != nil { + t.Errorf("OAuthAuthorizations.GetGrant returned error: %v", err) + } + + want := &Grant{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("OAuthAuthorizations.GetGrant = %+v, want %+v", got, want) + } +} + +func TestDeleteGrant(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Authorizations.DeleteGrant(context.Background(), 1) + if err != nil { + t.Errorf("OAuthAuthorizations.DeleteGrant returned error: %v", err) + } +} + +func TestAuthorizationsService_CreateImpersonation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1}`) + }) + + req := &AuthorizationRequest{Scopes: []Scope{ScopePublicRepo}} + got, _, err := client.Authorizations.CreateImpersonation(context.Background(), "u", req) + if err != nil { + t.Errorf("Authorizations.CreateImpersonation returned error: %+v", err) + } + + want := &Authorization{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Authorizations.CreateImpersonation returned %+v, want %+v", *got.ID, *want.ID) + } +} + +func TestAuthorizationsService_DeleteImpersonation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Authorizations.DeleteImpersonation(context.Background(), "u") + if err != nil { + t.Errorf("Authorizations.DeleteImpersonation returned error: %+v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/doc.go b/vendor/github.com/google/go-github/github/doc.go new file mode 100644 index 00000000..4ba03cb3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/doc.go @@ -0,0 +1,191 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package github provides a client for using the GitHub API. + +Usage: + + import "github.com/google/go-github/github" + +Construct a new GitHub client, then use the various services on the client to +access different parts of the GitHub API. For example: + + client := github.NewClient(nil) + + // list all organizations for user "willnorris" + orgs, _, err := client.Organizations.List(ctx, "willnorris", nil) + +Some API methods have optional parameters that can be passed. For example: + + client := github.NewClient(nil) + + // list public repositories for org "github" + opt := &github.RepositoryListByOrgOptions{Type: "public"} + repos, _, err := client.Repositories.ListByOrg(ctx, "github", opt) + +The services of a client divide the API into logical chunks and correspond to +the structure of the GitHub API documentation at +https://developer.github.com/v3/. + +Authentication + +The go-github library does not directly handle authentication. Instead, when +creating a new client, pass an http.Client that can handle authentication for +you. The easiest and recommended way to do this is using the golang.org/x/oauth2 +library, but you can always use any other library that provides an http.Client. +If you have an OAuth2 access token (for example, a personal API token), you can +use it with the oauth2 library using: + + import "golang.org/x/oauth2" + + func main() { + ctx := context.Background() + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: "... your access token ..."}, + ) + tc := oauth2.NewClient(ctx, ts) + + client := github.NewClient(tc) + + // list all repositories for the authenticated user + repos, _, err := client.Repositories.List(ctx, "", nil) + } + +Note that when using an authenticated Client, all calls made by the client will +include the specified OAuth token. Therefore, authenticated clients should +almost never be shared between different users. + +See the oauth2 docs for complete instructions on using that library. + +For API methods that require HTTP Basic Authentication, use the +BasicAuthTransport. + +GitHub Apps authentication can be provided by the +https://github.com/bradleyfalzon/ghinstallation package. + + import "github.com/bradleyfalzon/ghinstallation" + + func main() { + // Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99. + itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem") + if err != nil { + // Handle error. + } + + // Use installation transport with client + client := github.NewClient(&http.Client{Transport: itr}) + + // Use client... + } + +Rate Limiting + +GitHub imposes a rate limit on all API clients. Unauthenticated clients are +limited to 60 requests per hour, while authenticated clients can make up to +5,000 requests per hour. The Search API has a custom rate limit. Unauthenticated +clients are limited to 10 requests per minute, while authenticated clients +can make up to 30 requests per minute. To receive the higher rate limit when +making calls that are not issued on behalf of a user, +use UnauthenticatedRateLimitedTransport. + +The returned Response.Rate value contains the rate limit information +from the most recent API call. If a recent enough response isn't +available, you can use RateLimits to fetch the most up-to-date rate +limit data for the client. + +To detect an API rate limit error, you can check if its type is *github.RateLimitError: + + repos, _, err := client.Repositories.List(ctx, "", nil) + if _, ok := err.(*github.RateLimitError); ok { + log.Println("hit rate limit") + } + +Learn more about GitHub rate limiting at +https://developer.github.com/v3/#rate-limiting. + +Accepted Status + +Some endpoints may return a 202 Accepted status code, meaning that the +information required is not yet ready and was scheduled to be gathered on +the GitHub side. Methods known to behave like this are documented specifying +this behavior. + +To detect this condition of error, you can check if its type is +*github.AcceptedError: + + stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo) + if _, ok := err.(*github.AcceptedError); ok { + log.Println("scheduled on GitHub side") + } + +Conditional Requests + +The GitHub API has good support for conditional requests which will help +prevent you from burning through your rate limit, as well as help speed up your +application. go-github does not handle conditional requests directly, but is +instead designed to work with a caching http.Transport. We recommend using +https://github.com/gregjones/httpcache for that. + +Learn more about GitHub conditional requests at +https://developer.github.com/v3/#conditional-requests. + +Creating and Updating Resources + +All structs for GitHub resources use pointer values for all non-repeated fields. +This allows distinguishing between unset fields and those set to a zero-value. +Helper functions have been provided to easily create these pointers for string, +bool, and int values. For example: + + // create a new private repository named "foo" + repo := &github.Repository{ + Name: github.String("foo"), + Private: github.Bool(true), + } + client.Repositories.Create(ctx, "", repo) + +Users who have worked with protocol buffers should find this pattern familiar. + +Pagination + +All requests for resource collections (repos, pull requests, issues, etc.) +support pagination. Pagination options are described in the +github.ListOptions struct and passed to the list methods directly or as an +embedded type of a more specific list options struct (for example +github.PullRequestListOptions). Pages information is available via the +github.Response struct. + + client := github.NewClient(nil) + + opt := &github.RepositoryListByOrgOptions{ + ListOptions: github.ListOptions{PerPage: 10}, + } + // get all pages of results + var allRepos []*github.Repository + for { + repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt) + if err != nil { + return err + } + allRepos = append(allRepos, repos...) + if resp.NextPage == 0 { + break + } + opt.Page = resp.NextPage + } + +Google App Engine + +Go on App Engine Classic (which as of this writing uses Go 1.6) can not use +the "context" import and still relies on "golang.org/x/net/context". +As a result, if you wish to continue to use "go-github" on App Engine Classic, +you will need to rewrite all the "context" imports using the following command: + + gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go + +See "with_appengine.go" for more details. + +*/ +package github diff --git a/vendor/github.com/google/go-github/github/event_types.go b/vendor/github.com/google/go-github/github/event_types.go new file mode 100644 index 00000000..046ba513 --- /dev/null +++ b/vendor/github.com/google/go-github/github/event_types.go @@ -0,0 +1,748 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// These event types are shared between the Events API and used as Webhook payloads. + +package github + +// CommitCommentEvent is triggered when a commit comment is created. +// The Webhook event name is "commit_comment". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#commitcommentevent +type CommitCommentEvent struct { + Comment *RepositoryComment `json:"comment,omitempty"` + + // The following fields are only populated by Webhook events. + Action *string `json:"action,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// CreateEvent represents a created repository, branch, or tag. +// The Webhook event name is "create". +// +// Note: webhooks will not receive this event for created repositories. +// Additionally, webhooks will not receive this event for tags if more +// than three tags are pushed at once. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#createevent +type CreateEvent struct { + Ref *string `json:"ref,omitempty"` + // RefType is the object that was created. Possible values are: "repository", "branch", "tag". + RefType *string `json:"ref_type,omitempty"` + MasterBranch *string `json:"master_branch,omitempty"` + Description *string `json:"description,omitempty"` + + // The following fields are only populated by Webhook events. + PusherType *string `json:"pusher_type,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// DeleteEvent represents a deleted branch or tag. +// The Webhook event name is "delete". +// +// Note: webhooks will not receive this event for tags if more than three tags +// are deleted at once. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deleteevent +type DeleteEvent struct { + Ref *string `json:"ref,omitempty"` + // RefType is the object that was deleted. Possible values are: "branch", "tag". + RefType *string `json:"ref_type,omitempty"` + + // The following fields are only populated by Webhook events. + PusherType *string `json:"pusher_type,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// DeploymentEvent represents a deployment. +// The Webhook event name is "deployment". +// +// Events of this type are not visible in timelines, they are only used to trigger hooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentevent +type DeploymentEvent struct { + Deployment *Deployment `json:"deployment,omitempty"` + Repo *Repository `json:"repository,omitempty"` + + // The following fields are only populated by Webhook events. + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// DeploymentStatusEvent represents a deployment status. +// The Webhook event name is "deployment_status". +// +// Events of this type are not visible in timelines, they are only used to trigger hooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentstatusevent +type DeploymentStatusEvent struct { + Deployment *Deployment `json:"deployment,omitempty"` + DeploymentStatus *DeploymentStatus `json:"deployment_status,omitempty"` + Repo *Repository `json:"repository,omitempty"` + + // The following fields are only populated by Webhook events. + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// ForkEvent is triggered when a user forks a repository. +// The Webhook event name is "fork". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#forkevent +type ForkEvent struct { + // Forkee is the created repository. + Forkee *Repository `json:"forkee,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// Page represents a single Wiki page. +type Page struct { + PageName *string `json:"page_name,omitempty"` + Title *string `json:"title,omitempty"` + Summary *string `json:"summary,omitempty"` + Action *string `json:"action,omitempty"` + SHA *string `json:"sha,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +// GollumEvent is triggered when a Wiki page is created or updated. +// The Webhook event name is "gollum". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#gollumevent +type GollumEvent struct { + Pages []*Page `json:"pages,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// EditChange represents the changes when an issue, pull request, or comment has +// been edited. +type EditChange struct { + Title *struct { + From *string `json:"from,omitempty"` + } `json:"title,omitempty"` + Body *struct { + From *string `json:"from,omitempty"` + } `json:"body,omitempty"` +} + +// ProjectChange represents the changes when a project has been edited. +type ProjectChange struct { + Name *struct { + From *string `json:"from,omitempty"` + } `json:"name,omitempty"` + Body *struct { + From *string `json:"from,omitempty"` + } `json:"body,omitempty"` +} + +// ProjectCardChange represents the changes when a project card has been edited. +type ProjectCardChange struct { + Note *struct { + From *string `json:"from,omitempty"` + } `json:"note,omitempty"` +} + +// ProjectColumnChange represents the changes when a project column has been edited. +type ProjectColumnChange struct { + Name *struct { + From *string `json:"from,omitempty"` + } `json:"name,omitempty"` +} + +// TeamChange represents the changes when a team has been edited. +type TeamChange struct { + Description *struct { + From *string `json:"from,omitempty"` + } `json:"description,omitempty"` + Name *struct { + From *string `json:"from,omitempty"` + } `json:"name,omitempty"` + Privacy *struct { + From *string `json:"from,omitempty"` + } `json:"privacy,omitempty"` + Repository *struct { + Permissions *struct { + From *struct { + Admin *bool `json:"admin,omitempty"` + Pull *bool `json:"pull,omitempty"` + Push *bool `json:"push,omitempty"` + } `json:"from,omitempty"` + } `json:"permissions,omitempty"` + } `json:"repository,omitempty"` +} + +// InstallationEvent is triggered when a GitHub App has been installed or uninstalled. +// The Webhook event name is "installation". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationevent +type InstallationEvent struct { + // The action that was performed. Can be either "created" or "deleted". + Action *string `json:"action,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// InstallationRepositoriesEvent is triggered when a repository is added or +// removed from an installation. The Webhook event name is "installation_repositories". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationrepositoriesevent +type InstallationRepositoriesEvent struct { + // The action that was performed. Can be either "added" or "removed". + Action *string `json:"action,omitempty"` + RepositoriesAdded []*Repository `json:"repositories_added,omitempty"` + RepositoriesRemoved []*Repository `json:"repositories_removed,omitempty"` + RepositorySelection *string `json:"repository_selection,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// IssueCommentEvent is triggered when an issue comment is created on an issue +// or pull request. +// The Webhook event name is "issue_comment". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuecommentevent +type IssueCommentEvent struct { + // Action is the action that was performed on the comment. + // Possible values are: "created", "edited", "deleted". + Action *string `json:"action,omitempty"` + Issue *Issue `json:"issue,omitempty"` + Comment *IssueComment `json:"comment,omitempty"` + + // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// IssuesEvent is triggered when an issue is assigned, unassigned, labeled, +// unlabeled, opened, closed, or reopened. +// The Webhook event name is "issues". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuesevent +type IssuesEvent struct { + // Action is the action that was performed. Possible values are: "assigned", + // "unassigned", "labeled", "unlabeled", "opened", "closed", "reopened", "edited". + Action *string `json:"action,omitempty"` + Issue *Issue `json:"issue,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Label *Label `json:"label,omitempty"` + + // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// LabelEvent is triggered when a repository's label is created, edited, or deleted. +// The Webhook event name is "label" +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#labelevent +type LabelEvent struct { + // Action is the action that was performed. Possible values are: + // "created", "edited", "deleted" + Action *string `json:"action,omitempty"` + Label *Label `json:"label,omitempty"` + + // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// MarketplacePurchaseEvent is triggered when a user purchases, cancels, or changes +// their GitHub Marketplace plan. +// Webhook event name "marketplace_purchase". +// +// Github API docs: https://developer.github.com/v3/activity/events/types/#marketplacepurchaseevent +type MarketplacePurchaseEvent struct { + // Action is the action that was performed. Possible values are: + // "purchased", "cancelled", "changed". + Action *string `json:"action,omitempty"` + + // The following fields are only populated by Webhook events. + EffectiveDate *Timestamp `json:"effective_date,omitempty"` + MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` + PreviousMarketplacePurchase *MarketplacePurchase `json:"previous_marketplace_purchase,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// MemberEvent is triggered when a user is added as a collaborator to a repository. +// The Webhook event name is "member". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#memberevent +type MemberEvent struct { + // Action is the action that was performed. Possible value is: "added". + Action *string `json:"action,omitempty"` + Member *User `json:"member,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// MembershipEvent is triggered when a user is added or removed from a team. +// The Webhook event name is "membership". +// +// Events of this type are not visible in timelines, they are only used to +// trigger organization webhooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#membershipevent +type MembershipEvent struct { + // Action is the action that was performed. Possible values are: "added", "removed". + Action *string `json:"action,omitempty"` + // Scope is the scope of the membership. Possible value is: "team". + Scope *string `json:"scope,omitempty"` + Member *User `json:"member,omitempty"` + Team *Team `json:"team,omitempty"` + + // The following fields are only populated by Webhook events. + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted. +// The Webhook event name is "milestone". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#milestoneevent +type MilestoneEvent struct { + // Action is the action that was performed. Possible values are: + // "created", "closed", "opened", "edited", "deleted" + Action *string `json:"action,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + + // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Org *Organization `json:"organization,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// OrganizationEvent is triggered when a user is added, removed, or invited to an organization. +// Events of this type are not visible in timelines. These events are only used to trigger organization hooks. +// Webhook event name is "organization". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#organizationevent +type OrganizationEvent struct { + // Action is the action that was performed. + // Can be one of "member_added", "member_removed", or "member_invited". + Action *string `json:"action,omitempty"` + + // Invitaion is the invitation for the user or email if the action is "member_invited". + Invitation *Invitation `json:"invitation,omitempty"` + + // Membership is the membership between the user and the organization. + // Not present when the action is "member_invited". + Membership *Membership `json:"membership,omitempty"` + + Organization *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// OrgBlockEvent is triggered when an organization blocks or unblocks a user. +// The Webhook event name is "org_block". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#orgblockevent +type OrgBlockEvent struct { + // Action is the action that was performed. + // Can be "blocked" or "unblocked". + Action *string `json:"action,omitempty"` + BlockedUser *User `json:"blocked_user,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + + // The following fields are only populated by Webhook events. + Installation *Installation `json:"installation,omitempty"` +} + +// PageBuildEvent represents an attempted build of a GitHub Pages site, whether +// successful or not. +// The Webhook event name is "page_build". +// +// This event is triggered on push to a GitHub Pages enabled branch (gh-pages +// for project pages, master for user and organization pages). +// +// Events of this type are not visible in timelines, they are only used to trigger hooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pagebuildevent +type PageBuildEvent struct { + Build *PagesBuild `json:"build,omitempty"` + + // The following fields are only populated by Webhook events. + ID *int64 `json:"id,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// PingEvent is triggered when a Webhook is added to GitHub. +// +// GitHub API docs: https://developer.github.com/webhooks/#ping-event +type PingEvent struct { + // Random string of GitHub zen. + Zen *string `json:"zen,omitempty"` + // The ID of the webhook that triggered the ping. + HookID *int64 `json:"hook_id,omitempty"` + // The webhook configuration. + Hook *Hook `json:"hook,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// ProjectEvent is triggered when project is created, modified or deleted. +// The webhook event name is "project". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectevent +type ProjectEvent struct { + Action *string `json:"action,omitempty"` + Changes *ProjectChange `json:"changes,omitempty"` + Project *Project `json:"project,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// ProjectCardEvent is triggered when a project card is created, updated, moved, converted to an issue, or deleted. +// The webhook event name is "project_card". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcardevent +type ProjectCardEvent struct { + Action *string `json:"action,omitempty"` + Changes *ProjectCardChange `json:"changes,omitempty"` + AfterID *int64 `json:"after_id,omitempty"` + ProjectCard *ProjectCard `json:"project_card,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// ProjectColumnEvent is triggered when a project column is created, updated, moved, or deleted. +// The webhook event name is "project_column". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcolumnevent +type ProjectColumnEvent struct { + Action *string `json:"action,omitempty"` + Changes *ProjectColumnChange `json:"changes,omitempty"` + AfterID *int64 `json:"after_id,omitempty"` + ProjectColumn *ProjectColumn `json:"project_column,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// PublicEvent is triggered when a private repository is open sourced. +// According to GitHub: "Without a doubt: the best GitHub event." +// The Webhook event name is "public". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#publicevent +type PublicEvent struct { + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// PullRequestEvent is triggered when a pull request is assigned, unassigned, +// labeled, unlabeled, opened, closed, reopened, or synchronized. +// The Webhook event name is "pull_request". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent +type PullRequestEvent struct { + // Action is the action that was performed. Possible values are: + // "assigned", "unassigned", "review_requested", "review_request_removed", "labeled", "unlabeled", + // "opened", "closed", "reopened", "synchronize", "edited". + // If the action is "closed" and the merged key is false, + // the pull request was closed with unmerged commits. If the action is "closed" + // and the merged key is true, the pull request was merged. + Action *string `json:"action,omitempty"` + Number *int `json:"number,omitempty"` + PullRequest *PullRequest `json:"pull_request,omitempty"` + + // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` + RequestedReviewers []*User `json:"requested_reviewers,omitempty"` // Populated in "review_requested", "review_request_removed" event deliveries. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// PullRequestReviewEvent is triggered when a review is submitted on a pull +// request. +// The Webhook event name is "pull_request_review". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent +type PullRequestReviewEvent struct { + // Action is always "submitted". + Action *string `json:"action,omitempty"` + Review *PullRequestReview `json:"review,omitempty"` + PullRequest *PullRequest `json:"pull_request,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Organization *Organization `json:"organization,omitempty"` +} + +// PullRequestReviewCommentEvent is triggered when a comment is created on a +// portion of the unified diff of a pull request. +// The Webhook event name is "pull_request_review_comment". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent +type PullRequestReviewCommentEvent struct { + // Action is the action that was performed on the comment. + // Possible values are: "created", "edited", "deleted". + Action *string `json:"action,omitempty"` + PullRequest *PullRequest `json:"pull_request,omitempty"` + Comment *PullRequestComment `json:"comment,omitempty"` + + // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// PushEvent represents a git push to a GitHub repository. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent +type PushEvent struct { + PushID *int64 `json:"push_id,omitempty"` + Head *string `json:"head,omitempty"` + Ref *string `json:"ref,omitempty"` + Size *int `json:"size,omitempty"` + Commits []PushEventCommit `json:"commits,omitempty"` + Before *string `json:"before,omitempty"` + DistinctSize *int `json:"distinct_size,omitempty"` + + // The following fields are only populated by Webhook events. + After *string `json:"after,omitempty"` + Created *bool `json:"created,omitempty"` + Deleted *bool `json:"deleted,omitempty"` + Forced *bool `json:"forced,omitempty"` + BaseRef *string `json:"base_ref,omitempty"` + Compare *string `json:"compare,omitempty"` + Repo *PushEventRepository `json:"repository,omitempty"` + HeadCommit *PushEventCommit `json:"head_commit,omitempty"` + Pusher *User `json:"pusher,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +func (p PushEvent) String() string { + return Stringify(p) +} + +// PushEventCommit represents a git commit in a GitHub PushEvent. +type PushEventCommit struct { + Message *string `json:"message,omitempty"` + Author *CommitAuthor `json:"author,omitempty"` + URL *string `json:"url,omitempty"` + Distinct *bool `json:"distinct,omitempty"` + + // The following fields are only populated by Events API. + SHA *string `json:"sha,omitempty"` + + // The following fields are only populated by Webhook events. + ID *string `json:"id,omitempty"` + TreeID *string `json:"tree_id,omitempty"` + Timestamp *Timestamp `json:"timestamp,omitempty"` + Committer *CommitAuthor `json:"committer,omitempty"` + Added []string `json:"added,omitempty"` + Removed []string `json:"removed,omitempty"` + Modified []string `json:"modified,omitempty"` +} + +func (p PushEventCommit) String() string { + return Stringify(p) +} + +// PushEventRepository represents the repo object in a PushEvent payload. +type PushEventRepository struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + FullName *string `json:"full_name,omitempty"` + Owner *PushEventRepoOwner `json:"owner,omitempty"` + Private *bool `json:"private,omitempty"` + Description *string `json:"description,omitempty"` + Fork *bool `json:"fork,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + PushedAt *Timestamp `json:"pushed_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Homepage *string `json:"homepage,omitempty"` + Size *int `json:"size,omitempty"` + StargazersCount *int `json:"stargazers_count,omitempty"` + WatchersCount *int `json:"watchers_count,omitempty"` + Language *string `json:"language,omitempty"` + HasIssues *bool `json:"has_issues,omitempty"` + HasDownloads *bool `json:"has_downloads,omitempty"` + HasWiki *bool `json:"has_wiki,omitempty"` + HasPages *bool `json:"has_pages,omitempty"` + ForksCount *int `json:"forks_count,omitempty"` + OpenIssuesCount *int `json:"open_issues_count,omitempty"` + DefaultBranch *string `json:"default_branch,omitempty"` + MasterBranch *string `json:"master_branch,omitempty"` + Organization *string `json:"organization,omitempty"` + URL *string `json:"url,omitempty"` + ArchiveURL *string `json:"archive_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + StatusesURL *string `json:"statuses_url,omitempty"` + GitURL *string `json:"git_url,omitempty"` + SSHURL *string `json:"ssh_url,omitempty"` + CloneURL *string `json:"clone_url,omitempty"` + SVNURL *string `json:"svn_url,omitempty"` +} + +// PushEventRepoOwner is a basic representation of user/org in a PushEvent payload. +type PushEventRepoOwner struct { + Name *string `json:"name,omitempty"` + Email *string `json:"email,omitempty"` +} + +// ReleaseEvent is triggered when a release is published. +// The Webhook event name is "release". +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#releaseevent +type ReleaseEvent struct { + // Action is the action that was performed. Possible value is: "published". + Action *string `json:"action,omitempty"` + Release *RepositoryRelease `json:"release,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// RepositoryEvent is triggered when a repository is created. +// The Webhook event name is "repository". +// +// Events of this type are not visible in timelines, they are only used to +// trigger organization webhooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryevent +type RepositoryEvent struct { + // Action is the action that was performed. Possible values are: "created", "deleted", + // "publicized", "privatized". + Action *string `json:"action,omitempty"` + Repo *Repository `json:"repository,omitempty"` + + // The following fields are only populated by Webhook events. + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// StatusEvent is triggered when the status of a Git commit changes. +// The Webhook event name is "status". +// +// Events of this type are not visible in timelines, they are only used to +// trigger hooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#statusevent +type StatusEvent struct { + SHA *string `json:"sha,omitempty"` + // State is the new state. Possible values are: "pending", "success", "failure", "error". + State *string `json:"state,omitempty"` + Description *string `json:"description,omitempty"` + TargetURL *string `json:"target_url,omitempty"` + Branches []*Branch `json:"branches,omitempty"` + + // The following fields are only populated by Webhook events. + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Context *string `json:"context,omitempty"` + Commit *RepositoryCommit `json:"commit,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// TeamEvent is triggered when an organization's team is created, modified or deleted. +// The Webhook event name is "team". +// +// Events of this type are not visible in timelines. These events are only used +// to trigger hooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamevent +type TeamEvent struct { + Action *string `json:"action,omitempty"` + Team *Team `json:"team,omitempty"` + Changes *TeamChange `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + + // The following fields are only populated by Webhook events. + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// TeamAddEvent is triggered when a repository is added to a team. +// The Webhook event name is "team_add". +// +// Events of this type are not visible in timelines. These events are only used +// to trigger hooks. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamaddevent +type TeamAddEvent struct { + Team *Team `json:"team,omitempty"` + Repo *Repository `json:"repository,omitempty"` + + // The following fields are only populated by Webhook events. + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// WatchEvent is related to starring a repository, not watching. See this API +// blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/ +// +// The event’s actor is the user who starred a repository, and the event’s +// repository is the repository that was starred. +// +// GitHub API docs: https://developer.github.com/v3/activity/events/types/#watchevent +type WatchEvent struct { + // Action is the action that was performed. Possible value is: "started". + Action *string `json:"action,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} diff --git a/vendor/github.com/google/go-github/github/examples_test.go b/vendor/github.com/google/go-github/github/examples_test.go new file mode 100644 index 00000000..f09d6505 --- /dev/null +++ b/vendor/github.com/google/go-github/github/examples_test.go @@ -0,0 +1,76 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github_test + +import ( + "context" + "fmt" + "log" + + "github.com/google/go-github/github" +) + +func ExampleClient_Markdown() { + client := github.NewClient(nil) + + input := "# heading #\n\nLink to issue #1" + opt := &github.MarkdownOptions{Mode: "gfm", Context: "google/go-github"} + + output, _, err := client.Markdown(context.Background(), input, opt) + if err != nil { + fmt.Println(err) + } + + fmt.Println(output) +} + +func ExampleRepositoriesService_GetReadme() { + client := github.NewClient(nil) + + readme, _, err := client.Repositories.GetReadme(context.Background(), "google", "go-github", nil) + if err != nil { + fmt.Println(err) + return + } + + content, err := readme.GetContent() + if err != nil { + fmt.Println(err) + return + } + + fmt.Printf("google/go-github README:\n%v\n", content) +} + +func ExampleRepositoriesService_List() { + client := github.NewClient(nil) + + user := "willnorris" + opt := &github.RepositoryListOptions{Type: "owner", Sort: "updated", Direction: "desc"} + + repos, _, err := client.Repositories.List(context.Background(), user, opt) + if err != nil { + fmt.Println(err) + } + + fmt.Printf("Recently updated repositories by %q: %v", user, github.Stringify(repos)) +} + +func ExampleUsersService_ListAll() { + client := github.NewClient(nil) + opts := &github.UserListOptions{} + for { + users, _, err := client.Users.ListAll(context.Background(), opts) + if err != nil { + log.Fatalf("error listing users: %v", err) + } + if len(users) == 0 { + break + } + opts.Since = *users[len(users)-1].ID + // Process users... + } +} diff --git a/vendor/github.com/google/go-github/github/gen-accessors.go b/vendor/github.com/google/go-github/github/gen-accessors.go new file mode 100644 index 00000000..fe92206f --- /dev/null +++ b/vendor/github.com/google/go-github/github/gen-accessors.go @@ -0,0 +1,332 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// gen-accessors generates accessor methods for structs with pointer fields. +// +// It is meant to be used by the go-github authors in conjunction with the +// go generate tool before sending a commit to GitHub. +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io/ioutil" + "log" + "os" + "sort" + "strings" + "text/template" +) + +const ( + fileSuffix = "-accessors.go" +) + +var ( + verbose = flag.Bool("v", false, "Print verbose log messages") + + sourceTmpl = template.Must(template.New("source").Parse(source)) + + // blacklistStructMethod lists "struct.method" combos to skip. + blacklistStructMethod = map[string]bool{ + "RepositoryContent.GetContent": true, + "Client.GetBaseURL": true, + "Client.GetUploadURL": true, + "ErrorResponse.GetResponse": true, + "RateLimitError.GetResponse": true, + "AbuseRateLimitError.GetResponse": true, + } + // blacklistStruct lists structs to skip. + blacklistStruct = map[string]bool{ + "Client": true, + } +) + +func logf(fmt string, args ...interface{}) { + if *verbose { + log.Printf(fmt, args...) + } +} + +func main() { + flag.Parse() + fset := token.NewFileSet() + + pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0) + if err != nil { + log.Fatal(err) + return + } + + for pkgName, pkg := range pkgs { + t := &templateData{ + filename: pkgName + fileSuffix, + Year: 2017, + Package: pkgName, + Imports: map[string]string{}, + } + for filename, f := range pkg.Files { + logf("Processing %v...", filename) + if err := t.processAST(f); err != nil { + log.Fatal(err) + } + } + if err := t.dump(); err != nil { + log.Fatal(err) + } + } + logf("Done.") +} + +func (t *templateData) processAST(f *ast.File) error { + for _, decl := range f.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + // Skip unexported identifiers. + if !ts.Name.IsExported() { + logf("Struct %v is unexported; skipping.", ts.Name) + continue + } + // Check if the struct is blacklisted. + if blacklistStruct[ts.Name.Name] { + logf("Struct %v is blacklisted; skipping.", ts.Name) + continue + } + st, ok := ts.Type.(*ast.StructType) + if !ok { + continue + } + for _, field := range st.Fields.List { + se, ok := field.Type.(*ast.StarExpr) + if len(field.Names) == 0 || !ok { + continue + } + + fieldName := field.Names[0] + // Skip unexported identifiers. + if !fieldName.IsExported() { + logf("Field %v is unexported; skipping.", fieldName) + continue + } + // Check if "struct.method" is blacklisted. + if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] { + logf("Method %v is blacklisted; skipping.", key) + continue + } + + switch x := se.X.(type) { + case *ast.ArrayType: + t.addArrayType(x, ts.Name.String(), fieldName.String()) + case *ast.Ident: + t.addIdent(x, ts.Name.String(), fieldName.String()) + case *ast.MapType: + t.addMapType(x, ts.Name.String(), fieldName.String()) + case *ast.SelectorExpr: + t.addSelectorExpr(x, ts.Name.String(), fieldName.String()) + default: + logf("processAST: type %q, field %q, unknown %T: %+v", ts.Name, fieldName, x, x) + } + } + } + } + return nil +} + +func sourceFilter(fi os.FileInfo) bool { + return !strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), fileSuffix) +} + +func (t *templateData) dump() error { + if len(t.Getters) == 0 { + logf("No getters for %v; skipping.", t.filename) + return nil + } + + // Sort getters by ReceiverType.FieldName. + sort.Sort(byName(t.Getters)) + + var buf bytes.Buffer + if err := sourceTmpl.Execute(&buf, t); err != nil { + return err + } + clean, err := format.Source(buf.Bytes()) + if err != nil { + return err + } + + logf("Writing %v...", t.filename) + return ioutil.WriteFile(t.filename, clean, 0644) +} + +func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *getter { + return &getter{ + sortVal: strings.ToLower(receiverType) + "." + strings.ToLower(fieldName), + ReceiverVar: strings.ToLower(receiverType[:1]), + ReceiverType: receiverType, + FieldName: fieldName, + FieldType: fieldType, + ZeroValue: zeroValue, + NamedStruct: namedStruct, + } +} + +func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) { + var eltType string + switch elt := x.Elt.(type) { + case *ast.Ident: + eltType = elt.String() + default: + logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt) + return + } + + t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false)) +} + +func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) { + var zeroValue string + var namedStruct = false + switch x.String() { + case "int", "int64": + zeroValue = "0" + case "string": + zeroValue = `""` + case "bool": + zeroValue = "false" + case "Timestamp": + zeroValue = "Timestamp{}" + default: + zeroValue = "nil" + namedStruct = true + } + + t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct)) +} + +func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) { + var keyType string + switch key := x.Key.(type) { + case *ast.Ident: + keyType = key.String() + default: + logf("addMapType: type %q, field %q: unknown key type: %T %+v; skipping.", receiverType, fieldName, key, key) + return + } + + var valueType string + switch value := x.Value.(type) { + case *ast.Ident: + valueType = value.String() + default: + logf("addMapType: type %q, field %q: unknown value type: %T %+v; skipping.", receiverType, fieldName, value, value) + return + } + + fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType) + zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType) + t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) +} + +func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) { + if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field. + return + } + + var xX string + if xx, ok := x.X.(*ast.Ident); ok { + xX = xx.String() + } + + switch xX { + case "time", "json": + if xX == "json" { + t.Imports["encoding/json"] = "encoding/json" + } else { + t.Imports[xX] = xX + } + fieldType := fmt.Sprintf("%v.%v", xX, x.Sel.Name) + zeroValue := fmt.Sprintf("%v.%v{}", xX, x.Sel.Name) + if xX == "time" && x.Sel.Name == "Duration" { + zeroValue = "0" + } + t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) + default: + logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x) + } +} + +type templateData struct { + filename string + Year int + Package string + Imports map[string]string + Getters []*getter +} + +type getter struct { + sortVal string // Lower-case version of "ReceiverType.FieldName". + ReceiverVar string // The one-letter variable name to match the ReceiverType. + ReceiverType string + FieldName string + FieldType string + ZeroValue string + NamedStruct bool // Getter for named struct. +} + +type byName []*getter + +func (b byName) Len() int { return len(b) } +func (b byName) Less(i, j int) bool { return b[i].sortVal < b[j].sortVal } +func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } + +const source = `// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by gen-accessors; DO NOT EDIT. + +package {{.Package}} +{{with .Imports}} +import ( + {{- range . -}} + "{{.}}" + {{end -}} +) +{{end}} +{{range .Getters}} +{{if .NamedStruct}} +// Get{{.FieldName}} returns the {{.FieldName}} field. +func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} { + if {{.ReceiverVar}} == nil { + return {{.ZeroValue}} + } + return {{.ReceiverVar}}.{{.FieldName}} +} +{{else}} +// Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise. +func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} { + if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil { + return {{.ZeroValue}} + } + return *{{.ReceiverVar}}.{{.FieldName}} +} +{{end}} +{{end}} +` diff --git a/vendor/github.com/google/go-github/github/gists.go b/vendor/github.com/google/go-github/github/gists.go new file mode 100644 index 00000000..9108b642 --- /dev/null +++ b/vendor/github.com/google/go-github/github/gists.go @@ -0,0 +1,388 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// GistsService handles communication with the Gist related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/gists/ +type GistsService service + +// Gist represents a GitHub's gist. +type Gist struct { + ID *string `json:"id,omitempty"` + Description *string `json:"description,omitempty"` + Public *bool `json:"public,omitempty"` + Owner *User `json:"owner,omitempty"` + Files map[GistFilename]GistFile `json:"files,omitempty"` + Comments *int `json:"comments,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + GitPullURL *string `json:"git_pull_url,omitempty"` + GitPushURL *string `json:"git_push_url,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +func (g Gist) String() string { + return Stringify(g) +} + +// GistFilename represents filename on a gist. +type GistFilename string + +// GistFile represents a file on a gist. +type GistFile struct { + Size *int `json:"size,omitempty"` + Filename *string `json:"filename,omitempty"` + Language *string `json:"language,omitempty"` + Type *string `json:"type,omitempty"` + RawURL *string `json:"raw_url,omitempty"` + Content *string `json:"content,omitempty"` +} + +func (g GistFile) String() string { + return Stringify(g) +} + +// GistCommit represents a commit on a gist. +type GistCommit struct { + URL *string `json:"url,omitempty"` + Version *string `json:"version,omitempty"` + User *User `json:"user,omitempty"` + ChangeStatus *CommitStats `json:"change_status,omitempty"` + CommittedAt *Timestamp `json:"committed_at,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +func (gc GistCommit) String() string { + return Stringify(gc) +} + +// GistFork represents a fork of a gist. +type GistFork struct { + URL *string `json:"url,omitempty"` + User *User `json:"user,omitempty"` + ID *string `json:"id,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +func (gf GistFork) String() string { + return Stringify(gf) +} + +// GistListOptions specifies the optional parameters to the +// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods. +type GistListOptions struct { + // Since filters Gists by time. + Since time.Time `url:"since,omitempty"` + + ListOptions +} + +// List gists for a user. Passing the empty string will list +// all public gists if called anonymously. However, if the call +// is authenticated, it will returns all gists for the authenticated +// user. +// +// GitHub API docs: https://developer.github.com/v3/gists/#list-gists +func (s *GistsService) List(ctx context.Context, user string, opt *GistListOptions) ([]*Gist, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/gists", user) + } else { + u = "gists" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var gists []*Gist + resp, err := s.client.Do(ctx, req, &gists) + if err != nil { + return nil, resp, err + } + + return gists, resp, nil +} + +// ListAll lists all public gists. +// +// GitHub API docs: https://developer.github.com/v3/gists/#list-gists +func (s *GistsService) ListAll(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { + u, err := addOptions("gists/public", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var gists []*Gist + resp, err := s.client.Do(ctx, req, &gists) + if err != nil { + return nil, resp, err + } + + return gists, resp, nil +} + +// ListStarred lists starred gists of authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/gists/#list-gists +func (s *GistsService) ListStarred(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { + u, err := addOptions("gists/starred", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var gists []*Gist + resp, err := s.client.Do(ctx, req, &gists) + if err != nil { + return nil, resp, err + } + + return gists, resp, nil +} + +// Get a single gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/#get-a-single-gist +func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, error) { + u := fmt.Sprintf("gists/%v", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + gist := new(Gist) + resp, err := s.client.Do(ctx, req, gist) + if err != nil { + return nil, resp, err + } + + return gist, resp, nil +} + +// GetRevision gets a specific revision of a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist +func (s *GistsService) GetRevision(ctx context.Context, id, sha string) (*Gist, *Response, error) { + u := fmt.Sprintf("gists/%v/%v", id, sha) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + gist := new(Gist) + resp, err := s.client.Do(ctx, req, gist) + if err != nil { + return nil, resp, err + } + + return gist, resp, nil +} + +// Create a gist for authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/gists/#create-a-gist +func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response, error) { + u := "gists" + req, err := s.client.NewRequest("POST", u, gist) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + g := new(Gist) + resp, err := s.client.Do(ctx, req, g) + if err != nil { + return nil, resp, err + } + + return g, resp, nil +} + +// Edit a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/#edit-a-gist +func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist, *Response, error) { + u := fmt.Sprintf("gists/%v", id) + req, err := s.client.NewRequest("PATCH", u, gist) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + g := new(Gist) + resp, err := s.client.Do(ctx, req, g) + if err != nil { + return nil, resp, err + } + + return g, resp, nil +} + +// ListCommits lists commits of a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-commits +func (s *GistsService) ListCommits(ctx context.Context, id string, opt *ListOptions) ([]*GistCommit, *Response, error) { + u := fmt.Sprintf("gists/%v/commits", id) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var gistCommits []*GistCommit + resp, err := s.client.Do(ctx, req, &gistCommits) + if err != nil { + return nil, resp, err + } + + return gistCommits, resp, nil +} + +// Delete a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/#delete-a-gist +func (s *GistsService) Delete(ctx context.Context, id string) (*Response, error) { + u := fmt.Sprintf("gists/%v", id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// Star a gist on behalf of authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/gists/#star-a-gist +func (s *GistsService) Star(ctx context.Context, id string) (*Response, error) { + u := fmt.Sprintf("gists/%v/star", id) + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// Unstar a gist on a behalf of authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/gists/#unstar-a-gist +func (s *GistsService) Unstar(ctx context.Context, id string) (*Response, error) { + u := fmt.Sprintf("gists/%v/star", id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// IsStarred checks if a gist is starred by authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/gists/#check-if-a-gist-is-starred +func (s *GistsService) IsStarred(ctx context.Context, id string) (bool, *Response, error) { + u := fmt.Sprintf("gists/%v/star", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + resp, err := s.client.Do(ctx, req, nil) + starred, err := parseBoolResponse(err) + return starred, resp, err +} + +// Fork a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/#fork-a-gist +func (s *GistsService) Fork(ctx context.Context, id string) (*Gist, *Response, error) { + u := fmt.Sprintf("gists/%v/forks", id) + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + g := new(Gist) + resp, err := s.client.Do(ctx, req, g) + if err != nil { + return nil, resp, err + } + + return g, resp, nil +} + +// ListForks lists forks of a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-forks +func (s *GistsService) ListForks(ctx context.Context, id string) ([]*GistFork, *Response, error) { + u := fmt.Sprintf("gists/%v/forks", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var gistForks []*GistFork + resp, err := s.client.Do(ctx, req, &gistForks) + if err != nil { + return nil, resp, err + } + + return gistForks, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/gists_comments.go b/vendor/github.com/google/go-github/github/gists_comments.go new file mode 100644 index 00000000..d5322e3d --- /dev/null +++ b/vendor/github.com/google/go-github/github/gists_comments.go @@ -0,0 +1,119 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// GistComment represents a Gist comment. +type GistComment struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + Body *string `json:"body,omitempty"` + User *User `json:"user,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` +} + +func (g GistComment) String() string { + return Stringify(g) +} + +// ListComments lists all comments for a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist +func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *ListOptions) ([]*GistComment, *Response, error) { + u := fmt.Sprintf("gists/%v/comments", gistID) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var comments []*GistComment + resp, err := s.client.Do(ctx, req, &comments) + if err != nil { + return nil, resp, err + } + + return comments, resp, nil +} + +// GetComment retrieves a single comment from a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-single-comment +func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int64) (*GistComment, *Response, error) { + u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + c := new(GistComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// CreateComment creates a comment for a gist. +// +// GitHub API docs: https://developer.github.com/v3/gists/comments/#create-a-comment +func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment *GistComment) (*GistComment, *Response, error) { + u := fmt.Sprintf("gists/%v/comments", gistID) + req, err := s.client.NewRequest("POST", u, comment) + if err != nil { + return nil, nil, err + } + + c := new(GistComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// EditComment edits an existing gist comment. +// +// GitHub API docs: https://developer.github.com/v3/gists/comments/#edit-a-comment +func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int64, comment *GistComment) (*GistComment, *Response, error) { + u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) + req, err := s.client.NewRequest("PATCH", u, comment) + if err != nil { + return nil, nil, err + } + + c := new(GistComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// DeleteComment deletes a gist comment. +// +// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-comment +func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int64) (*Response, error) { + u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/gists_comments_test.go b/vendor/github.com/google/go-github/github/gists_comments_test.go new file mode 100644 index 00000000..d2f5a953 --- /dev/null +++ b/vendor/github.com/google/go-github/github/gists_comments_test.go @@ -0,0 +1,169 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestGistsService_ListComments(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id": 1}]`) + }) + + opt := &ListOptions{Page: 2} + comments, _, err := client.Gists.ListComments(context.Background(), "1", opt) + if err != nil { + t.Errorf("Gists.Comments returned error: %v", err) + } + + want := []*GistComment{{ID: Int64(1)}} + if !reflect.DeepEqual(comments, want) { + t.Errorf("Gists.ListComments returned %+v, want %+v", comments, want) + } +} + +func TestGistsService_ListComments_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.ListComments(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestGistsService_GetComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id": 1}`) + }) + + comment, _, err := client.Gists.GetComment(context.Background(), "1", 2) + if err != nil { + t.Errorf("Gists.GetComment returned error: %v", err) + } + + want := &GistComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Gists.GetComment returned %+v, want %+v", comment, want) + } +} + +func TestGistsService_GetComment_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.GetComment(context.Background(), "%", 1) + testURLParseError(t, err) +} + +func TestGistsService_CreateComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &GistComment{ID: Int64(1), Body: String("b")} + + mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { + v := new(GistComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Gists.CreateComment(context.Background(), "1", input) + if err != nil { + t.Errorf("Gists.CreateComment returned error: %v", err) + } + + want := &GistComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Gists.CreateComment returned %+v, want %+v", comment, want) + } +} + +func TestGistsService_CreateComment_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.CreateComment(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestGistsService_EditComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &GistComment{ID: Int64(1), Body: String("b")} + + mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { + v := new(GistComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Gists.EditComment(context.Background(), "1", 2, input) + if err != nil { + t.Errorf("Gists.EditComment returned error: %v", err) + } + + want := &GistComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Gists.EditComment returned %+v, want %+v", comment, want) + } +} + +func TestGistsService_EditComment_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.EditComment(context.Background(), "%", 1, nil) + testURLParseError(t, err) +} + +func TestGistsService_DeleteComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Gists.DeleteComment(context.Background(), "1", 2) + if err != nil { + t.Errorf("Gists.Delete returned error: %v", err) + } +} + +func TestGistsService_DeleteComment_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Gists.DeleteComment(context.Background(), "%", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/gists_test.go b/vendor/github.com/google/go-github/github/gists_test.go new file mode 100644 index 00000000..e6519151 --- /dev/null +++ b/vendor/github.com/google/go-github/github/gists_test.go @@ -0,0 +1,546 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestGistsService_List_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + since := "2013-01-01T00:00:00Z" + + mux.HandleFunc("/users/u/gists", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{ + "since": since, + }) + fmt.Fprint(w, `[{"id": "1"}]`) + }) + + opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} + gists, _, err := client.Gists.List(context.Background(), "u", opt) + if err != nil { + t.Errorf("Gists.List returned error: %v", err) + } + + want := []*Gist{{ID: String("1")}} + if !reflect.DeepEqual(gists, want) { + t.Errorf("Gists.List returned %+v, want %+v", gists, want) + } +} + +func TestGistsService_List_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `[{"id": "1"}]`) + }) + + gists, _, err := client.Gists.List(context.Background(), "", nil) + if err != nil { + t.Errorf("Gists.List returned error: %v", err) + } + + want := []*Gist{{ID: String("1")}} + if !reflect.DeepEqual(gists, want) { + t.Errorf("Gists.List returned %+v, want %+v", gists, want) + } +} + +func TestGistsService_List_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.List(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestGistsService_ListAll(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + since := "2013-01-01T00:00:00Z" + + mux.HandleFunc("/gists/public", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{ + "since": since, + }) + fmt.Fprint(w, `[{"id": "1"}]`) + }) + + opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} + gists, _, err := client.Gists.ListAll(context.Background(), opt) + if err != nil { + t.Errorf("Gists.ListAll returned error: %v", err) + } + + want := []*Gist{{ID: String("1")}} + if !reflect.DeepEqual(gists, want) { + t.Errorf("Gists.ListAll returned %+v, want %+v", gists, want) + } +} + +func TestGistsService_ListStarred(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + since := "2013-01-01T00:00:00Z" + + mux.HandleFunc("/gists/starred", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{ + "since": since, + }) + fmt.Fprint(w, `[{"id": "1"}]`) + }) + + opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} + gists, _, err := client.Gists.ListStarred(context.Background(), opt) + if err != nil { + t.Errorf("Gists.ListStarred returned error: %v", err) + } + + want := []*Gist{{ID: String("1")}} + if !reflect.DeepEqual(gists, want) { + t.Errorf("Gists.ListStarred returned %+v, want %+v", gists, want) + } +} + +func TestGistsService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"id": "1"}`) + }) + + gist, _, err := client.Gists.Get(context.Background(), "1") + if err != nil { + t.Errorf("Gists.Get returned error: %v", err) + } + + want := &Gist{ID: String("1")} + if !reflect.DeepEqual(gist, want) { + t.Errorf("Gists.Get returned %+v, want %+v", gist, want) + } +} + +func TestGistsService_Get_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.Get(context.Background(), "%") + testURLParseError(t, err) +} + +func TestGistsService_GetRevision(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"id": "1"}`) + }) + + gist, _, err := client.Gists.GetRevision(context.Background(), "1", "s") + if err != nil { + t.Errorf("Gists.Get returned error: %v", err) + } + + want := &Gist{ID: String("1")} + if !reflect.DeepEqual(gist, want) { + t.Errorf("Gists.Get returned %+v, want %+v", gist, want) + } +} + +func TestGistsService_GetRevision_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.GetRevision(context.Background(), "%", "%") + testURLParseError(t, err) +} + +func TestGistsService_Create(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Gist{ + Description: String("Gist description"), + Public: Bool(false), + Files: map[GistFilename]GistFile{ + "test.txt": {Content: String("Gist file content")}, + }, + } + + mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { + v := new(Gist) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, + ` + { + "id": "1", + "description": "Gist description", + "public": false, + "files": { + "test.txt": { + "filename": "test.txt" + } + } + }`) + }) + + gist, _, err := client.Gists.Create(context.Background(), input) + if err != nil { + t.Errorf("Gists.Create returned error: %v", err) + } + + want := &Gist{ + ID: String("1"), + Description: String("Gist description"), + Public: Bool(false), + Files: map[GistFilename]GistFile{ + "test.txt": {Filename: String("test.txt")}, + }, + } + if !reflect.DeepEqual(gist, want) { + t.Errorf("Gists.Create returned %+v, want %+v", gist, want) + } +} + +func TestGistsService_Edit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Gist{ + Description: String("New description"), + Files: map[GistFilename]GistFile{ + "new.txt": {Content: String("new file content")}, + }, + } + + mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { + v := new(Gist) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, + ` + { + "id": "1", + "description": "new description", + "public": false, + "files": { + "test.txt": { + "filename": "test.txt" + }, + "new.txt": { + "filename": "new.txt" + } + } + }`) + }) + + gist, _, err := client.Gists.Edit(context.Background(), "1", input) + if err != nil { + t.Errorf("Gists.Edit returned error: %v", err) + } + + want := &Gist{ + ID: String("1"), + Description: String("new description"), + Public: Bool(false), + Files: map[GistFilename]GistFile{ + "test.txt": {Filename: String("test.txt")}, + "new.txt": {Filename: String("new.txt")}, + }, + } + if !reflect.DeepEqual(gist, want) { + t.Errorf("Gists.Edit returned %+v, want %+v", gist, want) + } +} + +func TestGistsService_Edit_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.Edit(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestGistsService_ListCommits(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/commits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, nil) + fmt.Fprint(w, ` + [ + { + "url": "https://api.github.com/gists/1/1", + "version": "1", + "user": { + "id": 1 + }, + "change_status": { + "deletions": 0, + "additions": 180, + "total": 180 + }, + "committed_at": "2010-01-01T00:00:00Z" + } + ] + `) + }) + + gistCommits, _, err := client.Gists.ListCommits(context.Background(), "1", nil) + if err != nil { + t.Errorf("Gists.ListCommits returned error: %v", err) + } + + want := []*GistCommit{{ + URL: String("https://api.github.com/gists/1/1"), + Version: String("1"), + User: &User{ID: Int64(1)}, + CommittedAt: &Timestamp{time.Date(2010, 1, 1, 00, 00, 00, 0, time.UTC)}, + ChangeStatus: &CommitStats{ + Additions: Int(180), + Deletions: Int(0), + Total: Int(180), + }}} + + if !reflect.DeepEqual(gistCommits, want) { + t.Errorf("Gists.ListCommits returned %+v, want %+v", gistCommits, want) + } +} + +func TestGistsService_ListCommits_withOptions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/commits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[]`) + }) + + _, _, err := client.Gists.ListCommits(context.Background(), "1", &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Gists.ListCommits returned error: %v", err) + } +} + +func TestGistsService_Delete(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Gists.Delete(context.Background(), "1") + if err != nil { + t.Errorf("Gists.Delete returned error: %v", err) + } +} + +func TestGistsService_Delete_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Gists.Delete(context.Background(), "%") + testURLParseError(t, err) +} + +func TestGistsService_Star(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + _, err := client.Gists.Star(context.Background(), "1") + if err != nil { + t.Errorf("Gists.Star returned error: %v", err) + } +} + +func TestGistsService_Star_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Gists.Star(context.Background(), "%") + testURLParseError(t, err) +} + +func TestGistsService_Unstar(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Gists.Unstar(context.Background(), "1") + if err != nil { + t.Errorf("Gists.Unstar returned error: %v", err) + } +} + +func TestGistsService_Unstar_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Gists.Unstar(context.Background(), "%") + testURLParseError(t, err) +} + +func TestGistsService_IsStarred_hasStar(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + star, _, err := client.Gists.IsStarred(context.Background(), "1") + if err != nil { + t.Errorf("Gists.Starred returned error: %v", err) + } + if want := true; star != want { + t.Errorf("Gists.Starred returned %+v, want %+v", star, want) + } +} + +func TestGistsService_IsStarred_noStar(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + star, _, err := client.Gists.IsStarred(context.Background(), "1") + if err != nil { + t.Errorf("Gists.Starred returned error: %v", err) + } + if want := false; star != want { + t.Errorf("Gists.Starred returned %+v, want %+v", star, want) + } +} + +func TestGistsService_IsStarred_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.IsStarred(context.Background(), "%") + testURLParseError(t, err) +} + +func TestGistsService_Fork(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"id": "2"}`) + }) + + gist, _, err := client.Gists.Fork(context.Background(), "1") + if err != nil { + t.Errorf("Gists.Fork returned error: %v", err) + } + + want := &Gist{ID: String("2")} + if !reflect.DeepEqual(gist, want) { + t.Errorf("Gists.Fork returned %+v, want %+v", gist, want) + } +} + +func TestGistsService_ListForks(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, nil) + fmt.Fprint(w, ` + [ + {"url": "https://api.github.com/gists/1", + "user": {"id": 1}, + "id": "1", + "created_at": "2010-01-01T00:00:00Z", + "updated_at": "2013-01-01T00:00:00Z" + } + ] + `) + }) + + gistForks, _, err := client.Gists.ListForks(context.Background(), "1") + if err != nil { + t.Errorf("Gists.ListForks returned error: %v", err) + } + + want := []*GistFork{{ + URL: String("https://api.github.com/gists/1"), + ID: String("1"), + User: &User{ID: Int64(1)}, + CreatedAt: &Timestamp{time.Date(2010, 1, 1, 00, 00, 00, 0, time.UTC)}, + UpdatedAt: &Timestamp{time.Date(2013, 1, 1, 00, 00, 00, 0, time.UTC)}}} + + if !reflect.DeepEqual(gistForks, want) { + t.Errorf("Gists.ListForks returned %+v, want %+v", gistForks, want) + } +} + +func TestGistsService_Fork_invalidID(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gists.Fork(context.Background(), "%") + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/git.go b/vendor/github.com/google/go-github/github/git.go new file mode 100644 index 00000000..1ce47437 --- /dev/null +++ b/vendor/github.com/google/go-github/github/git.go @@ -0,0 +1,12 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +// GitService handles communication with the git data related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/git/ +type GitService service diff --git a/vendor/github.com/google/go-github/github/git_blobs.go b/vendor/github.com/google/go-github/github/git_blobs.go new file mode 100644 index 00000000..9d8fd27b --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_blobs.go @@ -0,0 +1,57 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Blob represents a blob object. +type Blob struct { + Content *string `json:"content,omitempty"` + Encoding *string `json:"encoding,omitempty"` + SHA *string `json:"sha,omitempty"` + Size *int `json:"size,omitempty"` + URL *string `json:"url,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +// GetBlob fetchs a blob from a repo given a SHA. +// +// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob +func (s *GitService) GetBlob(ctx context.Context, owner string, repo string, sha string) (*Blob, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + blob := new(Blob) + resp, err := s.client.Do(ctx, req, blob) + return blob, resp, err +} + +// CreateBlob creates a blob object. +// +// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob +func (s *GitService) CreateBlob(ctx context.Context, owner string, repo string, blob *Blob) (*Blob, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) + req, err := s.client.NewRequest("POST", u, blob) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + t := new(Blob) + resp, err := s.client.Do(ctx, req, t) + return t, resp, err +} diff --git a/vendor/github.com/google/go-github/github/git_blobs_test.go b/vendor/github.com/google/go-github/github/git_blobs_test.go new file mode 100644 index 00000000..d4ae22be --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_blobs_test.go @@ -0,0 +1,103 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestGitService_GetBlob(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/blobs/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + + fmt.Fprint(w, `{ + "sha": "s", + "content": "blob content" + }`) + }) + + blob, _, err := client.Git.GetBlob(context.Background(), "o", "r", "s") + if err != nil { + t.Errorf("Git.GetBlob returned error: %v", err) + } + + want := Blob{ + SHA: String("s"), + Content: String("blob content"), + } + + if !reflect.DeepEqual(*blob, want) { + t.Errorf("Blob.Get returned %+v, want %+v", *blob, want) + } +} + +func TestGitService_GetBlob_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Git.GetBlob(context.Background(), "%", "%", "%") + testURLParseError(t, err) +} + +func TestGitService_CreateBlob(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Blob{ + SHA: String("s"), + Content: String("blob content"), + Encoding: String("utf-8"), + Size: Int(12), + } + + mux.HandleFunc("/repos/o/r/git/blobs", func(w http.ResponseWriter, r *http.Request) { + v := new(Blob) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + + want := input + if !reflect.DeepEqual(v, want) { + t.Errorf("Git.CreateBlob request body: %+v, want %+v", v, want) + } + + fmt.Fprint(w, `{ + "sha": "s", + "content": "blob content", + "encoding": "utf-8", + "size": 12 + }`) + }) + + blob, _, err := client.Git.CreateBlob(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Git.CreateBlob returned error: %v", err) + } + + want := input + + if !reflect.DeepEqual(*blob, *want) { + t.Errorf("Git.CreateBlob returned %+v, want %+v", *blob, *want) + } +} + +func TestGitService_CreateBlob_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Git.CreateBlob(context.Background(), "%", "%", &Blob{}) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/git_commits.go b/vendor/github.com/google/go-github/github/git_commits.go new file mode 100644 index 00000000..29882569 --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_commits.go @@ -0,0 +1,139 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "strings" + "time" +) + +// SignatureVerification represents GPG signature verification. +type SignatureVerification struct { + Verified *bool `json:"verified,omitempty"` + Reason *string `json:"reason,omitempty"` + Signature *string `json:"signature,omitempty"` + Payload *string `json:"payload,omitempty"` +} + +// Commit represents a GitHub commit. +type Commit struct { + SHA *string `json:"sha,omitempty"` + Author *CommitAuthor `json:"author,omitempty"` + Committer *CommitAuthor `json:"committer,omitempty"` + Message *string `json:"message,omitempty"` + Tree *Tree `json:"tree,omitempty"` + Parents []Commit `json:"parents,omitempty"` + Stats *CommitStats `json:"stats,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + URL *string `json:"url,omitempty"` + Verification *SignatureVerification `json:"verification,omitempty"` + NodeID *string `json:"node_id,omitempty"` + + // CommentCount is the number of GitHub comments on the commit. This + // is only populated for requests that fetch GitHub data like + // Pulls.ListCommits, Repositories.ListCommits, etc. + CommentCount *int `json:"comment_count,omitempty"` +} + +func (c Commit) String() string { + return Stringify(c) +} + +// CommitAuthor represents the author or committer of a commit. The commit +// author may not correspond to a GitHub User. +type CommitAuthor struct { + Date *time.Time `json:"date,omitempty"` + Name *string `json:"name,omitempty"` + Email *string `json:"email,omitempty"` + + // The following fields are only populated by Webhook events. + Login *string `json:"username,omitempty"` // Renamed for go-github consistency. +} + +func (c CommitAuthor) String() string { + return Stringify(c) +} + +// GetCommit fetchs the Commit object for a given SHA. +// +// GitHub API docs: https://developer.github.com/v3/git/commits/#get-a-commit +func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeGitSigningPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + c := new(Commit) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// createCommit represents the body of a CreateCommit request. +type createCommit struct { + Author *CommitAuthor `json:"author,omitempty"` + Committer *CommitAuthor `json:"committer,omitempty"` + Message *string `json:"message,omitempty"` + Tree *string `json:"tree,omitempty"` + Parents []string `json:"parents,omitempty"` +} + +// CreateCommit creates a new commit in a repository. +// commit must not be nil. +// +// The commit.Committer is optional and will be filled with the commit.Author +// data if omitted. If the commit.Author is omitted, it will be filled in with +// the authenticated user’s information and the current date. +// +// GitHub API docs: https://developer.github.com/v3/git/commits/#create-a-commit +func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string, commit *Commit) (*Commit, *Response, error) { + if commit == nil { + return nil, nil, fmt.Errorf("commit must be provided") + } + + u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo) + + parents := make([]string, len(commit.Parents)) + for i, parent := range commit.Parents { + parents[i] = *parent.SHA + } + + body := &createCommit{ + Author: commit.Author, + Committer: commit.Committer, + Message: commit.Message, + Parents: parents, + } + if commit.Tree != nil { + body.Tree = commit.Tree.SHA + } + + req, err := s.client.NewRequest("POST", u, body) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + c := new(Commit) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/git_commits_test.go b/vendor/github.com/google/go-github/github/git_commits_test.go new file mode 100644 index 00000000..695e0551 --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_commits_test.go @@ -0,0 +1,93 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestGitService_GetCommit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeGitSigningPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/repos/o/r/git/commits/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `{"sha":"s","message":"m","author":{"name":"n"}}`) + }) + + commit, _, err := client.Git.GetCommit(context.Background(), "o", "r", "s") + if err != nil { + t.Errorf("Git.GetCommit returned error: %v", err) + } + + want := &Commit{SHA: String("s"), Message: String("m"), Author: &CommitAuthor{Name: String("n")}} + if !reflect.DeepEqual(commit, want) { + t.Errorf("Git.GetCommit returned %+v, want %+v", commit, want) + } +} + +func TestGitService_GetCommit_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Git.GetCommit(context.Background(), "%", "%", "%") + testURLParseError(t, err) +} + +func TestGitService_CreateCommit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Commit{ + Message: String("m"), + Tree: &Tree{SHA: String("t")}, + Parents: []Commit{{SHA: String("p")}}, + } + + mux.HandleFunc("/repos/o/r/git/commits", func(w http.ResponseWriter, r *http.Request) { + v := new(createCommit) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + + want := &createCommit{ + Message: input.Message, + Tree: String("t"), + Parents: []string{"p"}, + } + if !reflect.DeepEqual(v, want) { + t.Errorf("Request body = %+v, want %+v", v, want) + } + fmt.Fprint(w, `{"sha":"s"}`) + }) + + commit, _, err := client.Git.CreateCommit(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Git.CreateCommit returned error: %v", err) + } + + want := &Commit{SHA: String("s")} + if !reflect.DeepEqual(commit, want) { + t.Errorf("Git.CreateCommit returned %+v, want %+v", commit, want) + } +} + +func TestGitService_CreateCommit_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Git.CreateCommit(context.Background(), "%", "%", &Commit{}) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/git_refs.go b/vendor/github.com/google/go-github/github/git_refs.go new file mode 100644 index 00000000..0947d866 --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_refs.go @@ -0,0 +1,233 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" +) + +// Reference represents a GitHub reference. +type Reference struct { + Ref *string `json:"ref"` + URL *string `json:"url"` + Object *GitObject `json:"object"` + NodeID *string `json:"node_id,omitempty"` +} + +func (r Reference) String() string { + return Stringify(r) +} + +// GitObject represents a Git object. +type GitObject struct { + Type *string `json:"type"` + SHA *string `json:"sha"` + URL *string `json:"url"` +} + +func (o GitObject) String() string { + return Stringify(o) +} + +// createRefRequest represents the payload for creating a reference. +type createRefRequest struct { + Ref *string `json:"ref"` + SHA *string `json:"sha"` +} + +// updateRefRequest represents the payload for updating a reference. +type updateRefRequest struct { + SHA *string `json:"sha"` + Force *bool `json:"force"` +} + +// GetRef fetches a single Reference object for a given Git ref. +// If there is no exact match, GetRef will return an error. +// +// Note: The GitHub API can return multiple matches. +// If you wish to use this functionality please use the GetRefs() method. +// +// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference +func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref string) (*Reference, *Response, error) { + ref = strings.TrimPrefix(ref, "refs/") + u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + r := new(Reference) + resp, err := s.client.Do(ctx, req, r) + if _, ok := err.(*json.UnmarshalTypeError); ok { + // Multiple refs, means there wasn't an exact match. + return nil, resp, errors.New("no exact match found for this ref") + } else if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// GetRefs fetches a slice of Reference objects for a given Git ref. +// If there is an exact match, only that ref is returned. +// If there is no exact match, GitHub returns all refs that start with ref. +// If returned error is nil, there will be at least 1 ref returned. +// For example: +// +// "heads/featureA" -> ["refs/heads/featureA"] // Exact match, single ref is returned. +// "heads/feature" -> ["refs/heads/featureA", "refs/heads/featureB"] // All refs that start with ref. +// "heads/notexist" -> [] // Returns an error. +// +// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference +func (s *GitService) GetRefs(ctx context.Context, owner string, repo string, ref string) ([]*Reference, *Response, error) { + ref = strings.TrimPrefix(ref, "refs/") + u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var rawJSON json.RawMessage + resp, err := s.client.Do(ctx, req, &rawJSON) + if err != nil { + return nil, resp, err + } + + // Prioritize the most common case: a single returned ref. + r := new(Reference) + singleUnmarshalError := json.Unmarshal(rawJSON, r) + if singleUnmarshalError == nil { + return []*Reference{r}, resp, nil + } + + // Attempt to unmarshal multiple refs. + var rs []*Reference + multipleUnmarshalError := json.Unmarshal(rawJSON, &rs) + if multipleUnmarshalError == nil { + if len(rs) == 0 { + return nil, resp, fmt.Errorf("unexpected response from GitHub API: an array of refs with length 0") + } + return rs, resp, nil + } + + return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", singleUnmarshalError, multipleUnmarshalError) +} + +// ReferenceListOptions specifies optional parameters to the +// GitService.ListRefs method. +type ReferenceListOptions struct { + Type string `url:"-"` + + ListOptions +} + +// ListRefs lists all refs in a repository. +// +// GitHub API docs: https://developer.github.com/v3/git/refs/#get-all-references +func (s *GitService) ListRefs(ctx context.Context, owner, repo string, opt *ReferenceListOptions) ([]*Reference, *Response, error) { + var u string + if opt != nil && opt.Type != "" { + u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type) + } else { + u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var rs []*Reference + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// CreateRef creates a new ref in a repository. +// +// GitHub API docs: https://developer.github.com/v3/git/refs/#create-a-reference +func (s *GitService) CreateRef(ctx context.Context, owner string, repo string, ref *Reference) (*Reference, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) + req, err := s.client.NewRequest("POST", u, &createRefRequest{ + // back-compat with previous behavior that didn't require 'refs/' prefix + Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")), + SHA: ref.Object.SHA, + }) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + r := new(Reference) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// UpdateRef updates an existing ref in a repository. +// +// GitHub API docs: https://developer.github.com/v3/git/refs/#update-a-reference +func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) { + refPath := strings.TrimPrefix(*ref.Ref, "refs/") + u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath) + req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{ + SHA: ref.Object.SHA, + Force: &force, + }) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + r := new(Reference) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// DeleteRef deletes a ref from a repository. +// +// GitHub API docs: https://developer.github.com/v3/git/refs/#delete-a-reference +func (s *GitService) DeleteRef(ctx context.Context, owner string, repo string, ref string) (*Response, error) { + ref = strings.TrimPrefix(ref, "refs/") + u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/git_refs_test.go b/vendor/github.com/google/go-github/github/git_refs_test.go new file mode 100644 index 00000000..b0f5db56 --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_refs_test.go @@ -0,0 +1,438 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestGitService_GetRef_singleRef(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, ` + { + "ref": "refs/heads/b", + "url": "https://api.github.com/repos/o/r/git/refs/heads/b", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }`) + }) + + ref, _, err := client.Git.GetRef(context.Background(), "o", "r", "refs/heads/b") + if err != nil { + t.Fatalf("Git.GetRef returned error: %v", err) + } + + want := &Reference{ + Ref: String("refs/heads/b"), + URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Object: &GitObject{ + Type: String("commit"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + } + if !reflect.DeepEqual(ref, want) { + t.Errorf("Git.GetRef returned %+v, want %+v", ref, want) + } + + // without 'refs/' prefix + if _, _, err := client.Git.GetRef(context.Background(), "o", "r", "heads/b"); err != nil { + t.Errorf("Git.GetRef returned error: %v", err) + } +} + +func TestGitService_GetRef_multipleRefs(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, ` + [ + { + "ref": "refs/heads/booger", + "url": "https://api.github.com/repos/o/r/git/refs/heads/booger", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }, + { + "ref": "refs/heads/bandsaw", + "url": "https://api.github.com/repos/o/r/git/refs/heads/bandsaw", + "object": { + "type": "commit", + "sha": "612077ae6dffb4d2fbd8ce0cccaa58893b07b5ac", + "url": "https://api.github.com/repos/o/r/git/commits/612077ae6dffb4d2fbd8ce0cccaa58893b07b5ac" + } + } + ] + `) + }) + + _, _, err := client.Git.GetRef(context.Background(), "o", "r", "refs/heads/b") + want := "no exact match found for this ref" + if err.Error() != want { + t.Errorf("Git.GetRef returned %+v, want %+v", err, want) + } + +} + +func TestGitService_GetRefs_singleRef(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, ` + { + "ref": "refs/heads/b", + "url": "https://api.github.com/repos/o/r/git/refs/heads/b", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }`) + }) + + refs, _, err := client.Git.GetRefs(context.Background(), "o", "r", "refs/heads/b") + if err != nil { + t.Fatalf("Git.GetRefs returned error: %v", err) + } + + ref := refs[0] + want := &Reference{ + Ref: String("refs/heads/b"), + URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Object: &GitObject{ + Type: String("commit"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + } + if !reflect.DeepEqual(ref, want) { + t.Errorf("Git.GetRefs returned %+v, want %+v", ref, want) + } + + // without 'refs/' prefix + if _, _, err := client.Git.GetRefs(context.Background(), "o", "r", "heads/b"); err != nil { + t.Errorf("Git.GetRefs returned error: %v", err) + } +} + +func TestGitService_GetRefs_multipleRefs(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, ` + [ + { + "ref": "refs/heads/booger", + "url": "https://api.github.com/repos/o/r/git/refs/heads/booger", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }, + { + "ref": "refs/heads/bandsaw", + "url": "https://api.github.com/repos/o/r/git/refs/heads/bandsaw", + "object": { + "type": "commit", + "sha": "612077ae6dffb4d2fbd8ce0cccaa58893b07b5ac", + "url": "https://api.github.com/repos/o/r/git/commits/612077ae6dffb4d2fbd8ce0cccaa58893b07b5ac" + } + } + ] + `) + }) + + refs, _, err := client.Git.GetRefs(context.Background(), "o", "r", "refs/heads/b") + if err != nil { + t.Errorf("Git.GetRefs returned error: %v", err) + } + + want := &Reference{ + Ref: String("refs/heads/booger"), + URL: String("https://api.github.com/repos/o/r/git/refs/heads/booger"), + Object: &GitObject{ + Type: String("commit"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + } + if !reflect.DeepEqual(refs[0], want) { + t.Errorf("Git.GetRefs returned %+v, want %+v", refs[0], want) + } +} + +// TestGitService_GetRefs_noRefs tests for behaviour resulting from an unexpected GH response. This should never actually happen. +func TestGitService_GetRefs_noRefs(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, "[]") + }) + + _, _, err := client.Git.GetRefs(context.Background(), "o", "r", "refs/heads/b") + want := "unexpected response from GitHub API: an array of refs with length 0" + if err.Error() != want { + t.Errorf("Git.GetRefs returned %+v, want %+v", err, want) + } + +} + +func TestGitService_ListRefs(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, ` + [ + { + "ref": "refs/heads/branchA", + "url": "https://api.github.com/repos/o/r/git/refs/heads/branchA", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }, + { + "ref": "refs/heads/branchB", + "url": "https://api.github.com/repos/o/r/git/refs/heads/branchB", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + } + ]`) + }) + + refs, _, err := client.Git.ListRefs(context.Background(), "o", "r", nil) + if err != nil { + t.Errorf("Git.ListRefs returned error: %v", err) + } + + want := []*Reference{ + { + Ref: String("refs/heads/branchA"), + URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchA"), + Object: &GitObject{ + Type: String("commit"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + }, + { + Ref: String("refs/heads/branchB"), + URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchB"), + Object: &GitObject{ + Type: String("commit"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + }, + } + if !reflect.DeepEqual(refs, want) { + t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) + } +} + +func TestGitService_ListRefs_options(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"ref": "r"}]`) + }) + + opt := &ReferenceListOptions{Type: "t", ListOptions: ListOptions{Page: 2}} + refs, _, err := client.Git.ListRefs(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Git.ListRefs returned error: %v", err) + } + + want := []*Reference{{Ref: String("r")}} + if !reflect.DeepEqual(refs, want) { + t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) + } +} + +func TestGitService_CreateRef(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + args := &createRefRequest{ + Ref: String("refs/heads/b"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + } + + mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { + v := new(createRefRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, args) { + t.Errorf("Request body = %+v, want %+v", v, args) + } + fmt.Fprint(w, ` + { + "ref": "refs/heads/b", + "url": "https://api.github.com/repos/o/r/git/refs/heads/b", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }`) + }) + + ref, _, err := client.Git.CreateRef(context.Background(), "o", "r", &Reference{ + Ref: String("refs/heads/b"), + Object: &GitObject{ + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + }) + if err != nil { + t.Errorf("Git.CreateRef returned error: %v", err) + } + + want := &Reference{ + Ref: String("refs/heads/b"), + URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Object: &GitObject{ + Type: String("commit"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + } + if !reflect.DeepEqual(ref, want) { + t.Errorf("Git.CreateRef returned %+v, want %+v", ref, want) + } + + // without 'refs/' prefix + _, _, err = client.Git.CreateRef(context.Background(), "o", "r", &Reference{ + Ref: String("heads/b"), + Object: &GitObject{ + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + }) + if err != nil { + t.Errorf("Git.CreateRef returned error: %v", err) + } +} + +func TestGitService_UpdateRef(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + args := &updateRefRequest{ + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + Force: Bool(true), + } + + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + v := new(updateRefRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, args) { + t.Errorf("Request body = %+v, want %+v", v, args) + } + fmt.Fprint(w, ` + { + "ref": "refs/heads/b", + "url": "https://api.github.com/repos/o/r/git/refs/heads/b", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }`) + }) + + ref, _, err := client.Git.UpdateRef(context.Background(), "o", "r", &Reference{ + Ref: String("refs/heads/b"), + Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, + }, true) + if err != nil { + t.Errorf("Git.UpdateRef returned error: %v", err) + } + + want := &Reference{ + Ref: String("refs/heads/b"), + URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Object: &GitObject{ + Type: String("commit"), + SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + } + if !reflect.DeepEqual(ref, want) { + t.Errorf("Git.UpdateRef returned %+v, want %+v", ref, want) + } + + // without 'refs/' prefix + _, _, err = client.Git.UpdateRef(context.Background(), "o", "r", &Reference{ + Ref: String("heads/b"), + Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, + }, true) + if err != nil { + t.Errorf("Git.UpdateRef returned error: %v", err) + } +} + +func TestGitService_DeleteRef(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Git.DeleteRef(context.Background(), "o", "r", "refs/heads/b") + if err != nil { + t.Errorf("Git.DeleteRef returned error: %v", err) + } + + // without 'refs/' prefix + if _, err := client.Git.DeleteRef(context.Background(), "o", "r", "heads/b"); err != nil { + t.Errorf("Git.DeleteRef returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/git_tags.go b/vendor/github.com/google/go-github/github/git_tags.go new file mode 100644 index 00000000..f3822ffa --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_tags.go @@ -0,0 +1,84 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "strings" +) + +// Tag represents a tag object. +type Tag struct { + Tag *string `json:"tag,omitempty"` + SHA *string `json:"sha,omitempty"` + URL *string `json:"url,omitempty"` + Message *string `json:"message,omitempty"` + Tagger *CommitAuthor `json:"tagger,omitempty"` + Object *GitObject `json:"object,omitempty"` + Verification *SignatureVerification `json:"verification,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +// createTagRequest represents the body of a CreateTag request. This is mostly +// identical to Tag with the exception that the object SHA and Type are +// top-level fields, rather than being nested inside a JSON object. +type createTagRequest struct { + Tag *string `json:"tag,omitempty"` + Message *string `json:"message,omitempty"` + Object *string `json:"object,omitempty"` + Type *string `json:"type,omitempty"` + Tagger *CommitAuthor `json:"tagger,omitempty"` +} + +// GetTag fetchs a tag from a repo given a SHA. +// +// GitHub API docs: https://developer.github.com/v3/git/tags/#get-a-tag +func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha string) (*Tag, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/tags/%v", owner, repo, sha) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeGitSigningPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + tag := new(Tag) + resp, err := s.client.Do(ctx, req, tag) + return tag, resp, err +} + +// CreateTag creates a tag object. +// +// GitHub API docs: https://developer.github.com/v3/git/tags/#create-a-tag-object +func (s *GitService) CreateTag(ctx context.Context, owner string, repo string, tag *Tag) (*Tag, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/tags", owner, repo) + + // convert Tag into a createTagRequest + tagRequest := &createTagRequest{ + Tag: tag.Tag, + Message: tag.Message, + Tagger: tag.Tagger, + } + if tag.Object != nil { + tagRequest.Object = tag.Object.SHA + tagRequest.Type = tag.Object.Type + } + + req, err := s.client.NewRequest("POST", u, tagRequest) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + t := new(Tag) + resp, err := s.client.Do(ctx, req, t) + return t, resp, err +} diff --git a/vendor/github.com/google/go-github/github/git_tags_test.go b/vendor/github.com/google/go-github/github/git_tags_test.go new file mode 100644 index 00000000..728ea0b7 --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_tags_test.go @@ -0,0 +1,72 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestGitService_GetTag(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeGitSigningPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/repos/o/r/git/tags/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + + fmt.Fprint(w, `{"tag": "t"}`) + }) + + tag, _, err := client.Git.GetTag(context.Background(), "o", "r", "s") + if err != nil { + t.Errorf("Git.GetTag returned error: %v", err) + } + + want := &Tag{Tag: String("t")} + if !reflect.DeepEqual(tag, want) { + t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) + } +} + +func TestGitService_CreateTag(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &createTagRequest{Tag: String("t"), Object: String("s")} + + mux.HandleFunc("/repos/o/r/git/tags", func(w http.ResponseWriter, r *http.Request) { + v := new(createTagRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"tag": "t"}`) + }) + + tag, _, err := client.Git.CreateTag(context.Background(), "o", "r", &Tag{ + Tag: input.Tag, + Object: &GitObject{SHA: input.Object}, + }) + if err != nil { + t.Errorf("Git.CreateTag returned error: %v", err) + } + + want := &Tag{Tag: String("t")} + if !reflect.DeepEqual(tag, want) { + t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) + } +} diff --git a/vendor/github.com/google/go-github/github/git_trees.go b/vendor/github.com/google/go-github/github/git_trees.go new file mode 100644 index 00000000..4d6809a8 --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_trees.go @@ -0,0 +1,93 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Tree represents a GitHub tree. +type Tree struct { + SHA *string `json:"sha,omitempty"` + Entries []TreeEntry `json:"tree,omitempty"` +} + +func (t Tree) String() string { + return Stringify(t) +} + +// TreeEntry represents the contents of a tree structure. TreeEntry can +// represent either a blob, a commit (in the case of a submodule), or another +// tree. +type TreeEntry struct { + SHA *string `json:"sha,omitempty"` + Path *string `json:"path,omitempty"` + Mode *string `json:"mode,omitempty"` + Type *string `json:"type,omitempty"` + Size *int `json:"size,omitempty"` + Content *string `json:"content,omitempty"` + URL *string `json:"url,omitempty"` +} + +func (t TreeEntry) String() string { + return Stringify(t) +} + +// GetTree fetches the Tree object for a given sha hash from a repository. +// +// GitHub API docs: https://developer.github.com/v3/git/trees/#get-a-tree +func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha string, recursive bool) (*Tree, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/trees/%v", owner, repo, sha) + if recursive { + u += "?recursive=1" + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + t := new(Tree) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// createTree represents the body of a CreateTree request. +type createTree struct { + BaseTree string `json:"base_tree,omitempty"` + Entries []TreeEntry `json:"tree"` +} + +// CreateTree creates a new tree in a repository. If both a tree and a nested +// path modifying that tree are specified, it will overwrite the contents of +// that tree with the new path contents and write a new tree out. +// +// GitHub API docs: https://developer.github.com/v3/git/trees/#create-a-tree +func (s *GitService) CreateTree(ctx context.Context, owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo) + + body := &createTree{ + BaseTree: baseTree, + Entries: entries, + } + req, err := s.client.NewRequest("POST", u, body) + if err != nil { + return nil, nil, err + } + + t := new(Tree) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/git_trees_test.go b/vendor/github.com/google/go-github/github/git_trees_test.go new file mode 100644 index 00000000..e2a20efa --- /dev/null +++ b/vendor/github.com/google/go-github/github/git_trees_test.go @@ -0,0 +1,191 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestGitService_GetTree(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/git/trees/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "sha": "s", + "tree": [ { "type": "blob" } ] + }`) + }) + + tree, _, err := client.Git.GetTree(context.Background(), "o", "r", "s", true) + if err != nil { + t.Errorf("Git.GetTree returned error: %v", err) + } + + want := Tree{ + SHA: String("s"), + Entries: []TreeEntry{ + { + Type: String("blob"), + }, + }, + } + if !reflect.DeepEqual(*tree, want) { + t.Errorf("Tree.Get returned %+v, want %+v", *tree, want) + } +} + +func TestGitService_GetTree_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Git.GetTree(context.Background(), "%", "%", "%", false) + testURLParseError(t, err) +} + +func TestGitService_CreateTree(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := []TreeEntry{ + { + Path: String("file.rb"), + Mode: String("100644"), + Type: String("blob"), + SHA: String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), + }, + } + + mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { + v := new(createTree) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + + want := &createTree{ + BaseTree: "b", + Entries: input, + } + if !reflect.DeepEqual(v, want) { + t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) + } + + fmt.Fprint(w, `{ + "sha": "cd8274d15fa3ae2ab983129fb037999f264ba9a7", + "tree": [ + { + "path": "file.rb", + "mode": "100644", + "type": "blob", + "size": 132, + "sha": "7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b" + } + ] + }`) + }) + + tree, _, err := client.Git.CreateTree(context.Background(), "o", "r", "b", input) + if err != nil { + t.Errorf("Git.CreateTree returned error: %v", err) + } + + want := Tree{ + String("cd8274d15fa3ae2ab983129fb037999f264ba9a7"), + []TreeEntry{ + { + Path: String("file.rb"), + Mode: String("100644"), + Type: String("blob"), + Size: Int(132), + SHA: String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), + }, + }, + } + + if !reflect.DeepEqual(*tree, want) { + t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) + } +} + +func TestGitService_CreateTree_Content(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := []TreeEntry{ + { + Path: String("content.md"), + Mode: String("100644"), + Content: String("file content"), + }, + } + + mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { + v := new(createTree) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + + want := &createTree{ + BaseTree: "b", + Entries: input, + } + if !reflect.DeepEqual(v, want) { + t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) + } + + fmt.Fprint(w, `{ + "sha": "5c6780ad2c68743383b740fd1dab6f6a33202b11", + "url": "https://api.github.com/repos/o/r/git/trees/5c6780ad2c68743383b740fd1dab6f6a33202b11", + "tree": [ + { + "mode": "100644", + "type": "blob", + "sha": "aad8feacf6f8063150476a7b2bd9770f2794c08b", + "path": "content.md", + "size": 12, + "url": "https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b" + } + ] + }`) + }) + + tree, _, err := client.Git.CreateTree(context.Background(), "o", "r", "b", input) + if err != nil { + t.Errorf("Git.CreateTree returned error: %v", err) + } + + want := Tree{ + String("5c6780ad2c68743383b740fd1dab6f6a33202b11"), + []TreeEntry{ + { + Path: String("content.md"), + Mode: String("100644"), + Type: String("blob"), + Size: Int(12), + SHA: String("aad8feacf6f8063150476a7b2bd9770f2794c08b"), + URL: String("https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b"), + }, + }, + } + + if !reflect.DeepEqual(*tree, want) { + t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) + } +} + +func TestGitService_CreateTree_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Git.CreateTree(context.Background(), "%", "%", "", nil) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/github-accessors.go b/vendor/github.com/google/go-github/github/github-accessors.go new file mode 100644 index 00000000..30b76739 --- /dev/null +++ b/vendor/github.com/google/go-github/github/github-accessors.go @@ -0,0 +1,10429 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by gen-accessors; DO NOT EDIT. + +package github + +import ( + "encoding/json" + "time" +) + +// GetRetryAfter returns the RetryAfter field if it's non-nil, zero value otherwise. +func (a *AbuseRateLimitError) GetRetryAfter() time.Duration { + if a == nil || a.RetryAfter == nil { + return 0 + } + return *a.RetryAfter +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *AdminEnforcement) GetURL() string { + if a == nil || a.URL == nil { + return "" + } + return *a.URL +} + +// GetComments returns the Comments field. +func (a *AdminStats) GetComments() *CommentStats { + if a == nil { + return nil + } + return a.Comments +} + +// GetGists returns the Gists field. +func (a *AdminStats) GetGists() *GistStats { + if a == nil { + return nil + } + return a.Gists +} + +// GetHooks returns the Hooks field. +func (a *AdminStats) GetHooks() *HookStats { + if a == nil { + return nil + } + return a.Hooks +} + +// GetIssues returns the Issues field. +func (a *AdminStats) GetIssues() *IssueStats { + if a == nil { + return nil + } + return a.Issues +} + +// GetMilestones returns the Milestones field. +func (a *AdminStats) GetMilestones() *MilestoneStats { + if a == nil { + return nil + } + return a.Milestones +} + +// GetOrgs returns the Orgs field. +func (a *AdminStats) GetOrgs() *OrgStats { + if a == nil { + return nil + } + return a.Orgs +} + +// GetPages returns the Pages field. +func (a *AdminStats) GetPages() *PageStats { + if a == nil { + return nil + } + return a.Pages +} + +// GetPulls returns the Pulls field. +func (a *AdminStats) GetPulls() *PullStats { + if a == nil { + return nil + } + return a.Pulls +} + +// GetRepos returns the Repos field. +func (a *AdminStats) GetRepos() *RepoStats { + if a == nil { + return nil + } + return a.Repos +} + +// GetUsers returns the Users field. +func (a *AdminStats) GetUsers() *UserStats { + if a == nil { + return nil + } + return a.Users +} + +// GetVerifiablePasswordAuthentication returns the VerifiablePasswordAuthentication field if it's non-nil, zero value otherwise. +func (a *APIMeta) GetVerifiablePasswordAuthentication() bool { + if a == nil || a.VerifiablePasswordAuthentication == nil { + return false + } + return *a.VerifiablePasswordAuthentication +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *App) GetCreatedAt() time.Time { + if a == nil || a.CreatedAt == nil { + return time.Time{} + } + return *a.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (a *App) GetDescription() string { + if a == nil || a.Description == nil { + return "" + } + return *a.Description +} + +// GetExternalURL returns the ExternalURL field if it's non-nil, zero value otherwise. +func (a *App) GetExternalURL() string { + if a == nil || a.ExternalURL == nil { + return "" + } + return *a.ExternalURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (a *App) GetHTMLURL() string { + if a == nil || a.HTMLURL == nil { + return "" + } + return *a.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *App) GetID() int64 { + if a == nil || a.ID == nil { + return 0 + } + return *a.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *App) GetName() string { + if a == nil || a.Name == nil { + return "" + } + return *a.Name +} + +// GetOwner returns the Owner field. +func (a *App) GetOwner() *User { + if a == nil { + return nil + } + return a.Owner +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *App) GetUpdatedAt() time.Time { + if a == nil || a.UpdatedAt == nil { + return time.Time{} + } + return *a.UpdatedAt +} + +// GetApp returns the App field. +func (a *Authorization) GetApp() *AuthorizationApp { + if a == nil { + return nil + } + return a.App +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *Authorization) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} + } + return *a.CreatedAt +} + +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (a *Authorization) GetFingerprint() string { + if a == nil || a.Fingerprint == nil { + return "" + } + return *a.Fingerprint +} + +// GetHashedToken returns the HashedToken field if it's non-nil, zero value otherwise. +func (a *Authorization) GetHashedToken() string { + if a == nil || a.HashedToken == nil { + return "" + } + return *a.HashedToken +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *Authorization) GetID() int64 { + if a == nil || a.ID == nil { + return 0 + } + return *a.ID +} + +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (a *Authorization) GetNote() string { + if a == nil || a.Note == nil { + return "" + } + return *a.Note +} + +// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. +func (a *Authorization) GetNoteURL() string { + if a == nil || a.NoteURL == nil { + return "" + } + return *a.NoteURL +} + +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (a *Authorization) GetToken() string { + if a == nil || a.Token == nil { + return "" + } + return *a.Token +} + +// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise. +func (a *Authorization) GetTokenLastEight() string { + if a == nil || a.TokenLastEight == nil { + return "" + } + return *a.TokenLastEight +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *Authorization) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} + } + return *a.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *Authorization) GetURL() string { + if a == nil || a.URL == nil { + return "" + } + return *a.URL +} + +// GetUser returns the User field. +func (a *Authorization) GetUser() *User { + if a == nil { + return nil + } + return a.User +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (a *AuthorizationApp) GetClientID() string { + if a == nil || a.ClientID == nil { + return "" + } + return *a.ClientID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *AuthorizationApp) GetName() string { + if a == nil || a.Name == nil { + return "" + } + return *a.Name +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *AuthorizationApp) GetURL() string { + if a == nil || a.URL == nil { + return "" + } + return *a.URL +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetClientID() string { + if a == nil || a.ClientID == nil { + return "" + } + return *a.ClientID +} + +// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetClientSecret() string { + if a == nil || a.ClientSecret == nil { + return "" + } + return *a.ClientSecret +} + +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetFingerprint() string { + if a == nil || a.Fingerprint == nil { + return "" + } + return *a.Fingerprint +} + +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetNote() string { + if a == nil || a.Note == nil { + return "" + } + return *a.Note +} + +// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetNoteURL() string { + if a == nil || a.NoteURL == nil { + return "" + } + return *a.NoteURL +} + +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (a *AuthorizationUpdateRequest) GetFingerprint() string { + if a == nil || a.Fingerprint == nil { + return "" + } + return *a.Fingerprint +} + +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (a *AuthorizationUpdateRequest) GetNote() string { + if a == nil || a.Note == nil { + return "" + } + return *a.Note +} + +// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. +func (a *AuthorizationUpdateRequest) GetNoteURL() string { + if a == nil || a.NoteURL == nil { + return "" + } + return *a.NoteURL +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (b *Blob) GetContent() string { + if b == nil || b.Content == nil { + return "" + } + return *b.Content +} + +// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. +func (b *Blob) GetEncoding() string { + if b == nil || b.Encoding == nil { + return "" + } + return *b.Encoding +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (b *Blob) GetNodeID() string { + if b == nil || b.NodeID == nil { + return "" + } + return *b.NodeID +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (b *Blob) GetSHA() string { + if b == nil || b.SHA == nil { + return "" + } + return *b.SHA +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (b *Blob) GetSize() int { + if b == nil || b.Size == nil { + return 0 + } + return *b.Size +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (b *Blob) GetURL() string { + if b == nil || b.URL == nil { + return "" + } + return *b.URL +} + +// GetCommit returns the Commit field. +func (b *Branch) GetCommit() *RepositoryCommit { + if b == nil { + return nil + } + return b.Commit +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (b *Branch) GetName() string { + if b == nil || b.Name == nil { + return "" + } + return *b.Name +} + +// GetProtected returns the Protected field if it's non-nil, zero value otherwise. +func (b *Branch) GetProtected() bool { + if b == nil || b.Protected == nil { + return false + } + return *b.Protected +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetBody() string { + if c == nil || c.Body == nil { + return "" + } + return *c.Body +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetKey() string { + if c == nil || c.Key == nil { + return "" + } + return *c.Key +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetPath() string { + if c == nil || c.Path == nil { + return "" + } + return *c.Path +} + +// GetRepository returns the Repository field. +func (c *CodeResult) GetRepository() *Repository { + if c == nil { + return nil + } + return c.Repository +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetSHA() string { + if c == nil || c.SHA == nil { + return "" + } + return *c.SHA +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (c *CodeSearchResult) GetIncompleteResults() bool { + if c == nil || c.IncompleteResults == nil { + return false + } + return *c.IncompleteResults +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *CodeSearchResult) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetCommitURL() string { + if c == nil || c.CommitURL == nil { + return "" + } + return *c.CommitURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetRepositoryURL() string { + if c == nil || c.RepositoryURL == nil { + return "" + } + return *c.RepositoryURL +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetSHA() string { + if c == nil || c.SHA == nil { + return "" + } + return *c.SHA +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetState() string { + if c == nil || c.State == nil { + return "" + } + return *c.State +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetTotalCount() int { + if c == nil || c.TotalCount == nil { + return 0 + } + return *c.TotalCount +} + +// GetTotalCommitComments returns the TotalCommitComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalCommitComments() int { + if c == nil || c.TotalCommitComments == nil { + return 0 + } + return *c.TotalCommitComments +} + +// GetTotalGistComments returns the TotalGistComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalGistComments() int { + if c == nil || c.TotalGistComments == nil { + return 0 + } + return *c.TotalGistComments +} + +// GetTotalIssueComments returns the TotalIssueComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalIssueComments() int { + if c == nil || c.TotalIssueComments == nil { + return 0 + } + return *c.TotalIssueComments +} + +// GetTotalPullRequestComments returns the TotalPullRequestComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalPullRequestComments() int { + if c == nil || c.TotalPullRequestComments == nil { + return 0 + } + return *c.TotalPullRequestComments +} + +// GetAuthor returns the Author field. +func (c *Commit) GetAuthor() *CommitAuthor { + if c == nil { + return nil + } + return c.Author +} + +// GetCommentCount returns the CommentCount field if it's non-nil, zero value otherwise. +func (c *Commit) GetCommentCount() int { + if c == nil || c.CommentCount == nil { + return 0 + } + return *c.CommentCount +} + +// GetCommitter returns the Committer field. +func (c *Commit) GetCommitter() *CommitAuthor { + if c == nil { + return nil + } + return c.Committer +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *Commit) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (c *Commit) GetMessage() string { + if c == nil || c.Message == nil { + return "" + } + return *c.Message +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *Commit) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" + } + return *c.NodeID +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *Commit) GetSHA() string { + if c == nil || c.SHA == nil { + return "" + } + return *c.SHA +} + +// GetStats returns the Stats field. +func (c *Commit) GetStats() *CommitStats { + if c == nil { + return nil + } + return c.Stats +} + +// GetTree returns the Tree field. +func (c *Commit) GetTree() *Tree { + if c == nil { + return nil + } + return c.Tree +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *Commit) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetVerification returns the Verification field. +func (c *Commit) GetVerification() *SignatureVerification { + if c == nil { + return nil + } + return c.Verification +} + +// GetDate returns the Date field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetDate() time.Time { + if c == nil || c.Date == nil { + return time.Time{} + } + return *c.Date +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetLogin() string { + if c == nil || c.Login == nil { + return "" + } + return *c.Login +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *CommitCommentEvent) GetAction() string { + if c == nil || c.Action == nil { + return "" + } + return *c.Action +} + +// GetComment returns the Comment field. +func (c *CommitCommentEvent) GetComment() *RepositoryComment { + if c == nil { + return nil + } + return c.Comment +} + +// GetInstallation returns the Installation field. +func (c *CommitCommentEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetRepo returns the Repo field. +func (c *CommitCommentEvent) GetRepo() *Repository { + if c == nil { + return nil + } + return c.Repo +} + +// GetSender returns the Sender field. +func (c *CommitCommentEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetAdditions() int { + if c == nil || c.Additions == nil { + return 0 + } + return *c.Additions +} + +// GetBlobURL returns the BlobURL field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetBlobURL() string { + if c == nil || c.BlobURL == nil { + return "" + } + return *c.BlobURL +} + +// GetChanges returns the Changes field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetChanges() int { + if c == nil || c.Changes == nil { + return 0 + } + return *c.Changes +} + +// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetContentsURL() string { + if c == nil || c.ContentsURL == nil { + return "" + } + return *c.ContentsURL +} + +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetDeletions() int { + if c == nil || c.Deletions == nil { + return 0 + } + return *c.Deletions +} + +// GetFilename returns the Filename field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetFilename() string { + if c == nil || c.Filename == nil { + return "" + } + return *c.Filename +} + +// GetPatch returns the Patch field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetPatch() string { + if c == nil || c.Patch == nil { + return "" + } + return *c.Patch +} + +// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetRawURL() string { + if c == nil || c.RawURL == nil { + return "" + } + return *c.RawURL +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetSHA() string { + if c == nil || c.SHA == nil { + return "" + } + return *c.SHA +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetAuthor returns the Author field. +func (c *CommitResult) GetAuthor() *User { + if c == nil { + return nil + } + return c.Author +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetCommentsURL() string { + if c == nil || c.CommentsURL == nil { + return "" + } + return *c.CommentsURL +} + +// GetCommit returns the Commit field. +func (c *CommitResult) GetCommit() *Commit { + if c == nil { + return nil + } + return c.Commit +} + +// GetCommitter returns the Committer field. +func (c *CommitResult) GetCommitter() *User { + if c == nil { + return nil + } + return c.Committer +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetRepository returns the Repository field. +func (c *CommitResult) GetRepository() *Repository { + if c == nil { + return nil + } + return c.Repository +} + +// GetScore returns the Score field. +func (c *CommitResult) GetScore() *float64 { + if c == nil { + return nil + } + return c.Score +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetSHA() string { + if c == nil || c.SHA == nil { + return "" + } + return *c.SHA +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetAheadBy returns the AheadBy field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetAheadBy() int { + if c == nil || c.AheadBy == nil { + return 0 + } + return *c.AheadBy +} + +// GetBaseCommit returns the BaseCommit field. +func (c *CommitsComparison) GetBaseCommit() *RepositoryCommit { + if c == nil { + return nil + } + return c.BaseCommit +} + +// GetBehindBy returns the BehindBy field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetBehindBy() int { + if c == nil || c.BehindBy == nil { + return 0 + } + return *c.BehindBy +} + +// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetDiffURL() string { + if c == nil || c.DiffURL == nil { + return "" + } + return *c.DiffURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetMergeBaseCommit returns the MergeBaseCommit field. +func (c *CommitsComparison) GetMergeBaseCommit() *RepositoryCommit { + if c == nil { + return nil + } + return c.MergeBaseCommit +} + +// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetPatchURL() string { + if c == nil || c.PatchURL == nil { + return "" + } + return *c.PatchURL +} + +// GetPermalinkURL returns the PermalinkURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetPermalinkURL() string { + if c == nil || c.PermalinkURL == nil { + return "" + } + return *c.PermalinkURL +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetTotalCommits returns the TotalCommits field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetTotalCommits() int { + if c == nil || c.TotalCommits == nil { + return 0 + } + return *c.TotalCommits +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (c *CommitsSearchResult) GetIncompleteResults() bool { + if c == nil || c.IncompleteResults == nil { + return false + } + return *c.IncompleteResults +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *CommitsSearchResult) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (c *CommitStats) GetAdditions() int { + if c == nil || c.Additions == nil { + return 0 + } + return *c.Additions +} + +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (c *CommitStats) GetDeletions() int { + if c == nil || c.Deletions == nil { + return 0 + } + return *c.Deletions +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *CommitStats) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetCodeOfConduct returns the CodeOfConduct field. +func (c *CommunityHealthFiles) GetCodeOfConduct() *Metric { + if c == nil { + return nil + } + return c.CodeOfConduct +} + +// GetContributing returns the Contributing field. +func (c *CommunityHealthFiles) GetContributing() *Metric { + if c == nil { + return nil + } + return c.Contributing +} + +// GetLicense returns the License field. +func (c *CommunityHealthFiles) GetLicense() *Metric { + if c == nil { + return nil + } + return c.License +} + +// GetReadme returns the Readme field. +func (c *CommunityHealthFiles) GetReadme() *Metric { + if c == nil { + return nil + } + return c.Readme +} + +// GetFiles returns the Files field. +func (c *CommunityHealthMetrics) GetFiles() *CommunityHealthFiles { + if c == nil { + return nil + } + return c.Files +} + +// GetHealthPercentage returns the HealthPercentage field if it's non-nil, zero value otherwise. +func (c *CommunityHealthMetrics) GetHealthPercentage() int { + if c == nil || c.HealthPercentage == nil { + return 0 + } + return *c.HealthPercentage +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CommunityHealthMetrics) GetUpdatedAt() time.Time { + if c == nil || c.UpdatedAt == nil { + return time.Time{} + } + return *c.UpdatedAt +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetAvatarURL() string { + if c == nil || c.AvatarURL == nil { + return "" + } + return *c.AvatarURL +} + +// GetContributions returns the Contributions field if it's non-nil, zero value otherwise. +func (c *Contributor) GetContributions() int { + if c == nil || c.Contributions == nil { + return 0 + } + return *c.Contributions +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetEventsURL() string { + if c == nil || c.EventsURL == nil { + return "" + } + return *c.EventsURL +} + +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetFollowersURL() string { + if c == nil || c.FollowersURL == nil { + return "" + } + return *c.FollowersURL +} + +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetFollowingURL() string { + if c == nil || c.FollowingURL == nil { + return "" + } + return *c.FollowingURL +} + +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetGistsURL() string { + if c == nil || c.GistsURL == nil { + return "" + } + return *c.GistsURL +} + +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetGravatarID() string { + if c == nil || c.GravatarID == nil { + return "" + } + return *c.GravatarID +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *Contributor) GetLogin() string { + if c == nil || c.Login == nil { + return "" + } + return *c.Login +} + +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetOrganizationsURL() string { + if c == nil || c.OrganizationsURL == nil { + return "" + } + return *c.OrganizationsURL +} + +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetReceivedEventsURL() string { + if c == nil || c.ReceivedEventsURL == nil { + return "" + } + return *c.ReceivedEventsURL +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetReposURL() string { + if c == nil || c.ReposURL == nil { + return "" + } + return *c.ReposURL +} + +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (c *Contributor) GetSiteAdmin() bool { + if c == nil || c.SiteAdmin == nil { + return false + } + return *c.SiteAdmin +} + +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetStarredURL() string { + if c == nil || c.StarredURL == nil { + return "" + } + return *c.StarredURL +} + +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetSubscriptionsURL() string { + if c == nil || c.SubscriptionsURL == nil { + return "" + } + return *c.SubscriptionsURL +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *Contributor) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetAuthor returns the Author field. +func (c *ContributorStats) GetAuthor() *Contributor { + if c == nil { + return nil + } + return c.Author +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *ContributorStats) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetInstallation returns the Installation field. +func (c *CreateEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetMasterBranch() string { + if c == nil || c.MasterBranch == nil { + return "" + } + return *c.MasterBranch +} + +// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetPusherType() string { + if c == nil || c.PusherType == nil { + return "" + } + return *c.PusherType +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetRef() string { + if c == nil || c.Ref == nil { + return "" + } + return *c.Ref +} + +// GetRefType returns the RefType field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetRefType() string { + if c == nil || c.RefType == nil { + return "" + } + return *c.RefType +} + +// GetRepo returns the Repo field. +func (c *CreateEvent) GetRepo() *Repository { + if c == nil { + return nil + } + return c.Repo +} + +// GetSender returns the Sender field. +func (c *CreateEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetInstallation returns the Installation field. +func (d *DeleteEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. +func (d *DeleteEvent) GetPusherType() string { + if d == nil || d.PusherType == nil { + return "" + } + return *d.PusherType +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (d *DeleteEvent) GetRef() string { + if d == nil || d.Ref == nil { + return "" + } + return *d.Ref +} + +// GetRefType returns the RefType field if it's non-nil, zero value otherwise. +func (d *DeleteEvent) GetRefType() string { + if d == nil || d.RefType == nil { + return "" + } + return *d.RefType +} + +// GetRepo returns the Repo field. +func (d *DeleteEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeleteEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *Deployment) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetCreator returns the Creator field. +func (d *Deployment) GetCreator() *User { + if d == nil { + return nil + } + return d.Creator +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *Deployment) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *Deployment) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *Deployment) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *Deployment) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (d *Deployment) GetRef() string { + if d == nil || d.Ref == nil { + return "" + } + return *d.Ref +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (d *Deployment) GetRepositoryURL() string { + if d == nil || d.RepositoryURL == nil { + return "" + } + return *d.RepositoryURL +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (d *Deployment) GetSHA() string { + if d == nil || d.SHA == nil { + return "" + } + return *d.SHA +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (d *Deployment) GetStatusesURL() string { + if d == nil || d.StatusesURL == nil { + return "" + } + return *d.StatusesURL +} + +// GetTask returns the Task field if it's non-nil, zero value otherwise. +func (d *Deployment) GetTask() string { + if d == nil || d.Task == nil { + return "" + } + return *d.Task +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *Deployment) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (d *Deployment) GetURL() string { + if d == nil || d.URL == nil { + return "" + } + return *d.URL +} + +// GetDeployment returns the Deployment field. +func (d *DeploymentEvent) GetDeployment() *Deployment { + if d == nil { + return nil + } + return d.Deployment +} + +// GetInstallation returns the Installation field. +func (d *DeploymentEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetRepo returns the Repo field. +func (d *DeploymentEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeploymentEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetAutoMerge returns the AutoMerge field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetAutoMerge() bool { + if d == nil || d.AutoMerge == nil { + return false + } + return *d.AutoMerge +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetPayload returns the Payload field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetPayload() string { + if d == nil || d.Payload == nil { + return "" + } + return *d.Payload +} + +// GetProductionEnvironment returns the ProductionEnvironment field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetProductionEnvironment() bool { + if d == nil || d.ProductionEnvironment == nil { + return false + } + return *d.ProductionEnvironment +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetRef() string { + if d == nil || d.Ref == nil { + return "" + } + return *d.Ref +} + +// GetRequiredContexts returns the RequiredContexts field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetRequiredContexts() []string { + if d == nil || d.RequiredContexts == nil { + return nil + } + return *d.RequiredContexts +} + +// GetTask returns the Task field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetTask() string { + if d == nil || d.Task == nil { + return "" + } + return *d.Task +} + +// GetTransientEnvironment returns the TransientEnvironment field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetTransientEnvironment() bool { + if d == nil || d.TransientEnvironment == nil { + return false + } + return *d.TransientEnvironment +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetCreator returns the Creator field. +func (d *DeploymentStatus) GetCreator() *User { + if d == nil { + return nil + } + return d.Creator +} + +// GetDeploymentURL returns the DeploymentURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetDeploymentURL() string { + if d == nil || d.DeploymentURL == nil { + return "" + } + return *d.DeploymentURL +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetRepositoryURL() string { + if d == nil || d.RepositoryURL == nil { + return "" + } + return *d.RepositoryURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetState() string { + if d == nil || d.State == nil { + return "" + } + return *d.State +} + +// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetTargetURL() string { + if d == nil || d.TargetURL == nil { + return "" + } + return *d.TargetURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetDeployment returns the Deployment field. +func (d *DeploymentStatusEvent) GetDeployment() *Deployment { + if d == nil { + return nil + } + return d.Deployment +} + +// GetDeploymentStatus returns the DeploymentStatus field. +func (d *DeploymentStatusEvent) GetDeploymentStatus() *DeploymentStatus { + if d == nil { + return nil + } + return d.DeploymentStatus +} + +// GetInstallation returns the Installation field. +func (d *DeploymentStatusEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetRepo returns the Repo field. +func (d *DeploymentStatusEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeploymentStatusEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetAutoInactive returns the AutoInactive field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetAutoInactive() bool { + if d == nil || d.AutoInactive == nil { + return false + } + return *d.AutoInactive +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEnvironmentURL returns the EnvironmentURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetEnvironmentURL() string { + if d == nil || d.EnvironmentURL == nil { + return "" + } + return *d.EnvironmentURL +} + +// GetLogURL returns the LogURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetLogURL() string { + if d == nil || d.LogURL == nil { + return "" + } + return *d.LogURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetState() string { + if d == nil || d.State == nil { + return "" + } + return *d.State +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetBody() string { + if d == nil || d.Body == nil { + return "" + } + return *d.Body +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetPath() string { + if d == nil || d.Path == nil { + return "" + } + return *d.Path +} + +// GetPosition returns the Position field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetPosition() int { + if d == nil || d.Position == nil { + return 0 + } + return *d.Position +} + +// GetActor returns the Actor field. +func (e *Event) GetActor() *User { + if e == nil { + return nil + } + return e.Actor +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (e *Event) GetCreatedAt() time.Time { + if e == nil || e.CreatedAt == nil { + return time.Time{} + } + return *e.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (e *Event) GetID() string { + if e == nil || e.ID == nil { + return "" + } + return *e.ID +} + +// GetOrg returns the Org field. +func (e *Event) GetOrg() *Organization { + if e == nil { + return nil + } + return e.Org +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (e *Event) GetPublic() bool { + if e == nil || e.Public == nil { + return false + } + return *e.Public +} + +// GetRawPayload returns the RawPayload field if it's non-nil, zero value otherwise. +func (e *Event) GetRawPayload() json.RawMessage { + if e == nil || e.RawPayload == nil { + return json.RawMessage{} + } + return *e.RawPayload +} + +// GetRepo returns the Repo field. +func (e *Event) GetRepo() *Repository { + if e == nil { + return nil + } + return e.Repo +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (e *Event) GetType() string { + if e == nil || e.Type == nil { + return "" + } + return *e.Type +} + +// GetHRef returns the HRef field if it's non-nil, zero value otherwise. +func (f *FeedLink) GetHRef() string { + if f == nil || f.HRef == nil { + return "" + } + return *f.HRef +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (f *FeedLink) GetType() string { + if f == nil || f.Type == nil { + return "" + } + return *f.Type +} + +// GetCurrentUserActorURL returns the CurrentUserActorURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserActorURL() string { + if f == nil || f.CurrentUserActorURL == nil { + return "" + } + return *f.CurrentUserActorURL +} + +// GetCurrentUserOrganizationURL returns the CurrentUserOrganizationURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserOrganizationURL() string { + if f == nil || f.CurrentUserOrganizationURL == nil { + return "" + } + return *f.CurrentUserOrganizationURL +} + +// GetCurrentUserPublicURL returns the CurrentUserPublicURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserPublicURL() string { + if f == nil || f.CurrentUserPublicURL == nil { + return "" + } + return *f.CurrentUserPublicURL +} + +// GetCurrentUserURL returns the CurrentUserURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserURL() string { + if f == nil || f.CurrentUserURL == nil { + return "" + } + return *f.CurrentUserURL +} + +// GetTimelineURL returns the TimelineURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetTimelineURL() string { + if f == nil || f.TimelineURL == nil { + return "" + } + return *f.TimelineURL +} + +// GetUserURL returns the UserURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetUserURL() string { + if f == nil || f.UserURL == nil { + return "" + } + return *f.UserURL +} + +// GetForkee returns the Forkee field. +func (f *ForkEvent) GetForkee() *Repository { + if f == nil { + return nil + } + return f.Forkee +} + +// GetInstallation returns the Installation field. +func (f *ForkEvent) GetInstallation() *Installation { + if f == nil { + return nil + } + return f.Installation +} + +// GetRepo returns the Repo field. +func (f *ForkEvent) GetRepo() *Repository { + if f == nil { + return nil + } + return f.Repo +} + +// GetSender returns the Sender field. +func (f *ForkEvent) GetSender() *User { + if f == nil { + return nil + } + return f.Sender +} + +// GetComments returns the Comments field if it's non-nil, zero value otherwise. +func (g *Gist) GetComments() int { + if g == nil || g.Comments == nil { + return 0 + } + return *g.Comments +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *Gist) GetCreatedAt() time.Time { + if g == nil || g.CreatedAt == nil { + return time.Time{} + } + return *g.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (g *Gist) GetDescription() string { + if g == nil || g.Description == nil { + return "" + } + return *g.Description +} + +// GetGitPullURL returns the GitPullURL field if it's non-nil, zero value otherwise. +func (g *Gist) GetGitPullURL() string { + if g == nil || g.GitPullURL == nil { + return "" + } + return *g.GitPullURL +} + +// GetGitPushURL returns the GitPushURL field if it's non-nil, zero value otherwise. +func (g *Gist) GetGitPushURL() string { + if g == nil || g.GitPushURL == nil { + return "" + } + return *g.GitPushURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (g *Gist) GetHTMLURL() string { + if g == nil || g.HTMLURL == nil { + return "" + } + return *g.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *Gist) GetID() string { + if g == nil || g.ID == nil { + return "" + } + return *g.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (g *Gist) GetNodeID() string { + if g == nil || g.NodeID == nil { + return "" + } + return *g.NodeID +} + +// GetOwner returns the Owner field. +func (g *Gist) GetOwner() *User { + if g == nil { + return nil + } + return g.Owner +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (g *Gist) GetPublic() bool { + if g == nil || g.Public == nil { + return false + } + return *g.Public +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (g *Gist) GetUpdatedAt() time.Time { + if g == nil || g.UpdatedAt == nil { + return time.Time{} + } + return *g.UpdatedAt +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (g *GistComment) GetBody() string { + if g == nil || g.Body == nil { + return "" + } + return *g.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *GistComment) GetCreatedAt() time.Time { + if g == nil || g.CreatedAt == nil { + return time.Time{} + } + return *g.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GistComment) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GistComment) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetUser returns the User field. +func (g *GistComment) GetUser() *User { + if g == nil { + return nil + } + return g.User +} + +// GetChangeStatus returns the ChangeStatus field. +func (g *GistCommit) GetChangeStatus() *CommitStats { + if g == nil { + return nil + } + return g.ChangeStatus +} + +// GetCommittedAt returns the CommittedAt field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetCommittedAt() Timestamp { + if g == nil || g.CommittedAt == nil { + return Timestamp{} + } + return *g.CommittedAt +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetNodeID() string { + if g == nil || g.NodeID == nil { + return "" + } + return *g.NodeID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetUser returns the User field. +func (g *GistCommit) GetUser() *User { + if g == nil { + return nil + } + return g.User +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetVersion() string { + if g == nil || g.Version == nil { + return "" + } + return *g.Version +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (g *GistFile) GetContent() string { + if g == nil || g.Content == nil { + return "" + } + return *g.Content +} + +// GetFilename returns the Filename field if it's non-nil, zero value otherwise. +func (g *GistFile) GetFilename() string { + if g == nil || g.Filename == nil { + return "" + } + return *g.Filename +} + +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (g *GistFile) GetLanguage() string { + if g == nil || g.Language == nil { + return "" + } + return *g.Language +} + +// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. +func (g *GistFile) GetRawURL() string { + if g == nil || g.RawURL == nil { + return "" + } + return *g.RawURL +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (g *GistFile) GetSize() int { + if g == nil || g.Size == nil { + return 0 + } + return *g.Size +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (g *GistFile) GetType() string { + if g == nil || g.Type == nil { + return "" + } + return *g.Type +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *GistFork) GetCreatedAt() Timestamp { + if g == nil || g.CreatedAt == nil { + return Timestamp{} + } + return *g.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GistFork) GetID() string { + if g == nil || g.ID == nil { + return "" + } + return *g.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (g *GistFork) GetNodeID() string { + if g == nil || g.NodeID == nil { + return "" + } + return *g.NodeID +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (g *GistFork) GetUpdatedAt() Timestamp { + if g == nil || g.UpdatedAt == nil { + return Timestamp{} + } + return *g.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GistFork) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetUser returns the User field. +func (g *GistFork) GetUser() *User { + if g == nil { + return nil + } + return g.User +} + +// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. +func (g *GistStats) GetPrivateGists() int { + if g == nil || g.PrivateGists == nil { + return 0 + } + return *g.PrivateGists +} + +// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. +func (g *GistStats) GetPublicGists() int { + if g == nil || g.PublicGists == nil { + return 0 + } + return *g.PublicGists +} + +// GetTotalGists returns the TotalGists field if it's non-nil, zero value otherwise. +func (g *GistStats) GetTotalGists() int { + if g == nil || g.TotalGists == nil { + return 0 + } + return *g.TotalGists +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (g *Gitignore) GetName() string { + if g == nil || g.Name == nil { + return "" + } + return *g.Name +} + +// GetSource returns the Source field if it's non-nil, zero value otherwise. +func (g *Gitignore) GetSource() string { + if g == nil || g.Source == nil { + return "" + } + return *g.Source +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (g *GitObject) GetSHA() string { + if g == nil || g.SHA == nil { + return "" + } + return *g.SHA +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (g *GitObject) GetType() string { + if g == nil || g.Type == nil { + return "" + } + return *g.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GitObject) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetInstallation returns the Installation field. +func (g *GollumEvent) GetInstallation() *Installation { + if g == nil { + return nil + } + return g.Installation +} + +// GetRepo returns the Repo field. +func (g *GollumEvent) GetRepo() *Repository { + if g == nil { + return nil + } + return g.Repo +} + +// GetSender returns the Sender field. +func (g *GollumEvent) GetSender() *User { + if g == nil { + return nil + } + return g.Sender +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (g *GPGEmail) GetEmail() string { + if g == nil || g.Email == nil { + return "" + } + return *g.Email +} + +// GetVerified returns the Verified field if it's non-nil, zero value otherwise. +func (g *GPGEmail) GetVerified() bool { + if g == nil || g.Verified == nil { + return false + } + return *g.Verified +} + +// GetCanCertify returns the CanCertify field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanCertify() bool { + if g == nil || g.CanCertify == nil { + return false + } + return *g.CanCertify +} + +// GetCanEncryptComms returns the CanEncryptComms field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanEncryptComms() bool { + if g == nil || g.CanEncryptComms == nil { + return false + } + return *g.CanEncryptComms +} + +// GetCanEncryptStorage returns the CanEncryptStorage field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanEncryptStorage() bool { + if g == nil || g.CanEncryptStorage == nil { + return false + } + return *g.CanEncryptStorage +} + +// GetCanSign returns the CanSign field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanSign() bool { + if g == nil || g.CanSign == nil { + return false + } + return *g.CanSign +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCreatedAt() time.Time { + if g == nil || g.CreatedAt == nil { + return time.Time{} + } + return *g.CreatedAt +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetExpiresAt() time.Time { + if g == nil || g.ExpiresAt == nil { + return time.Time{} + } + return *g.ExpiresAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetKeyID returns the KeyID field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetKeyID() string { + if g == nil || g.KeyID == nil { + return "" + } + return *g.KeyID +} + +// GetPrimaryKeyID returns the PrimaryKeyID field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetPrimaryKeyID() int64 { + if g == nil || g.PrimaryKeyID == nil { + return 0 + } + return *g.PrimaryKeyID +} + +// GetPublicKey returns the PublicKey field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetPublicKey() string { + if g == nil || g.PublicKey == nil { + return "" + } + return *g.PublicKey +} + +// GetApp returns the App field. +func (g *Grant) GetApp() *AuthorizationApp { + if g == nil { + return nil + } + return g.App +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *Grant) GetCreatedAt() Timestamp { + if g == nil || g.CreatedAt == nil { + return Timestamp{} + } + return *g.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *Grant) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (g *Grant) GetUpdatedAt() Timestamp { + if g == nil || g.UpdatedAt == nil { + return Timestamp{} + } + return *g.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *Grant) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetActive returns the Active field if it's non-nil, zero value otherwise. +func (h *Hook) GetActive() bool { + if h == nil || h.Active == nil { + return false + } + return *h.Active +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (h *Hook) GetCreatedAt() time.Time { + if h == nil || h.CreatedAt == nil { + return time.Time{} + } + return *h.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *Hook) GetID() int64 { + if h == nil || h.ID == nil { + return 0 + } + return *h.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (h *Hook) GetName() string { + if h == nil || h.Name == nil { + return "" + } + return *h.Name +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (h *Hook) GetUpdatedAt() time.Time { + if h == nil || h.UpdatedAt == nil { + return time.Time{} + } + return *h.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (h *Hook) GetURL() string { + if h == nil || h.URL == nil { + return "" + } + return *h.URL +} + +// GetActiveHooks returns the ActiveHooks field if it's non-nil, zero value otherwise. +func (h *HookStats) GetActiveHooks() int { + if h == nil || h.ActiveHooks == nil { + return 0 + } + return *h.ActiveHooks +} + +// GetInactiveHooks returns the InactiveHooks field if it's non-nil, zero value otherwise. +func (h *HookStats) GetInactiveHooks() int { + if h == nil || h.InactiveHooks == nil { + return 0 + } + return *h.InactiveHooks +} + +// GetTotalHooks returns the TotalHooks field if it's non-nil, zero value otherwise. +func (h *HookStats) GetTotalHooks() int { + if h == nil || h.TotalHooks == nil { + return 0 + } + return *h.TotalHooks +} + +// GetAuthorsCount returns the AuthorsCount field if it's non-nil, zero value otherwise. +func (i *Import) GetAuthorsCount() int { + if i == nil || i.AuthorsCount == nil { + return 0 + } + return *i.AuthorsCount +} + +// GetAuthorsURL returns the AuthorsURL field if it's non-nil, zero value otherwise. +func (i *Import) GetAuthorsURL() string { + if i == nil || i.AuthorsURL == nil { + return "" + } + return *i.AuthorsURL +} + +// GetCommitCount returns the CommitCount field if it's non-nil, zero value otherwise. +func (i *Import) GetCommitCount() int { + if i == nil || i.CommitCount == nil { + return 0 + } + return *i.CommitCount +} + +// GetFailedStep returns the FailedStep field if it's non-nil, zero value otherwise. +func (i *Import) GetFailedStep() string { + if i == nil || i.FailedStep == nil { + return "" + } + return *i.FailedStep +} + +// GetHasLargeFiles returns the HasLargeFiles field if it's non-nil, zero value otherwise. +func (i *Import) GetHasLargeFiles() bool { + if i == nil || i.HasLargeFiles == nil { + return false + } + return *i.HasLargeFiles +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *Import) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetHumanName returns the HumanName field if it's non-nil, zero value otherwise. +func (i *Import) GetHumanName() string { + if i == nil || i.HumanName == nil { + return "" + } + return *i.HumanName +} + +// GetLargeFilesCount returns the LargeFilesCount field if it's non-nil, zero value otherwise. +func (i *Import) GetLargeFilesCount() int { + if i == nil || i.LargeFilesCount == nil { + return 0 + } + return *i.LargeFilesCount +} + +// GetLargeFilesSize returns the LargeFilesSize field if it's non-nil, zero value otherwise. +func (i *Import) GetLargeFilesSize() int { + if i == nil || i.LargeFilesSize == nil { + return 0 + } + return *i.LargeFilesSize +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (i *Import) GetMessage() string { + if i == nil || i.Message == nil { + return "" + } + return *i.Message +} + +// GetPercent returns the Percent field if it's non-nil, zero value otherwise. +func (i *Import) GetPercent() int { + if i == nil || i.Percent == nil { + return 0 + } + return *i.Percent +} + +// GetPushPercent returns the PushPercent field if it's non-nil, zero value otherwise. +func (i *Import) GetPushPercent() int { + if i == nil || i.PushPercent == nil { + return 0 + } + return *i.PushPercent +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (i *Import) GetRepositoryURL() string { + if i == nil || i.RepositoryURL == nil { + return "" + } + return *i.RepositoryURL +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (i *Import) GetStatus() string { + if i == nil || i.Status == nil { + return "" + } + return *i.Status +} + +// GetStatusText returns the StatusText field if it's non-nil, zero value otherwise. +func (i *Import) GetStatusText() string { + if i == nil || i.StatusText == nil { + return "" + } + return *i.StatusText +} + +// GetTFVCProject returns the TFVCProject field if it's non-nil, zero value otherwise. +func (i *Import) GetTFVCProject() string { + if i == nil || i.TFVCProject == nil { + return "" + } + return *i.TFVCProject +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *Import) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetUseLFS returns the UseLFS field if it's non-nil, zero value otherwise. +func (i *Import) GetUseLFS() string { + if i == nil || i.UseLFS == nil { + return "" + } + return *i.UseLFS +} + +// GetVCS returns the VCS field if it's non-nil, zero value otherwise. +func (i *Import) GetVCS() string { + if i == nil || i.VCS == nil { + return "" + } + return *i.VCS +} + +// GetVCSPassword returns the VCSPassword field if it's non-nil, zero value otherwise. +func (i *Import) GetVCSPassword() string { + if i == nil || i.VCSPassword == nil { + return "" + } + return *i.VCSPassword +} + +// GetVCSURL returns the VCSURL field if it's non-nil, zero value otherwise. +func (i *Import) GetVCSURL() string { + if i == nil || i.VCSURL == nil { + return "" + } + return *i.VCSURL +} + +// GetVCSUsername returns the VCSUsername field if it's non-nil, zero value otherwise. +func (i *Import) GetVCSUsername() string { + if i == nil || i.VCSUsername == nil { + return "" + } + return *i.VCSUsername +} + +// GetAccessTokensURL returns the AccessTokensURL field if it's non-nil, zero value otherwise. +func (i *Installation) GetAccessTokensURL() string { + if i == nil || i.AccessTokensURL == nil { + return "" + } + return *i.AccessTokensURL +} + +// GetAccount returns the Account field. +func (i *Installation) GetAccount() *User { + if i == nil { + return nil + } + return i.Account +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *Installation) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *Installation) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. +func (i *Installation) GetRepositoriesURL() string { + if i == nil || i.RepositoriesURL == nil { + return "" + } + return *i.RepositoriesURL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *InstallationEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetInstallation returns the Installation field. +func (i *InstallationEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetSender returns the Sender field. +func (i *InstallationEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *InstallationRepositoriesEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetInstallation returns the Installation field. +func (i *InstallationRepositoriesEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. +func (i *InstallationRepositoriesEvent) GetRepositorySelection() string { + if i == nil || i.RepositorySelection == nil { + return "" + } + return *i.RepositorySelection +} + +// GetSender returns the Sender field. +func (i *InstallationRepositoriesEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (i *InstallationToken) GetExpiresAt() time.Time { + if i == nil || i.ExpiresAt == nil { + return time.Time{} + } + return *i.ExpiresAt +} + +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (i *InstallationToken) GetToken() string { + if i == nil || i.Token == nil { + return "" + } + return *i.Token +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *Invitation) GetCreatedAt() time.Time { + if i == nil || i.CreatedAt == nil { + return time.Time{} + } + return *i.CreatedAt +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (i *Invitation) GetEmail() string { + if i == nil || i.Email == nil { + return "" + } + return *i.Email +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *Invitation) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetInviter returns the Inviter field. +func (i *Invitation) GetInviter() *User { + if i == nil { + return nil + } + return i.Inviter +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (i *Invitation) GetLogin() string { + if i == nil || i.Login == nil { + return "" + } + return *i.Login +} + +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (i *Invitation) GetRole() string { + if i == nil || i.Role == nil { + return "" + } + return *i.Role +} + +// GetAssignee returns the Assignee field. +func (i *Issue) GetAssignee() *User { + if i == nil { + return nil + } + return i.Assignee +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (i *Issue) GetBody() string { + if i == nil || i.Body == nil { + return "" + } + return *i.Body +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (i *Issue) GetClosedAt() time.Time { + if i == nil || i.ClosedAt == nil { + return time.Time{} + } + return *i.ClosedAt +} + +// GetClosedBy returns the ClosedBy field. +func (i *Issue) GetClosedBy() *User { + if i == nil { + return nil + } + return i.ClosedBy +} + +// GetComments returns the Comments field if it's non-nil, zero value otherwise. +func (i *Issue) GetComments() int { + if i == nil || i.Comments == nil { + return 0 + } + return *i.Comments +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetCommentsURL() string { + if i == nil || i.CommentsURL == nil { + return "" + } + return *i.CommentsURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *Issue) GetCreatedAt() time.Time { + if i == nil || i.CreatedAt == nil { + return time.Time{} + } + return *i.CreatedAt +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetEventsURL() string { + if i == nil || i.EventsURL == nil { + return "" + } + return *i.EventsURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *Issue) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetLabelsURL() string { + if i == nil || i.LabelsURL == nil { + return "" + } + return *i.LabelsURL +} + +// GetLocked returns the Locked field if it's non-nil, zero value otherwise. +func (i *Issue) GetLocked() bool { + if i == nil || i.Locked == nil { + return false + } + return *i.Locked +} + +// GetMilestone returns the Milestone field. +func (i *Issue) GetMilestone() *Milestone { + if i == nil { + return nil + } + return i.Milestone +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *Issue) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" + } + return *i.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (i *Issue) GetNumber() int { + if i == nil || i.Number == nil { + return 0 + } + return *i.Number +} + +// GetPullRequestLinks returns the PullRequestLinks field. +func (i *Issue) GetPullRequestLinks() *PullRequestLinks { + if i == nil { + return nil + } + return i.PullRequestLinks +} + +// GetReactions returns the Reactions field. +func (i *Issue) GetReactions() *Reactions { + if i == nil { + return nil + } + return i.Reactions +} + +// GetRepository returns the Repository field. +func (i *Issue) GetRepository() *Repository { + if i == nil { + return nil + } + return i.Repository +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetRepositoryURL() string { + if i == nil || i.RepositoryURL == nil { + return "" + } + return *i.RepositoryURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (i *Issue) GetState() string { + if i == nil || i.State == nil { + return "" + } + return *i.State +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (i *Issue) GetTitle() string { + if i == nil || i.Title == nil { + return "" + } + return *i.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *Issue) GetUpdatedAt() time.Time { + if i == nil || i.UpdatedAt == nil { + return time.Time{} + } + return *i.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *Issue) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetUser returns the User field. +func (i *Issue) GetUser() *User { + if i == nil { + return nil + } + return i.User +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetBody() string { + if i == nil || i.Body == nil { + return "" + } + return *i.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetCreatedAt() time.Time { + if i == nil || i.CreatedAt == nil { + return time.Time{} + } + return *i.CreatedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetIssueURL() string { + if i == nil || i.IssueURL == nil { + return "" + } + return *i.IssueURL +} + +// GetReactions returns the Reactions field. +func (i *IssueComment) GetReactions() *Reactions { + if i == nil { + return nil + } + return i.Reactions +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetUpdatedAt() time.Time { + if i == nil || i.UpdatedAt == nil { + return time.Time{} + } + return *i.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetUser returns the User field. +func (i *IssueComment) GetUser() *User { + if i == nil { + return nil + } + return i.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *IssueCommentEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetChanges returns the Changes field. +func (i *IssueCommentEvent) GetChanges() *EditChange { + if i == nil { + return nil + } + return i.Changes +} + +// GetComment returns the Comment field. +func (i *IssueCommentEvent) GetComment() *IssueComment { + if i == nil { + return nil + } + return i.Comment +} + +// GetInstallation returns the Installation field. +func (i *IssueCommentEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetIssue returns the Issue field. +func (i *IssueCommentEvent) GetIssue() *Issue { + if i == nil { + return nil + } + return i.Issue +} + +// GetRepo returns the Repo field. +func (i *IssueCommentEvent) GetRepo() *Repository { + if i == nil { + return nil + } + return i.Repo +} + +// GetSender returns the Sender field. +func (i *IssueCommentEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetActor returns the Actor field. +func (i *IssueEvent) GetActor() *User { + if i == nil { + return nil + } + return i.Actor +} + +// GetAssignee returns the Assignee field. +func (i *IssueEvent) GetAssignee() *User { + if i == nil { + return nil + } + return i.Assignee +} + +// GetAssigner returns the Assigner field. +func (i *IssueEvent) GetAssigner() *User { + if i == nil { + return nil + } + return i.Assigner +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetCommitID() string { + if i == nil || i.CommitID == nil { + return "" + } + return *i.CommitID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetCreatedAt() time.Time { + if i == nil || i.CreatedAt == nil { + return time.Time{} + } + return *i.CreatedAt +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetEvent() string { + if i == nil || i.Event == nil { + return "" + } + return *i.Event +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetIssue returns the Issue field. +func (i *IssueEvent) GetIssue() *Issue { + if i == nil { + return nil + } + return i.Issue +} + +// GetLabel returns the Label field. +func (i *IssueEvent) GetLabel() *Label { + if i == nil { + return nil + } + return i.Label +} + +// GetMilestone returns the Milestone field. +func (i *IssueEvent) GetMilestone() *Milestone { + if i == nil { + return nil + } + return i.Milestone +} + +// GetRename returns the Rename field. +func (i *IssueEvent) GetRename() *Rename { + if i == nil { + return nil + } + return i.Rename +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetAssignee returns the Assignee field if it's non-nil, zero value otherwise. +func (i *IssueRequest) GetAssignee() string { + if i == nil || i.Assignee == nil { + return "" + } + return *i.Assignee +} + +// GetAssignees returns the Assignees field if it's non-nil, zero value otherwise. +func (i *IssueRequest) GetAssignees() []string { + if i == nil || i.Assignees == nil { + return nil + } + return *i.Assignees +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (i *IssueRequest) GetBody() string { + if i == nil || i.Body == nil { + return "" + } + return *i.Body +} + +// GetLabels returns the Labels field if it's non-nil, zero value otherwise. +func (i *IssueRequest) GetLabels() []string { + if i == nil || i.Labels == nil { + return nil + } + return *i.Labels +} + +// GetMilestone returns the Milestone field if it's non-nil, zero value otherwise. +func (i *IssueRequest) GetMilestone() int { + if i == nil || i.Milestone == nil { + return 0 + } + return *i.Milestone +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (i *IssueRequest) GetState() string { + if i == nil || i.State == nil { + return "" + } + return *i.State +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (i *IssueRequest) GetTitle() string { + if i == nil || i.Title == nil { + return "" + } + return *i.Title +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *IssuesEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetAssignee returns the Assignee field. +func (i *IssuesEvent) GetAssignee() *User { + if i == nil { + return nil + } + return i.Assignee +} + +// GetChanges returns the Changes field. +func (i *IssuesEvent) GetChanges() *EditChange { + if i == nil { + return nil + } + return i.Changes +} + +// GetInstallation returns the Installation field. +func (i *IssuesEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetIssue returns the Issue field. +func (i *IssuesEvent) GetIssue() *Issue { + if i == nil { + return nil + } + return i.Issue +} + +// GetLabel returns the Label field. +func (i *IssuesEvent) GetLabel() *Label { + if i == nil { + return nil + } + return i.Label +} + +// GetRepo returns the Repo field. +func (i *IssuesEvent) GetRepo() *Repository { + if i == nil { + return nil + } + return i.Repo +} + +// GetSender returns the Sender field. +func (i *IssuesEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (i *IssuesSearchResult) GetIncompleteResults() bool { + if i == nil || i.IncompleteResults == nil { + return false + } + return *i.IncompleteResults +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (i *IssuesSearchResult) GetTotal() int { + if i == nil || i.Total == nil { + return 0 + } + return *i.Total +} + +// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetClosedIssues() int { + if i == nil || i.ClosedIssues == nil { + return 0 + } + return *i.ClosedIssues +} + +// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetOpenIssues() int { + if i == nil || i.OpenIssues == nil { + return 0 + } + return *i.OpenIssues +} + +// GetTotalIssues returns the TotalIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetTotalIssues() int { + if i == nil || i.TotalIssues == nil { + return 0 + } + return *i.TotalIssues +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (k *Key) GetID() int64 { + if k == nil || k.ID == nil { + return 0 + } + return *k.ID +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (k *Key) GetKey() string { + if k == nil || k.Key == nil { + return "" + } + return *k.Key +} + +// GetReadOnly returns the ReadOnly field if it's non-nil, zero value otherwise. +func (k *Key) GetReadOnly() bool { + if k == nil || k.ReadOnly == nil { + return false + } + return *k.ReadOnly +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (k *Key) GetTitle() string { + if k == nil || k.Title == nil { + return "" + } + return *k.Title +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (k *Key) GetURL() string { + if k == nil || k.URL == nil { + return "" + } + return *k.URL +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (l *Label) GetColor() string { + if l == nil || l.Color == nil { + return "" + } + return *l.Color +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (l *Label) GetID() int64 { + if l == nil || l.ID == nil { + return 0 + } + return *l.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (l *Label) GetName() string { + if l == nil || l.Name == nil { + return "" + } + return *l.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (l *Label) GetNodeID() string { + if l == nil || l.NodeID == nil { + return "" + } + return *l.NodeID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (l *Label) GetURL() string { + if l == nil || l.URL == nil { + return "" + } + return *l.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (l *LabelEvent) GetAction() string { + if l == nil || l.Action == nil { + return "" + } + return *l.Action +} + +// GetChanges returns the Changes field. +func (l *LabelEvent) GetChanges() *EditChange { + if l == nil { + return nil + } + return l.Changes +} + +// GetInstallation returns the Installation field. +func (l *LabelEvent) GetInstallation() *Installation { + if l == nil { + return nil + } + return l.Installation +} + +// GetLabel returns the Label field. +func (l *LabelEvent) GetLabel() *Label { + if l == nil { + return nil + } + return l.Label +} + +// GetOrg returns the Org field. +func (l *LabelEvent) GetOrg() *Organization { + if l == nil { + return nil + } + return l.Org +} + +// GetRepo returns the Repo field. +func (l *LabelEvent) GetRepo() *Repository { + if l == nil { + return nil + } + return l.Repo +} + +// GetOID returns the OID field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetOID() string { + if l == nil || l.OID == nil { + return "" + } + return *l.OID +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetPath() string { + if l == nil || l.Path == nil { + return "" + } + return *l.Path +} + +// GetRefName returns the RefName field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetRefName() string { + if l == nil || l.RefName == nil { + return "" + } + return *l.RefName +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetSize() int { + if l == nil || l.Size == nil { + return 0 + } + return *l.Size +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (l *License) GetBody() string { + if l == nil || l.Body == nil { + return "" + } + return *l.Body +} + +// GetConditions returns the Conditions field if it's non-nil, zero value otherwise. +func (l *License) GetConditions() []string { + if l == nil || l.Conditions == nil { + return nil + } + return *l.Conditions +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (l *License) GetDescription() string { + if l == nil || l.Description == nil { + return "" + } + return *l.Description +} + +// GetFeatured returns the Featured field if it's non-nil, zero value otherwise. +func (l *License) GetFeatured() bool { + if l == nil || l.Featured == nil { + return false + } + return *l.Featured +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (l *License) GetHTMLURL() string { + if l == nil || l.HTMLURL == nil { + return "" + } + return *l.HTMLURL +} + +// GetImplementation returns the Implementation field if it's non-nil, zero value otherwise. +func (l *License) GetImplementation() string { + if l == nil || l.Implementation == nil { + return "" + } + return *l.Implementation +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (l *License) GetKey() string { + if l == nil || l.Key == nil { + return "" + } + return *l.Key +} + +// GetLimitations returns the Limitations field if it's non-nil, zero value otherwise. +func (l *License) GetLimitations() []string { + if l == nil || l.Limitations == nil { + return nil + } + return *l.Limitations +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (l *License) GetName() string { + if l == nil || l.Name == nil { + return "" + } + return *l.Name +} + +// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. +func (l *License) GetPermissions() []string { + if l == nil || l.Permissions == nil { + return nil + } + return *l.Permissions +} + +// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise. +func (l *License) GetSPDXID() string { + if l == nil || l.SPDXID == nil { + return "" + } + return *l.SPDXID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (l *License) GetURL() string { + if l == nil || l.URL == nil { + return "" + } + return *l.URL +} + +// GetAccountsURL returns the AccountsURL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetAccountsURL() string { + if m == nil || m.AccountsURL == nil { + return "" + } + return *m.AccountsURL +} + +// GetBullets returns the Bullets field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetBullets() []string { + if m == nil || m.Bullets == nil { + return nil + } + return *m.Bullets +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetDescription() string { + if m == nil || m.Description == nil { + return "" + } + return *m.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetMonthlyPriceInCents returns the MonthlyPriceInCents field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetMonthlyPriceInCents() int { + if m == nil || m.MonthlyPriceInCents == nil { + return 0 + } + return *m.MonthlyPriceInCents +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetName() string { + if m == nil || m.Name == nil { + return "" + } + return *m.Name +} + +// GetPriceModel returns the PriceModel field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetPriceModel() string { + if m == nil || m.PriceModel == nil { + return "" + } + return *m.PriceModel +} + +// GetUnitName returns the UnitName field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetUnitName() string { + if m == nil || m.UnitName == nil { + return "" + } + return *m.UnitName +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetYearlyPriceInCents returns the YearlyPriceInCents field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetYearlyPriceInCents() int { + if m == nil || m.YearlyPriceInCents == nil { + return 0 + } + return *m.YearlyPriceInCents +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetEmail() string { + if m == nil || m.Email == nil { + return "" + } + return *m.Email +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetLogin() string { + if m == nil || m.Login == nil { + return "" + } + return *m.Login +} + +// GetMarketplacePurchase returns the MarketplacePurchase field. +func (m *MarketplacePlanAccount) GetMarketplacePurchase() *MarketplacePurchase { + if m == nil { + return nil + } + return m.MarketplacePurchase +} + +// GetOrganizationBillingEmail returns the OrganizationBillingEmail field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetOrganizationBillingEmail() string { + if m == nil || m.OrganizationBillingEmail == nil { + return "" + } + return *m.OrganizationBillingEmail +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetType() string { + if m == nil || m.Type == nil { + return "" + } + return *m.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetAccount returns the Account field. +func (m *MarketplacePurchase) GetAccount() *MarketplacePlanAccount { + if m == nil { + return nil + } + return m.Account +} + +// GetBillingCycle returns the BillingCycle field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetBillingCycle() string { + if m == nil || m.BillingCycle == nil { + return "" + } + return *m.BillingCycle +} + +// GetNextBillingDate returns the NextBillingDate field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetNextBillingDate() string { + if m == nil || m.NextBillingDate == nil { + return "" + } + return *m.NextBillingDate +} + +// GetPlan returns the Plan field. +func (m *MarketplacePurchase) GetPlan() *MarketplacePlan { + if m == nil { + return nil + } + return m.Plan +} + +// GetUnitCount returns the UnitCount field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetUnitCount() int { + if m == nil || m.UnitCount == nil { + return 0 + } + return *m.UnitCount +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseEvent) GetEffectiveDate() Timestamp { + if m == nil || m.EffectiveDate == nil { + return Timestamp{} + } + return *m.EffectiveDate +} + +// GetInstallation returns the Installation field. +func (m *MarketplacePurchaseEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMarketplacePurchase returns the MarketplacePurchase field. +func (m *MarketplacePurchaseEvent) GetMarketplacePurchase() *MarketplacePurchase { + if m == nil { + return nil + } + return m.MarketplacePurchase +} + +// GetPreviousMarketplacePurchase returns the PreviousMarketplacePurchase field. +func (m *MarketplacePurchaseEvent) GetPreviousMarketplacePurchase() *MarketplacePurchase { + if m == nil { + return nil + } + return m.PreviousMarketplacePurchase +} + +// GetSender returns the Sender field. +func (m *MarketplacePurchaseEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetText returns the Text field if it's non-nil, zero value otherwise. +func (m *Match) GetText() string { + if m == nil || m.Text == nil { + return "" + } + return *m.Text +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MemberEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetInstallation returns the Installation field. +func (m *MemberEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMember returns the Member field. +func (m *MemberEvent) GetMember() *User { + if m == nil { + return nil + } + return m.Member +} + +// GetRepo returns the Repo field. +func (m *MemberEvent) GetRepo() *Repository { + if m == nil { + return nil + } + return m.Repo +} + +// GetSender returns the Sender field. +func (m *MemberEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetOrganization returns the Organization field. +func (m *Membership) GetOrganization() *Organization { + if m == nil { + return nil + } + return m.Organization +} + +// GetOrganizationURL returns the OrganizationURL field if it's non-nil, zero value otherwise. +func (m *Membership) GetOrganizationURL() string { + if m == nil || m.OrganizationURL == nil { + return "" + } + return *m.OrganizationURL +} + +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (m *Membership) GetRole() string { + if m == nil || m.Role == nil { + return "" + } + return *m.Role +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *Membership) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Membership) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetUser returns the User field. +func (m *Membership) GetUser() *User { + if m == nil { + return nil + } + return m.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MembershipEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetInstallation returns the Installation field. +func (m *MembershipEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMember returns the Member field. +func (m *MembershipEvent) GetMember() *User { + if m == nil { + return nil + } + return m.Member +} + +// GetOrg returns the Org field. +func (m *MembershipEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetScope returns the Scope field if it's non-nil, zero value otherwise. +func (m *MembershipEvent) GetScope() string { + if m == nil || m.Scope == nil { + return "" + } + return *m.Scope +} + +// GetSender returns the Sender field. +func (m *MembershipEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetTeam returns the Team field. +func (m *MembershipEvent) GetTeam() *Team { + if m == nil { + return nil + } + return m.Team +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (m *Metric) GetHTMLURL() string { + if m == nil || m.HTMLURL == nil { + return "" + } + return *m.HTMLURL +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (m *Metric) GetKey() string { + if m == nil || m.Key == nil { + return "" + } + return *m.Key +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (m *Metric) GetName() string { + if m == nil || m.Name == nil { + return "" + } + return *m.Name +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Metric) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (m *Migration) GetCreatedAt() string { + if m == nil || m.CreatedAt == nil { + return "" + } + return *m.CreatedAt +} + +// GetExcludeAttachments returns the ExcludeAttachments field if it's non-nil, zero value otherwise. +func (m *Migration) GetExcludeAttachments() bool { + if m == nil || m.ExcludeAttachments == nil { + return false + } + return *m.ExcludeAttachments +} + +// GetGUID returns the GUID field if it's non-nil, zero value otherwise. +func (m *Migration) GetGUID() string { + if m == nil || m.GUID == nil { + return "" + } + return *m.GUID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *Migration) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLockRepositories returns the LockRepositories field if it's non-nil, zero value otherwise. +func (m *Migration) GetLockRepositories() bool { + if m == nil || m.LockRepositories == nil { + return false + } + return *m.LockRepositories +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *Migration) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (m *Migration) GetUpdatedAt() string { + if m == nil || m.UpdatedAt == nil { + return "" + } + return *m.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Migration) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (m *Milestone) GetClosedAt() time.Time { + if m == nil || m.ClosedAt == nil { + return time.Time{} + } + return *m.ClosedAt +} + +// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. +func (m *Milestone) GetClosedIssues() int { + if m == nil || m.ClosedIssues == nil { + return 0 + } + return *m.ClosedIssues +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (m *Milestone) GetCreatedAt() time.Time { + if m == nil || m.CreatedAt == nil { + return time.Time{} + } + return *m.CreatedAt +} + +// GetCreator returns the Creator field. +func (m *Milestone) GetCreator() *User { + if m == nil { + return nil + } + return m.Creator +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (m *Milestone) GetDescription() string { + if m == nil || m.Description == nil { + return "" + } + return *m.Description +} + +// GetDueOn returns the DueOn field if it's non-nil, zero value otherwise. +func (m *Milestone) GetDueOn() time.Time { + if m == nil || m.DueOn == nil { + return time.Time{} + } + return *m.DueOn +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (m *Milestone) GetHTMLURL() string { + if m == nil || m.HTMLURL == nil { + return "" + } + return *m.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *Milestone) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. +func (m *Milestone) GetLabelsURL() string { + if m == nil || m.LabelsURL == nil { + return "" + } + return *m.LabelsURL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (m *Milestone) GetNodeID() string { + if m == nil || m.NodeID == nil { + return "" + } + return *m.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (m *Milestone) GetNumber() int { + if m == nil || m.Number == nil { + return 0 + } + return *m.Number +} + +// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. +func (m *Milestone) GetOpenIssues() int { + if m == nil || m.OpenIssues == nil { + return 0 + } + return *m.OpenIssues +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *Milestone) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (m *Milestone) GetTitle() string { + if m == nil || m.Title == nil { + return "" + } + return *m.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (m *Milestone) GetUpdatedAt() time.Time { + if m == nil || m.UpdatedAt == nil { + return time.Time{} + } + return *m.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Milestone) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MilestoneEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetChanges returns the Changes field. +func (m *MilestoneEvent) GetChanges() *EditChange { + if m == nil { + return nil + } + return m.Changes +} + +// GetInstallation returns the Installation field. +func (m *MilestoneEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMilestone returns the Milestone field. +func (m *MilestoneEvent) GetMilestone() *Milestone { + if m == nil { + return nil + } + return m.Milestone +} + +// GetOrg returns the Org field. +func (m *MilestoneEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetRepo returns the Repo field. +func (m *MilestoneEvent) GetRepo() *Repository { + if m == nil { + return nil + } + return m.Repo +} + +// GetSender returns the Sender field. +func (m *MilestoneEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetClosedMilestones returns the ClosedMilestones field if it's non-nil, zero value otherwise. +func (m *MilestoneStats) GetClosedMilestones() int { + if m == nil || m.ClosedMilestones == nil { + return 0 + } + return *m.ClosedMilestones +} + +// GetOpenMilestones returns the OpenMilestones field if it's non-nil, zero value otherwise. +func (m *MilestoneStats) GetOpenMilestones() int { + if m == nil || m.OpenMilestones == nil { + return 0 + } + return *m.OpenMilestones +} + +// GetTotalMilestones returns the TotalMilestones field if it's non-nil, zero value otherwise. +func (m *MilestoneStats) GetTotalMilestones() int { + if m == nil || m.TotalMilestones == nil { + return 0 + } + return *m.TotalMilestones +} + +// GetBase returns the Base field if it's non-nil, zero value otherwise. +func (n *NewPullRequest) GetBase() string { + if n == nil || n.Base == nil { + return "" + } + return *n.Base +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (n *NewPullRequest) GetBody() string { + if n == nil || n.Body == nil { + return "" + } + return *n.Body +} + +// GetHead returns the Head field if it's non-nil, zero value otherwise. +func (n *NewPullRequest) GetHead() string { + if n == nil || n.Head == nil { + return "" + } + return *n.Head +} + +// GetIssue returns the Issue field if it's non-nil, zero value otherwise. +func (n *NewPullRequest) GetIssue() int { + if n == nil || n.Issue == nil { + return 0 + } + return *n.Issue +} + +// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. +func (n *NewPullRequest) GetMaintainerCanModify() bool { + if n == nil || n.MaintainerCanModify == nil { + return false + } + return *n.MaintainerCanModify +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (n *NewPullRequest) GetTitle() string { + if n == nil || n.Title == nil { + return "" + } + return *n.Title +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (n *NewTeam) GetDescription() string { + if n == nil || n.Description == nil { + return "" + } + return *n.Description +} + +// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. +func (n *NewTeam) GetLDAPDN() string { + if n == nil || n.LDAPDN == nil { + return "" + } + return *n.LDAPDN +} + +// GetParentTeamID returns the ParentTeamID field if it's non-nil, zero value otherwise. +func (n *NewTeam) GetParentTeamID() int64 { + if n == nil || n.ParentTeamID == nil { + return 0 + } + return *n.ParentTeamID +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (n *NewTeam) GetPermission() string { + if n == nil || n.Permission == nil { + return "" + } + return *n.Permission +} + +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (n *NewTeam) GetPrivacy() string { + if n == nil || n.Privacy == nil { + return "" + } + return *n.Privacy +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (n *Notification) GetID() string { + if n == nil || n.ID == nil { + return "" + } + return *n.ID +} + +// GetLastReadAt returns the LastReadAt field if it's non-nil, zero value otherwise. +func (n *Notification) GetLastReadAt() time.Time { + if n == nil || n.LastReadAt == nil { + return time.Time{} + } + return *n.LastReadAt +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (n *Notification) GetReason() string { + if n == nil || n.Reason == nil { + return "" + } + return *n.Reason +} + +// GetRepository returns the Repository field. +func (n *Notification) GetRepository() *Repository { + if n == nil { + return nil + } + return n.Repository +} + +// GetSubject returns the Subject field. +func (n *Notification) GetSubject() *NotificationSubject { + if n == nil { + return nil + } + return n.Subject +} + +// GetUnread returns the Unread field if it's non-nil, zero value otherwise. +func (n *Notification) GetUnread() bool { + if n == nil || n.Unread == nil { + return false + } + return *n.Unread +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (n *Notification) GetUpdatedAt() time.Time { + if n == nil || n.UpdatedAt == nil { + return time.Time{} + } + return *n.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (n *Notification) GetURL() string { + if n == nil || n.URL == nil { + return "" + } + return *n.URL +} + +// GetLatestCommentURL returns the LatestCommentURL field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetLatestCommentURL() string { + if n == nil || n.LatestCommentURL == nil { + return "" + } + return *n.LatestCommentURL +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetTitle() string { + if n == nil || n.Title == nil { + return "" + } + return *n.Title +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetType() string { + if n == nil || n.Type == nil { + return "" + } + return *n.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetURL() string { + if n == nil || n.URL == nil { + return "" + } + return *n.URL +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetAvatarURL() string { + if o == nil || o.AvatarURL == nil { + return "" + } + return *o.AvatarURL +} + +// GetBillingEmail returns the BillingEmail field if it's non-nil, zero value otherwise. +func (o *Organization) GetBillingEmail() string { + if o == nil || o.BillingEmail == nil { + return "" + } + return *o.BillingEmail +} + +// GetBlog returns the Blog field if it's non-nil, zero value otherwise. +func (o *Organization) GetBlog() string { + if o == nil || o.Blog == nil { + return "" + } + return *o.Blog +} + +// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. +func (o *Organization) GetCollaborators() int { + if o == nil || o.Collaborators == nil { + return 0 + } + return *o.Collaborators +} + +// GetCompany returns the Company field if it's non-nil, zero value otherwise. +func (o *Organization) GetCompany() string { + if o == nil || o.Company == nil { + return "" + } + return *o.Company +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (o *Organization) GetCreatedAt() time.Time { + if o == nil || o.CreatedAt == nil { + return time.Time{} + } + return *o.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (o *Organization) GetDescription() string { + if o == nil || o.Description == nil { + return "" + } + return *o.Description +} + +// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. +func (o *Organization) GetDiskUsage() int { + if o == nil || o.DiskUsage == nil { + return 0 + } + return *o.DiskUsage +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (o *Organization) GetEmail() string { + if o == nil || o.Email == nil { + return "" + } + return *o.Email +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetEventsURL() string { + if o == nil || o.EventsURL == nil { + return "" + } + return *o.EventsURL +} + +// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. +func (o *Organization) GetFollowers() int { + if o == nil || o.Followers == nil { + return 0 + } + return *o.Followers +} + +// GetFollowing returns the Following field if it's non-nil, zero value otherwise. +func (o *Organization) GetFollowing() int { + if o == nil || o.Following == nil { + return 0 + } + return *o.Following +} + +// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetHooksURL() string { + if o == nil || o.HooksURL == nil { + return "" + } + return *o.HooksURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetHTMLURL() string { + if o == nil || o.HTMLURL == nil { + return "" + } + return *o.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (o *Organization) GetID() int64 { + if o == nil || o.ID == nil { + return 0 + } + return *o.ID +} + +// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetIssuesURL() string { + if o == nil || o.IssuesURL == nil { + return "" + } + return *o.IssuesURL +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (o *Organization) GetLocation() string { + if o == nil || o.Location == nil { + return "" + } + return *o.Location +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (o *Organization) GetLogin() string { + if o == nil || o.Login == nil { + return "" + } + return *o.Login +} + +// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersURL() string { + if o == nil || o.MembersURL == nil { + return "" + } + return *o.MembersURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (o *Organization) GetName() string { + if o == nil || o.Name == nil { + return "" + } + return *o.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (o *Organization) GetNodeID() string { + if o == nil || o.NodeID == nil { + return "" + } + return *o.NodeID +} + +// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetOwnedPrivateRepos() int { + if o == nil || o.OwnedPrivateRepos == nil { + return 0 + } + return *o.OwnedPrivateRepos +} + +// GetPlan returns the Plan field. +func (o *Organization) GetPlan() *Plan { + if o == nil { + return nil + } + return o.Plan +} + +// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. +func (o *Organization) GetPrivateGists() int { + if o == nil || o.PrivateGists == nil { + return 0 + } + return *o.PrivateGists +} + +// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. +func (o *Organization) GetPublicGists() int { + if o == nil || o.PublicGists == nil { + return 0 + } + return *o.PublicGists +} + +// GetPublicMembersURL returns the PublicMembersURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetPublicMembersURL() string { + if o == nil || o.PublicMembersURL == nil { + return "" + } + return *o.PublicMembersURL +} + +// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetPublicRepos() int { + if o == nil || o.PublicRepos == nil { + return 0 + } + return *o.PublicRepos +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetReposURL() string { + if o == nil || o.ReposURL == nil { + return "" + } + return *o.ReposURL +} + +// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetTotalPrivateRepos() int { + if o == nil || o.TotalPrivateRepos == nil { + return 0 + } + return *o.TotalPrivateRepos +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (o *Organization) GetType() string { + if o == nil || o.Type == nil { + return "" + } + return *o.Type +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (o *Organization) GetUpdatedAt() time.Time { + if o == nil || o.UpdatedAt == nil { + return time.Time{} + } + return *o.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (o *Organization) GetURL() string { + if o == nil || o.URL == nil { + return "" + } + return *o.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (o *OrganizationEvent) GetAction() string { + if o == nil || o.Action == nil { + return "" + } + return *o.Action +} + +// GetInstallation returns the Installation field. +func (o *OrganizationEvent) GetInstallation() *Installation { + if o == nil { + return nil + } + return o.Installation +} + +// GetInvitation returns the Invitation field. +func (o *OrganizationEvent) GetInvitation() *Invitation { + if o == nil { + return nil + } + return o.Invitation +} + +// GetMembership returns the Membership field. +func (o *OrganizationEvent) GetMembership() *Membership { + if o == nil { + return nil + } + return o.Membership +} + +// GetOrganization returns the Organization field. +func (o *OrganizationEvent) GetOrganization() *Organization { + if o == nil { + return nil + } + return o.Organization +} + +// GetSender returns the Sender field. +func (o *OrganizationEvent) GetSender() *User { + if o == nil { + return nil + } + return o.Sender +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (o *OrgBlockEvent) GetAction() string { + if o == nil || o.Action == nil { + return "" + } + return *o.Action +} + +// GetBlockedUser returns the BlockedUser field. +func (o *OrgBlockEvent) GetBlockedUser() *User { + if o == nil { + return nil + } + return o.BlockedUser +} + +// GetInstallation returns the Installation field. +func (o *OrgBlockEvent) GetInstallation() *Installation { + if o == nil { + return nil + } + return o.Installation +} + +// GetOrganization returns the Organization field. +func (o *OrgBlockEvent) GetOrganization() *Organization { + if o == nil { + return nil + } + return o.Organization +} + +// GetSender returns the Sender field. +func (o *OrgBlockEvent) GetSender() *User { + if o == nil { + return nil + } + return o.Sender +} + +// GetDisabledOrgs returns the DisabledOrgs field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetDisabledOrgs() int { + if o == nil || o.DisabledOrgs == nil { + return 0 + } + return *o.DisabledOrgs +} + +// GetTotalOrgs returns the TotalOrgs field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetTotalOrgs() int { + if o == nil || o.TotalOrgs == nil { + return 0 + } + return *o.TotalOrgs +} + +// GetTotalTeamMembers returns the TotalTeamMembers field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetTotalTeamMembers() int { + if o == nil || o.TotalTeamMembers == nil { + return 0 + } + return *o.TotalTeamMembers +} + +// GetTotalTeams returns the TotalTeams field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetTotalTeams() int { + if o == nil || o.TotalTeams == nil { + return 0 + } + return *o.TotalTeams +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *Page) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *Page) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetPageName returns the PageName field if it's non-nil, zero value otherwise. +func (p *Page) GetPageName() string { + if p == nil || p.PageName == nil { + return "" + } + return *p.PageName +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (p *Page) GetSHA() string { + if p == nil || p.SHA == nil { + return "" + } + return *p.SHA +} + +// GetSummary returns the Summary field if it's non-nil, zero value otherwise. +func (p *Page) GetSummary() string { + if p == nil || p.Summary == nil { + return "" + } + return *p.Summary +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (p *Page) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +// GetBuild returns the Build field. +func (p *PageBuildEvent) GetBuild() *PagesBuild { + if p == nil { + return nil + } + return p.Build +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PageBuildEvent) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetInstallation returns the Installation field. +func (p *PageBuildEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetRepo returns the Repo field. +func (p *PageBuildEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PageBuildEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetCNAME returns the CNAME field if it's non-nil, zero value otherwise. +func (p *Pages) GetCNAME() string { + if p == nil || p.CNAME == nil { + return "" + } + return *p.CNAME +} + +// GetCustom404 returns the Custom404 field if it's non-nil, zero value otherwise. +func (p *Pages) GetCustom404() bool { + if p == nil || p.Custom404 == nil { + return false + } + return *p.Custom404 +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *Pages) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (p *Pages) GetStatus() string { + if p == nil || p.Status == nil { + return "" + } + return *p.Status +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *Pages) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetCommit returns the Commit field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetCommit() string { + if p == nil || p.Commit == nil { + return "" + } + return *p.Commit +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDuration returns the Duration field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetDuration() int { + if p == nil || p.Duration == nil { + return 0 + } + return *p.Duration +} + +// GetError returns the Error field. +func (p *PagesBuild) GetError() *PagesError { + if p == nil { + return nil + } + return p.Error +} + +// GetPusher returns the Pusher field. +func (p *PagesBuild) GetPusher() *User { + if p == nil { + return nil + } + return p.Pusher +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetStatus() string { + if p == nil || p.Status == nil { + return "" + } + return *p.Status +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (p *PagesError) GetMessage() string { + if p == nil || p.Message == nil { + return "" + } + return *p.Message +} + +// GetTotalPages returns the TotalPages field if it's non-nil, zero value otherwise. +func (p *PageStats) GetTotalPages() int { + if p == nil || p.TotalPages == nil { + return 0 + } + return *p.TotalPages +} + +// GetHook returns the Hook field. +func (p *PingEvent) GetHook() *Hook { + if p == nil { + return nil + } + return p.Hook +} + +// GetHookID returns the HookID field if it's non-nil, zero value otherwise. +func (p *PingEvent) GetHookID() int64 { + if p == nil || p.HookID == nil { + return 0 + } + return *p.HookID +} + +// GetInstallation returns the Installation field. +func (p *PingEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetZen returns the Zen field if it's non-nil, zero value otherwise. +func (p *PingEvent) GetZen() string { + if p == nil || p.Zen == nil { + return "" + } + return *p.Zen +} + +// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. +func (p *Plan) GetCollaborators() int { + if p == nil || p.Collaborators == nil { + return 0 + } + return *p.Collaborators +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *Plan) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetPrivateRepos returns the PrivateRepos field if it's non-nil, zero value otherwise. +func (p *Plan) GetPrivateRepos() int { + if p == nil || p.PrivateRepos == nil { + return 0 + } + return *p.PrivateRepos +} + +// GetSpace returns the Space field if it's non-nil, zero value otherwise. +func (p *Plan) GetSpace() int { + if p == nil || p.Space == nil { + return 0 + } + return *p.Space +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *Project) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *Project) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetCreator returns the Creator field. +func (p *Project) GetCreator() *User { + if p == nil { + return nil + } + return p.Creator +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *Project) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *Project) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *Project) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetOwnerURL returns the OwnerURL field if it's non-nil, zero value otherwise. +func (p *Project) GetOwnerURL() string { + if p == nil || p.OwnerURL == nil { + return "" + } + return *p.OwnerURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *Project) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *Project) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetColumnID returns the ColumnID field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetColumnID() int64 { + if p == nil || p.ColumnID == nil { + return 0 + } + return *p.ColumnID +} + +// GetColumnURL returns the ColumnURL field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetColumnURL() string { + if p == nil || p.ColumnURL == nil { + return "" + } + return *p.ColumnURL +} + +// GetContentURL returns the ContentURL field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetContentURL() string { + if p == nil || p.ContentURL == nil { + return "" + } + return *p.ContentURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetCreator returns the Creator field. +func (p *ProjectCard) GetCreator() *User { + if p == nil { + return nil + } + return p.Creator +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetNote() string { + if p == nil || p.Note == nil { + return "" + } + return *p.Note +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *ProjectCard) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *ProjectCardEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetAfterID returns the AfterID field if it's non-nil, zero value otherwise. +func (p *ProjectCardEvent) GetAfterID() int64 { + if p == nil || p.AfterID == nil { + return 0 + } + return *p.AfterID +} + +// GetChanges returns the Changes field. +func (p *ProjectCardEvent) GetChanges() *ProjectCardChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetInstallation returns the Installation field. +func (p *ProjectCardEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *ProjectCardEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetProjectCard returns the ProjectCard field. +func (p *ProjectCardEvent) GetProjectCard() *ProjectCard { + if p == nil { + return nil + } + return p.ProjectCard +} + +// GetRepo returns the Repo field. +func (p *ProjectCardEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *ProjectCardEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectColumn) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectColumn) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *ProjectColumn) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetProjectURL returns the ProjectURL field if it's non-nil, zero value otherwise. +func (p *ProjectColumn) GetProjectURL() string { + if p == nil || p.ProjectURL == nil { + return "" + } + return *p.ProjectURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectColumn) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *ProjectColumnEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetAfterID returns the AfterID field if it's non-nil, zero value otherwise. +func (p *ProjectColumnEvent) GetAfterID() int64 { + if p == nil || p.AfterID == nil { + return 0 + } + return *p.AfterID +} + +// GetChanges returns the Changes field. +func (p *ProjectColumnEvent) GetChanges() *ProjectColumnChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetInstallation returns the Installation field. +func (p *ProjectColumnEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *ProjectColumnEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetProjectColumn returns the ProjectColumn field. +func (p *ProjectColumnEvent) GetProjectColumn() *ProjectColumn { + if p == nil { + return nil + } + return p.ProjectColumn +} + +// GetRepo returns the Repo field. +func (p *ProjectColumnEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *ProjectColumnEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *ProjectEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetChanges returns the Changes field. +func (p *ProjectEvent) GetChanges() *ProjectChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetInstallation returns the Installation field. +func (p *ProjectEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *ProjectEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetProject returns the Project field. +func (p *ProjectEvent) GetProject() *Project { + if p == nil { + return nil + } + return p.Project +} + +// GetRepo returns the Repo field. +func (p *ProjectEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *ProjectEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetEnforceAdmins returns the EnforceAdmins field. +func (p *Protection) GetEnforceAdmins() *AdminEnforcement { + if p == nil { + return nil + } + return p.EnforceAdmins +} + +// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. +func (p *Protection) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcement { + if p == nil { + return nil + } + return p.RequiredPullRequestReviews +} + +// GetRequiredStatusChecks returns the RequiredStatusChecks field. +func (p *Protection) GetRequiredStatusChecks() *RequiredStatusChecks { + if p == nil { + return nil + } + return p.RequiredStatusChecks +} + +// GetRestrictions returns the Restrictions field. +func (p *Protection) GetRestrictions() *BranchRestrictions { + if p == nil { + return nil + } + return p.Restrictions +} + +// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. +func (p *ProtectionRequest) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcementRequest { + if p == nil { + return nil + } + return p.RequiredPullRequestReviews +} + +// GetRequiredStatusChecks returns the RequiredStatusChecks field. +func (p *ProtectionRequest) GetRequiredStatusChecks() *RequiredStatusChecks { + if p == nil { + return nil + } + return p.RequiredStatusChecks +} + +// GetRestrictions returns the Restrictions field. +func (p *ProtectionRequest) GetRestrictions() *BranchRestrictionsRequest { + if p == nil { + return nil + } + return p.Restrictions +} + +// GetInstallation returns the Installation field. +func (p *PublicEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetRepo returns the Repo field. +func (p *PublicEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PublicEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetAdditions() int { + if p == nil || p.Additions == nil { + return 0 + } + return *p.Additions +} + +// GetAssignee returns the Assignee field. +func (p *PullRequest) GetAssignee() *User { + if p == nil { + return nil + } + return p.Assignee +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetAuthorAssociation() string { + if p == nil || p.AuthorAssociation == nil { + return "" + } + return *p.AuthorAssociation +} + +// GetBase returns the Base field. +func (p *PullRequest) GetBase() *PullRequestBranch { + if p == nil { + return nil + } + return p.Base +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetChangedFiles returns the ChangedFiles field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetChangedFiles() int { + if p == nil || p.ChangedFiles == nil { + return 0 + } + return *p.ChangedFiles +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetClosedAt() time.Time { + if p == nil || p.ClosedAt == nil { + return time.Time{} + } + return *p.ClosedAt +} + +// GetComments returns the Comments field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetComments() int { + if p == nil || p.Comments == nil { + return 0 + } + return *p.Comments +} + +// GetCommits returns the Commits field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetCommits() int { + if p == nil || p.Commits == nil { + return 0 + } + return *p.Commits +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetCreatedAt() time.Time { + if p == nil || p.CreatedAt == nil { + return time.Time{} + } + return *p.CreatedAt +} + +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetDeletions() int { + if p == nil || p.Deletions == nil { + return 0 + } + return *p.Deletions +} + +// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetDiffURL() string { + if p == nil || p.DiffURL == nil { + return "" + } + return *p.DiffURL +} + +// GetHead returns the Head field. +func (p *PullRequest) GetHead() *PullRequestBranch { + if p == nil { + return nil + } + return p.Head +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetIssueURL() string { + if p == nil || p.IssueURL == nil { + return "" + } + return *p.IssueURL +} + +// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMaintainerCanModify() bool { + if p == nil || p.MaintainerCanModify == nil { + return false + } + return *p.MaintainerCanModify +} + +// GetMergeable returns the Mergeable field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergeable() bool { + if p == nil || p.Mergeable == nil { + return false + } + return *p.Mergeable +} + +// GetMergeableState returns the MergeableState field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergeableState() string { + if p == nil || p.MergeableState == nil { + return "" + } + return *p.MergeableState +} + +// GetMergeCommitSHA returns the MergeCommitSHA field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergeCommitSHA() string { + if p == nil || p.MergeCommitSHA == nil { + return "" + } + return *p.MergeCommitSHA +} + +// GetMerged returns the Merged field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMerged() bool { + if p == nil || p.Merged == nil { + return false + } + return *p.Merged +} + +// GetMergedAt returns the MergedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergedAt() time.Time { + if p == nil || p.MergedAt == nil { + return time.Time{} + } + return *p.MergedAt +} + +// GetMergedBy returns the MergedBy field. +func (p *PullRequest) GetMergedBy() *User { + if p == nil { + return nil + } + return p.MergedBy +} + +// GetMilestone returns the Milestone field. +func (p *PullRequest) GetMilestone() *Milestone { + if p == nil { + return nil + } + return p.Milestone +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetPatchURL() string { + if p == nil || p.PatchURL == nil { + return "" + } + return *p.PatchURL +} + +// GetReviewCommentsURL returns the ReviewCommentsURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetReviewCommentsURL() string { + if p == nil || p.ReviewCommentsURL == nil { + return "" + } + return *p.ReviewCommentsURL +} + +// GetReviewCommentURL returns the ReviewCommentURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetReviewCommentURL() string { + if p == nil || p.ReviewCommentURL == nil { + return "" + } + return *p.ReviewCommentURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetStatusesURL() string { + if p == nil || p.StatusesURL == nil { + return "" + } + return *p.StatusesURL +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetUpdatedAt() time.Time { + if p == nil || p.UpdatedAt == nil { + return time.Time{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetUser returns the User field. +func (p *PullRequest) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetLabel returns the Label field if it's non-nil, zero value otherwise. +func (p *PullRequestBranch) GetLabel() string { + if p == nil || p.Label == nil { + return "" + } + return *p.Label +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (p *PullRequestBranch) GetRef() string { + if p == nil || p.Ref == nil { + return "" + } + return *p.Ref +} + +// GetRepo returns the Repo field. +func (p *PullRequestBranch) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (p *PullRequestBranch) GetSHA() string { + if p == nil || p.SHA == nil { + return "" + } + return *p.SHA +} + +// GetUser returns the User field. +func (p *PullRequestBranch) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetCommitID() string { + if p == nil || p.CommitID == nil { + return "" + } + return *p.CommitID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetCreatedAt() time.Time { + if p == nil || p.CreatedAt == nil { + return time.Time{} + } + return *p.CreatedAt +} + +// GetDiffHunk returns the DiffHunk field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetDiffHunk() string { + if p == nil || p.DiffHunk == nil { + return "" + } + return *p.DiffHunk +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetInReplyTo returns the InReplyTo field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetInReplyTo() int64 { + if p == nil || p.InReplyTo == nil { + return 0 + } + return *p.InReplyTo +} + +// GetOriginalCommitID returns the OriginalCommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetOriginalCommitID() string { + if p == nil || p.OriginalCommitID == nil { + return "" + } + return *p.OriginalCommitID +} + +// GetOriginalPosition returns the OriginalPosition field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetOriginalPosition() int { + if p == nil || p.OriginalPosition == nil { + return 0 + } + return *p.OriginalPosition +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetPath() string { + if p == nil || p.Path == nil { + return "" + } + return *p.Path +} + +// GetPosition returns the Position field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetPosition() int { + if p == nil || p.Position == nil { + return 0 + } + return *p.Position +} + +// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetPullRequestURL() string { + if p == nil || p.PullRequestURL == nil { + return "" + } + return *p.PullRequestURL +} + +// GetReactions returns the Reactions field. +func (p *PullRequestComment) GetReactions() *Reactions { + if p == nil { + return nil + } + return p.Reactions +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetUpdatedAt() time.Time { + if p == nil || p.UpdatedAt == nil { + return time.Time{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetUser returns the User field. +func (p *PullRequestComment) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetChanges returns the Changes field. +func (p *PullRequestEvent) GetChanges() *EditChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetInstallation returns the Installation field. +func (p *PullRequestEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *PullRequestEvent) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetRepo returns the Repo field. +func (p *PullRequestEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PullRequestEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetDiffURL() string { + if p == nil || p.DiffURL == nil { + return "" + } + return *p.DiffURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetPatchURL() string { + if p == nil || p.PatchURL == nil { + return "" + } + return *p.PatchURL +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetMerged returns the Merged field if it's non-nil, zero value otherwise. +func (p *PullRequestMergeResult) GetMerged() bool { + if p == nil || p.Merged == nil { + return false + } + return *p.Merged +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (p *PullRequestMergeResult) GetMessage() string { + if p == nil || p.Message == nil { + return "" + } + return *p.Message +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (p *PullRequestMergeResult) GetSHA() string { + if p == nil || p.SHA == nil { + return "" + } + return *p.SHA +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetCommitID() string { + if p == nil || p.CommitID == nil { + return "" + } + return *p.CommitID +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetPullRequestURL() string { + if p == nil || p.PullRequestURL == nil { + return "" + } + return *p.PullRequestURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetSubmittedAt() time.Time { + if p == nil || p.SubmittedAt == nil { + return time.Time{} + } + return *p.SubmittedAt +} + +// GetUser returns the User field. +func (p *PullRequestReview) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewCommentEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetChanges returns the Changes field. +func (p *PullRequestReviewCommentEvent) GetChanges() *EditChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetComment returns the Comment field. +func (p *PullRequestReviewCommentEvent) GetComment() *PullRequestComment { + if p == nil { + return nil + } + return p.Comment +} + +// GetInstallation returns the Installation field. +func (p *PullRequestReviewCommentEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestReviewCommentEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetRepo returns the Repo field. +func (p *PullRequestReviewCommentEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PullRequestReviewCommentEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewDismissalRequest) GetMessage() string { + if p == nil || p.Message == nil { + return "" + } + return *p.Message +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetInstallation returns the Installation field. +func (p *PullRequestReviewEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrganization returns the Organization field. +func (p *PullRequestReviewEvent) GetOrganization() *Organization { + if p == nil { + return nil + } + return p.Organization +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestReviewEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetRepo returns the Repo field. +func (p *PullRequestReviewEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetReview returns the Review field. +func (p *PullRequestReviewEvent) GetReview() *PullRequestReview { + if p == nil { + return nil + } + return p.Review +} + +// GetSender returns the Sender field. +func (p *PullRequestReviewEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewRequest) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewRequest) GetCommitID() string { + if p == nil || p.CommitID == nil { + return "" + } + return *p.CommitID +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewRequest) GetEvent() string { + if p == nil || p.Event == nil { + return "" + } + return *p.Event +} + +// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. +func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { + if p == nil { + return nil + } + return p.DismissalRestrictionsRequest +} + +// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. +func (p *PullRequestReviewsEnforcementUpdate) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { + if p == nil { + return nil + } + return p.DismissalRestrictionsRequest +} + +// GetDismissStaleReviews returns the DismissStaleReviews field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcementUpdate) GetDismissStaleReviews() bool { + if p == nil || p.DismissStaleReviews == nil { + return false + } + return *p.DismissStaleReviews +} + +// GetMergablePulls returns the MergablePulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetMergablePulls() int { + if p == nil || p.MergablePulls == nil { + return 0 + } + return *p.MergablePulls +} + +// GetMergedPulls returns the MergedPulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetMergedPulls() int { + if p == nil || p.MergedPulls == nil { + return 0 + } + return *p.MergedPulls +} + +// GetTotalPulls returns the TotalPulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetTotalPulls() int { + if p == nil || p.TotalPulls == nil { + return 0 + } + return *p.TotalPulls +} + +// GetUnmergablePulls returns the UnmergablePulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetUnmergablePulls() int { + if p == nil || p.UnmergablePulls == nil { + return 0 + } + return *p.UnmergablePulls +} + +// GetCommits returns the Commits field if it's non-nil, zero value otherwise. +func (p *PunchCard) GetCommits() int { + if p == nil || p.Commits == nil { + return 0 + } + return *p.Commits +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (p *PunchCard) GetDay() int { + if p == nil || p.Day == nil { + return 0 + } + return *p.Day +} + +// GetHour returns the Hour field if it's non-nil, zero value otherwise. +func (p *PunchCard) GetHour() int { + if p == nil || p.Hour == nil { + return 0 + } + return *p.Hour +} + +// GetAfter returns the After field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetAfter() string { + if p == nil || p.After == nil { + return "" + } + return *p.After +} + +// GetBaseRef returns the BaseRef field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetBaseRef() string { + if p == nil || p.BaseRef == nil { + return "" + } + return *p.BaseRef +} + +// GetBefore returns the Before field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetBefore() string { + if p == nil || p.Before == nil { + return "" + } + return *p.Before +} + +// GetCompare returns the Compare field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetCompare() string { + if p == nil || p.Compare == nil { + return "" + } + return *p.Compare +} + +// GetCreated returns the Created field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetCreated() bool { + if p == nil || p.Created == nil { + return false + } + return *p.Created +} + +// GetDeleted returns the Deleted field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetDeleted() bool { + if p == nil || p.Deleted == nil { + return false + } + return *p.Deleted +} + +// GetDistinctSize returns the DistinctSize field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetDistinctSize() int { + if p == nil || p.DistinctSize == nil { + return 0 + } + return *p.DistinctSize +} + +// GetForced returns the Forced field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetForced() bool { + if p == nil || p.Forced == nil { + return false + } + return *p.Forced +} + +// GetHead returns the Head field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetHead() string { + if p == nil || p.Head == nil { + return "" + } + return *p.Head +} + +// GetHeadCommit returns the HeadCommit field. +func (p *PushEvent) GetHeadCommit() *PushEventCommit { + if p == nil { + return nil + } + return p.HeadCommit +} + +// GetInstallation returns the Installation field. +func (p *PushEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetPusher returns the Pusher field. +func (p *PushEvent) GetPusher() *User { + if p == nil { + return nil + } + return p.Pusher +} + +// GetPushID returns the PushID field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetPushID() int64 { + if p == nil || p.PushID == nil { + return 0 + } + return *p.PushID +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetRef() string { + if p == nil || p.Ref == nil { + return "" + } + return *p.Ref +} + +// GetRepo returns the Repo field. +func (p *PushEvent) GetRepo() *PushEventRepository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PushEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetSize() int { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetAuthor returns the Author field. +func (p *PushEventCommit) GetAuthor() *CommitAuthor { + if p == nil { + return nil + } + return p.Author +} + +// GetCommitter returns the Committer field. +func (p *PushEventCommit) GetCommitter() *CommitAuthor { + if p == nil { + return nil + } + return p.Committer +} + +// GetDistinct returns the Distinct field if it's non-nil, zero value otherwise. +func (p *PushEventCommit) GetDistinct() bool { + if p == nil || p.Distinct == nil { + return false + } + return *p.Distinct +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PushEventCommit) GetID() string { + if p == nil || p.ID == nil { + return "" + } + return *p.ID +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (p *PushEventCommit) GetMessage() string { + if p == nil || p.Message == nil { + return "" + } + return *p.Message +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (p *PushEventCommit) GetSHA() string { + if p == nil || p.SHA == nil { + return "" + } + return *p.SHA +} + +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (p *PushEventCommit) GetTimestamp() Timestamp { + if p == nil || p.Timestamp == nil { + return Timestamp{} + } + return *p.Timestamp +} + +// GetTreeID returns the TreeID field if it's non-nil, zero value otherwise. +func (p *PushEventCommit) GetTreeID() string { + if p == nil || p.TreeID == nil { + return "" + } + return *p.TreeID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PushEventCommit) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (p *PushEventRepoOwner) GetEmail() string { + if p == nil || p.Email == nil { + return "" + } + return *p.Email +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PushEventRepoOwner) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetArchiveURL() string { + if p == nil || p.ArchiveURL == nil { + return "" + } + return *p.ArchiveURL +} + +// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetCloneURL() string { + if p == nil || p.CloneURL == nil { + return "" + } + return *p.CloneURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetDefaultBranch() string { + if p == nil || p.DefaultBranch == nil { + return "" + } + return *p.DefaultBranch +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetFork returns the Fork field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetFork() bool { + if p == nil || p.Fork == nil { + return false + } + return *p.Fork +} + +// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetForksCount() int { + if p == nil || p.ForksCount == nil { + return 0 + } + return *p.ForksCount +} + +// GetFullName returns the FullName field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetFullName() string { + if p == nil || p.FullName == nil { + return "" + } + return *p.FullName +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetGitURL() string { + if p == nil || p.GitURL == nil { + return "" + } + return *p.GitURL +} + +// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasDownloads() bool { + if p == nil || p.HasDownloads == nil { + return false + } + return *p.HasDownloads +} + +// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasIssues() bool { + if p == nil || p.HasIssues == nil { + return false + } + return *p.HasIssues +} + +// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasPages() bool { + if p == nil || p.HasPages == nil { + return false + } + return *p.HasPages +} + +// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasWiki() bool { + if p == nil || p.HasWiki == nil { + return false + } + return *p.HasWiki +} + +// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHomepage() string { + if p == nil || p.Homepage == nil { + return "" + } + return *p.Homepage +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetLanguage() string { + if p == nil || p.Language == nil { + return "" + } + return *p.Language +} + +// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetMasterBranch() string { + if p == nil || p.MasterBranch == nil { + return "" + } + return *p.MasterBranch +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetOpenIssuesCount() int { + if p == nil || p.OpenIssuesCount == nil { + return 0 + } + return *p.OpenIssuesCount +} + +// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetOrganization() string { + if p == nil || p.Organization == nil { + return "" + } + return *p.Organization +} + +// GetOwner returns the Owner field. +func (p *PushEventRepository) GetOwner() *PushEventRepoOwner { + if p == nil { + return nil + } + return p.Owner +} + +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetPrivate() bool { + if p == nil || p.Private == nil { + return false + } + return *p.Private +} + +// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetPushedAt() Timestamp { + if p == nil || p.PushedAt == nil { + return Timestamp{} + } + return *p.PushedAt +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetSize() int { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetSSHURL() string { + if p == nil || p.SSHURL == nil { + return "" + } + return *p.SSHURL +} + +// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetStargazersCount() int { + if p == nil || p.StargazersCount == nil { + return 0 + } + return *p.StargazersCount +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetStatusesURL() string { + if p == nil || p.StatusesURL == nil { + return "" + } + return *p.StatusesURL +} + +// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetSVNURL() string { + if p == nil || p.SVNURL == nil { + return "" + } + return *p.SVNURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetWatchersCount() int { + if p == nil || p.WatchersCount == nil { + return 0 + } + return *p.WatchersCount +} + +// GetCore returns the Core field. +func (r *RateLimits) GetCore() *Rate { + if r == nil { + return nil + } + return r.Core +} + +// GetSearch returns the Search field. +func (r *RateLimits) GetSearch() *Rate { + if r == nil { + return nil + } + return r.Search +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (r *Reaction) GetContent() string { + if r == nil || r.Content == nil { + return "" + } + return *r.Content +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *Reaction) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetUser returns the User field. +func (r *Reaction) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + +// GetConfused returns the Confused field if it's non-nil, zero value otherwise. +func (r *Reactions) GetConfused() int { + if r == nil || r.Confused == nil { + return 0 + } + return *r.Confused +} + +// GetHeart returns the Heart field if it's non-nil, zero value otherwise. +func (r *Reactions) GetHeart() int { + if r == nil || r.Heart == nil { + return 0 + } + return *r.Heart +} + +// GetHooray returns the Hooray field if it's non-nil, zero value otherwise. +func (r *Reactions) GetHooray() int { + if r == nil || r.Hooray == nil { + return 0 + } + return *r.Hooray +} + +// GetLaugh returns the Laugh field if it's non-nil, zero value otherwise. +func (r *Reactions) GetLaugh() int { + if r == nil || r.Laugh == nil { + return 0 + } + return *r.Laugh +} + +// GetMinusOne returns the MinusOne field if it's non-nil, zero value otherwise. +func (r *Reactions) GetMinusOne() int { + if r == nil || r.MinusOne == nil { + return 0 + } + return *r.MinusOne +} + +// GetPlusOne returns the PlusOne field if it's non-nil, zero value otherwise. +func (r *Reactions) GetPlusOne() int { + if r == nil || r.PlusOne == nil { + return 0 + } + return *r.PlusOne +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (r *Reactions) GetTotalCount() int { + if r == nil || r.TotalCount == nil { + return 0 + } + return *r.TotalCount +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *Reactions) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *Reference) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetObject returns the Object field. +func (r *Reference) GetObject() *GitObject { + if r == nil { + return nil + } + return r.Object +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (r *Reference) GetRef() string { + if r == nil || r.Ref == nil { + return "" + } + return *r.Ref +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *Reference) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetBrowserDownloadURL returns the BrowserDownloadURL field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetBrowserDownloadURL() string { + if r == nil || r.BrowserDownloadURL == nil { + return "" + } + return *r.BrowserDownloadURL +} + +// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetContentType() string { + if r == nil || r.ContentType == nil { + return "" + } + return *r.ContentType +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetDownloadCount returns the DownloadCount field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetDownloadCount() int { + if r == nil || r.DownloadCount == nil { + return 0 + } + return *r.DownloadCount +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetLabel returns the Label field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetLabel() string { + if r == nil || r.Label == nil { + return "" + } + return *r.Label +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetState() string { + if r == nil || r.State == nil { + return "" + } + return *r.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetUploader returns the Uploader field. +func (r *ReleaseAsset) GetUploader() *User { + if r == nil { + return nil + } + return r.Uploader +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *ReleaseEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetInstallation returns the Installation field. +func (r *ReleaseEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetRelease returns the Release field. +func (r *ReleaseEvent) GetRelease() *RepositoryRelease { + if r == nil { + return nil + } + return r.Release +} + +// GetRepo returns the Repo field. +func (r *ReleaseEvent) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetSender returns the Sender field. +func (r *ReleaseEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *Rename) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetTo returns the To field if it's non-nil, zero value otherwise. +func (r *Rename) GetTo() string { + if r == nil || r.To == nil { + return "" + } + return *r.To +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (r *RepositoriesSearchResult) GetIncompleteResults() bool { + if r == nil || r.IncompleteResults == nil { + return false + } + return *r.IncompleteResults +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (r *RepositoriesSearchResult) GetTotal() int { + if r == nil || r.Total == nil { + return 0 + } + return *r.Total +} + +// GetAllowMergeCommit returns the AllowMergeCommit field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowMergeCommit() bool { + if r == nil || r.AllowMergeCommit == nil { + return false + } + return *r.AllowMergeCommit +} + +// GetAllowRebaseMerge returns the AllowRebaseMerge field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowRebaseMerge() bool { + if r == nil || r.AllowRebaseMerge == nil { + return false + } + return *r.AllowRebaseMerge +} + +// GetAllowSquashMerge returns the AllowSquashMerge field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowSquashMerge() bool { + if r == nil || r.AllowSquashMerge == nil { + return false + } + return *r.AllowSquashMerge +} + +// GetArchived returns the Archived field if it's non-nil, zero value otherwise. +func (r *Repository) GetArchived() bool { + if r == nil || r.Archived == nil { + return false + } + return *r.Archived +} + +// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetArchiveURL() string { + if r == nil || r.ArchiveURL == nil { + return "" + } + return *r.ArchiveURL +} + +// GetAssigneesURL returns the AssigneesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetAssigneesURL() string { + if r == nil || r.AssigneesURL == nil { + return "" + } + return *r.AssigneesURL +} + +// GetAutoInit returns the AutoInit field if it's non-nil, zero value otherwise. +func (r *Repository) GetAutoInit() bool { + if r == nil || r.AutoInit == nil { + return false + } + return *r.AutoInit +} + +// GetBlobsURL returns the BlobsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetBlobsURL() string { + if r == nil || r.BlobsURL == nil { + return "" + } + return *r.BlobsURL +} + +// GetBranchesURL returns the BranchesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetBranchesURL() string { + if r == nil || r.BranchesURL == nil { + return "" + } + return *r.BranchesURL +} + +// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCloneURL() string { + if r == nil || r.CloneURL == nil { + return "" + } + return *r.CloneURL +} + +// GetCodeOfConduct returns the CodeOfConduct field. +func (r *Repository) GetCodeOfConduct() *CodeOfConduct { + if r == nil { + return nil + } + return r.CodeOfConduct +} + +// GetCollaboratorsURL returns the CollaboratorsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCollaboratorsURL() string { + if r == nil || r.CollaboratorsURL == nil { + return "" + } + return *r.CollaboratorsURL +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCommentsURL() string { + if r == nil || r.CommentsURL == nil { + return "" + } + return *r.CommentsURL +} + +// GetCommitsURL returns the CommitsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCommitsURL() string { + if r == nil || r.CommitsURL == nil { + return "" + } + return *r.CommitsURL +} + +// GetCompareURL returns the CompareURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCompareURL() string { + if r == nil || r.CompareURL == nil { + return "" + } + return *r.CompareURL +} + +// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetContentsURL() string { + if r == nil || r.ContentsURL == nil { + return "" + } + return *r.ContentsURL +} + +// GetContributorsURL returns the ContributorsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetContributorsURL() string { + if r == nil || r.ContributorsURL == nil { + return "" + } + return *r.ContributorsURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *Repository) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. +func (r *Repository) GetDefaultBranch() string { + if r == nil || r.DefaultBranch == nil { + return "" + } + return *r.DefaultBranch +} + +// GetDeploymentsURL returns the DeploymentsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetDeploymentsURL() string { + if r == nil || r.DeploymentsURL == nil { + return "" + } + return *r.DeploymentsURL +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (r *Repository) GetDescription() string { + if r == nil || r.Description == nil { + return "" + } + return *r.Description +} + +// GetDownloadsURL returns the DownloadsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetDownloadsURL() string { + if r == nil || r.DownloadsURL == nil { + return "" + } + return *r.DownloadsURL +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetEventsURL() string { + if r == nil || r.EventsURL == nil { + return "" + } + return *r.EventsURL +} + +// GetFork returns the Fork field if it's non-nil, zero value otherwise. +func (r *Repository) GetFork() bool { + if r == nil || r.Fork == nil { + return false + } + return *r.Fork +} + +// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetForksCount() int { + if r == nil || r.ForksCount == nil { + return 0 + } + return *r.ForksCount +} + +// GetForksURL returns the ForksURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetForksURL() string { + if r == nil || r.ForksURL == nil { + return "" + } + return *r.ForksURL +} + +// GetFullName returns the FullName field if it's non-nil, zero value otherwise. +func (r *Repository) GetFullName() string { + if r == nil || r.FullName == nil { + return "" + } + return *r.FullName +} + +// GetGitCommitsURL returns the GitCommitsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitCommitsURL() string { + if r == nil || r.GitCommitsURL == nil { + return "" + } + return *r.GitCommitsURL +} + +// GetGitignoreTemplate returns the GitignoreTemplate field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitignoreTemplate() string { + if r == nil || r.GitignoreTemplate == nil { + return "" + } + return *r.GitignoreTemplate +} + +// GetGitRefsURL returns the GitRefsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitRefsURL() string { + if r == nil || r.GitRefsURL == nil { + return "" + } + return *r.GitRefsURL +} + +// GetGitTagsURL returns the GitTagsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitTagsURL() string { + if r == nil || r.GitTagsURL == nil { + return "" + } + return *r.GitTagsURL +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitURL() string { + if r == nil || r.GitURL == nil { + return "" + } + return *r.GitURL +} + +// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasDownloads() bool { + if r == nil || r.HasDownloads == nil { + return false + } + return *r.HasDownloads +} + +// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasIssues() bool { + if r == nil || r.HasIssues == nil { + return false + } + return *r.HasIssues +} + +// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasPages() bool { + if r == nil || r.HasPages == nil { + return false + } + return *r.HasPages +} + +// GetHasProjects returns the HasProjects field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasProjects() bool { + if r == nil || r.HasProjects == nil { + return false + } + return *r.HasProjects +} + +// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasWiki() bool { + if r == nil || r.HasWiki == nil { + return false + } + return *r.HasWiki +} + +// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. +func (r *Repository) GetHomepage() string { + if r == nil || r.Homepage == nil { + return "" + } + return *r.Homepage +} + +// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetHooksURL() string { + if r == nil || r.HooksURL == nil { + return "" + } + return *r.HooksURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *Repository) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetIssueCommentURL returns the IssueCommentURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetIssueCommentURL() string { + if r == nil || r.IssueCommentURL == nil { + return "" + } + return *r.IssueCommentURL +} + +// GetIssueEventsURL returns the IssueEventsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetIssueEventsURL() string { + if r == nil || r.IssueEventsURL == nil { + return "" + } + return *r.IssueEventsURL +} + +// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetIssuesURL() string { + if r == nil || r.IssuesURL == nil { + return "" + } + return *r.IssuesURL +} + +// GetKeysURL returns the KeysURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetKeysURL() string { + if r == nil || r.KeysURL == nil { + return "" + } + return *r.KeysURL +} + +// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetLabelsURL() string { + if r == nil || r.LabelsURL == nil { + return "" + } + return *r.LabelsURL +} + +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (r *Repository) GetLanguage() string { + if r == nil || r.Language == nil { + return "" + } + return *r.Language +} + +// GetLanguagesURL returns the LanguagesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetLanguagesURL() string { + if r == nil || r.LanguagesURL == nil { + return "" + } + return *r.LanguagesURL +} + +// GetLicense returns the License field. +func (r *Repository) GetLicense() *License { + if r == nil { + return nil + } + return r.License +} + +// GetLicenseTemplate returns the LicenseTemplate field if it's non-nil, zero value otherwise. +func (r *Repository) GetLicenseTemplate() string { + if r == nil || r.LicenseTemplate == nil { + return "" + } + return *r.LicenseTemplate +} + +// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. +func (r *Repository) GetMasterBranch() string { + if r == nil || r.MasterBranch == nil { + return "" + } + return *r.MasterBranch +} + +// GetMergesURL returns the MergesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetMergesURL() string { + if r == nil || r.MergesURL == nil { + return "" + } + return *r.MergesURL +} + +// GetMilestonesURL returns the MilestonesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetMilestonesURL() string { + if r == nil || r.MilestonesURL == nil { + return "" + } + return *r.MilestonesURL +} + +// GetMirrorURL returns the MirrorURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetMirrorURL() string { + if r == nil || r.MirrorURL == nil { + return "" + } + return *r.MirrorURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *Repository) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetNetworkCount returns the NetworkCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetNetworkCount() int { + if r == nil || r.NetworkCount == nil { + return 0 + } + return *r.NetworkCount +} + +// GetNotificationsURL returns the NotificationsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetNotificationsURL() string { + if r == nil || r.NotificationsURL == nil { + return "" + } + return *r.NotificationsURL +} + +// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetOpenIssuesCount() int { + if r == nil || r.OpenIssuesCount == nil { + return 0 + } + return *r.OpenIssuesCount +} + +// GetOrganization returns the Organization field. +func (r *Repository) GetOrganization() *Organization { + if r == nil { + return nil + } + return r.Organization +} + +// GetOwner returns the Owner field. +func (r *Repository) GetOwner() *User { + if r == nil { + return nil + } + return r.Owner +} + +// GetParent returns the Parent field. +func (r *Repository) GetParent() *Repository { + if r == nil { + return nil + } + return r.Parent +} + +// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. +func (r *Repository) GetPermissions() map[string]bool { + if r == nil || r.Permissions == nil { + return map[string]bool{} + } + return *r.Permissions +} + +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (r *Repository) GetPrivate() bool { + if r == nil || r.Private == nil { + return false + } + return *r.Private +} + +// GetPullsURL returns the PullsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetPullsURL() string { + if r == nil || r.PullsURL == nil { + return "" + } + return *r.PullsURL +} + +// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. +func (r *Repository) GetPushedAt() Timestamp { + if r == nil || r.PushedAt == nil { + return Timestamp{} + } + return *r.PushedAt +} + +// GetReleasesURL returns the ReleasesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetReleasesURL() string { + if r == nil || r.ReleasesURL == nil { + return "" + } + return *r.ReleasesURL +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *Repository) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetSource returns the Source field. +func (r *Repository) GetSource() *Repository { + if r == nil { + return nil + } + return r.Source +} + +// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSSHURL() string { + if r == nil || r.SSHURL == nil { + return "" + } + return *r.SSHURL +} + +// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetStargazersCount() int { + if r == nil || r.StargazersCount == nil { + return 0 + } + return *r.StargazersCount +} + +// GetStargazersURL returns the StargazersURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetStargazersURL() string { + if r == nil || r.StargazersURL == nil { + return "" + } + return *r.StargazersURL +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetStatusesURL() string { + if r == nil || r.StatusesURL == nil { + return "" + } + return *r.StatusesURL +} + +// GetSubscribersCount returns the SubscribersCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetSubscribersCount() int { + if r == nil || r.SubscribersCount == nil { + return 0 + } + return *r.SubscribersCount +} + +// GetSubscribersURL returns the SubscribersURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSubscribersURL() string { + if r == nil || r.SubscribersURL == nil { + return "" + } + return *r.SubscribersURL +} + +// GetSubscriptionURL returns the SubscriptionURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSubscriptionURL() string { + if r == nil || r.SubscriptionURL == nil { + return "" + } + return *r.SubscriptionURL +} + +// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSVNURL() string { + if r == nil || r.SVNURL == nil { + return "" + } + return *r.SVNURL +} + +// GetTagsURL returns the TagsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetTagsURL() string { + if r == nil || r.TagsURL == nil { + return "" + } + return *r.TagsURL +} + +// GetTeamID returns the TeamID field if it's non-nil, zero value otherwise. +func (r *Repository) GetTeamID() int64 { + if r == nil || r.TeamID == nil { + return 0 + } + return *r.TeamID +} + +// GetTeamsURL returns the TeamsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetTeamsURL() string { + if r == nil || r.TeamsURL == nil { + return "" + } + return *r.TeamsURL +} + +// GetTreesURL returns the TreesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetTreesURL() string { + if r == nil || r.TreesURL == nil { + return "" + } + return *r.TreesURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *Repository) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *Repository) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetWatchersCount() int { + if r == nil || r.WatchersCount == nil { + return 0 + } + return *r.WatchersCount +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetBody() string { + if r == nil || r.Body == nil { + return "" + } + return *r.Body +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetCommitID() string { + if r == nil || r.CommitID == nil { + return "" + } + return *r.CommitID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetCreatedAt() time.Time { + if r == nil || r.CreatedAt == nil { + return time.Time{} + } + return *r.CreatedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetPath() string { + if r == nil || r.Path == nil { + return "" + } + return *r.Path +} + +// GetPosition returns the Position field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetPosition() int { + if r == nil || r.Position == nil { + return 0 + } + return *r.Position +} + +// GetReactions returns the Reactions field. +func (r *RepositoryComment) GetReactions() *Reactions { + if r == nil { + return nil + } + return r.Reactions +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetUpdatedAt() time.Time { + if r == nil || r.UpdatedAt == nil { + return time.Time{} + } + return *r.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetUser returns the User field. +func (r *RepositoryComment) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + +// GetAuthor returns the Author field. +func (r *RepositoryCommit) GetAuthor() *User { + if r == nil { + return nil + } + return r.Author +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetCommentsURL() string { + if r == nil || r.CommentsURL == nil { + return "" + } + return *r.CommentsURL +} + +// GetCommit returns the Commit field. +func (r *RepositoryCommit) GetCommit() *Commit { + if r == nil { + return nil + } + return r.Commit +} + +// GetCommitter returns the Committer field. +func (r *RepositoryCommit) GetCommitter() *User { + if r == nil { + return nil + } + return r.Committer +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetStats returns the Stats field. +func (r *RepositoryCommit) GetStats() *CommitStats { + if r == nil { + return nil + } + return r.Stats +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetDownloadURL() string { + if r == nil || r.DownloadURL == nil { + return "" + } + return *r.DownloadURL +} + +// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetEncoding() string { + if r == nil || r.Encoding == nil { + return "" + } + return *r.Encoding +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetGitURL() string { + if r == nil || r.GitURL == nil { + return "" + } + return *r.GitURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetPath() string { + if r == nil || r.Path == nil { + return "" + } + return *r.Path +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetAuthor returns the Author field. +func (r *RepositoryContentFileOptions) GetAuthor() *CommitAuthor { + if r == nil { + return nil + } + return r.Author +} + +// GetBranch returns the Branch field if it's non-nil, zero value otherwise. +func (r *RepositoryContentFileOptions) GetBranch() string { + if r == nil || r.Branch == nil { + return "" + } + return *r.Branch +} + +// GetCommitter returns the Committer field. +func (r *RepositoryContentFileOptions) GetCommitter() *CommitAuthor { + if r == nil { + return nil + } + return r.Committer +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (r *RepositoryContentFileOptions) GetMessage() string { + if r == nil || r.Message == nil { + return "" + } + return *r.Message +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryContentFileOptions) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetContent returns the Content field. +func (r *RepositoryContentResponse) GetContent() *RepositoryContent { + if r == nil { + return nil + } + return r.Content +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *RepositoryEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetInstallation returns the Installation field. +func (r *RepositoryEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetOrg returns the Org field. +func (r *RepositoryEvent) GetOrg() *Organization { + if r == nil { + return nil + } + return r.Org +} + +// GetRepo returns the Repo field. +func (r *RepositoryEvent) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetSender returns the Sender field. +func (r *RepositoryEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetInvitee returns the Invitee field. +func (r *RepositoryInvitation) GetInvitee() *User { + if r == nil { + return nil + } + return r.Invitee +} + +// GetInviter returns the Inviter field. +func (r *RepositoryInvitation) GetInviter() *User { + if r == nil { + return nil + } + return r.Inviter +} + +// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetPermissions() string { + if r == nil || r.Permissions == nil { + return "" + } + return *r.Permissions +} + +// GetRepo returns the Repo field. +func (r *RepositoryInvitation) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetContent() string { + if r == nil || r.Content == nil { + return "" + } + return *r.Content +} + +// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetDownloadURL() string { + if r == nil || r.DownloadURL == nil { + return "" + } + return *r.DownloadURL +} + +// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetEncoding() string { + if r == nil || r.Encoding == nil { + return "" + } + return *r.Encoding +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetGitURL() string { + if r == nil || r.GitURL == nil { + return "" + } + return *r.GitURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetLicense returns the License field. +func (r *RepositoryLicense) GetLicense() *License { + if r == nil { + return nil + } + return r.License +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetPath() string { + if r == nil || r.Path == nil { + return "" + } + return *r.Path +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetBase returns the Base field if it's non-nil, zero value otherwise. +func (r *RepositoryMergeRequest) GetBase() string { + if r == nil || r.Base == nil { + return "" + } + return *r.Base +} + +// GetCommitMessage returns the CommitMessage field if it's non-nil, zero value otherwise. +func (r *RepositoryMergeRequest) GetCommitMessage() string { + if r == nil || r.CommitMessage == nil { + return "" + } + return *r.CommitMessage +} + +// GetHead returns the Head field if it's non-nil, zero value otherwise. +func (r *RepositoryMergeRequest) GetHead() string { + if r == nil || r.Head == nil { + return "" + } + return *r.Head +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissionLevel) GetPermission() string { + if r == nil || r.Permission == nil { + return "" + } + return *r.Permission +} + +// GetUser returns the User field. +func (r *RepositoryPermissionLevel) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + +// GetAssetsURL returns the AssetsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetAssetsURL() string { + if r == nil || r.AssetsURL == nil { + return "" + } + return *r.AssetsURL +} + +// GetAuthor returns the Author field. +func (r *RepositoryRelease) GetAuthor() *User { + if r == nil { + return nil + } + return r.Author +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetBody() string { + if r == nil || r.Body == nil { + return "" + } + return *r.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetDraft() bool { + if r == nil || r.Draft == nil { + return false + } + return *r.Draft +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetPrerelease() bool { + if r == nil || r.Prerelease == nil { + return false + } + return *r.Prerelease +} + +// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetPublishedAt() Timestamp { + if r == nil || r.PublishedAt == nil { + return Timestamp{} + } + return *r.PublishedAt +} + +// GetTagName returns the TagName field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetTagName() string { + if r == nil || r.TagName == nil { + return "" + } + return *r.TagName +} + +// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetTarballURL() string { + if r == nil || r.TarballURL == nil { + return "" + } + return *r.TarballURL +} + +// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetTargetCommitish() string { + if r == nil || r.TargetCommitish == nil { + return "" + } + return *r.TargetCommitish +} + +// GetUploadURL returns the UploadURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetUploadURL() string { + if r == nil || r.UploadURL == nil { + return "" + } + return *r.UploadURL +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetZipballURL() string { + if r == nil || r.ZipballURL == nil { + return "" + } + return *r.ZipballURL +} + +// GetCommit returns the Commit field. +func (r *RepositoryTag) GetCommit() *Commit { + if r == nil { + return nil + } + return r.Commit +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryTag) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryTag) GetTarballURL() string { + if r == nil || r.TarballURL == nil { + return "" + } + return *r.TarballURL +} + +// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryTag) GetZipballURL() string { + if r == nil || r.ZipballURL == nil { + return "" + } + return *r.ZipballURL +} + +// GetForkRepos returns the ForkRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetForkRepos() int { + if r == nil || r.ForkRepos == nil { + return 0 + } + return *r.ForkRepos +} + +// GetOrgRepos returns the OrgRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetOrgRepos() int { + if r == nil || r.OrgRepos == nil { + return 0 + } + return *r.OrgRepos +} + +// GetRootRepos returns the RootRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetRootRepos() int { + if r == nil || r.RootRepos == nil { + return 0 + } + return *r.RootRepos +} + +// GetTotalPushes returns the TotalPushes field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetTotalPushes() int { + if r == nil || r.TotalPushes == nil { + return 0 + } + return *r.TotalPushes +} + +// GetTotalRepos returns the TotalRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetTotalRepos() int { + if r == nil || r.TotalRepos == nil { + return 0 + } + return *r.TotalRepos +} + +// GetTotalWikis returns the TotalWikis field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetTotalWikis() int { + if r == nil || r.TotalWikis == nil { + return 0 + } + return *r.TotalWikis +} + +// GetContext returns the Context field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetContext() string { + if r == nil || r.Context == nil { + return "" + } + return *r.Context +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetCreatedAt() time.Time { + if r == nil || r.CreatedAt == nil { + return time.Time{} + } + return *r.CreatedAt +} + +// GetCreator returns the Creator field. +func (r *RepoStatus) GetCreator() *User { + if r == nil { + return nil + } + return r.Creator +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetDescription() string { + if r == nil || r.Description == nil { + return "" + } + return *r.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetState() string { + if r == nil || r.State == nil { + return "" + } + return *r.State +} + +// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetTargetURL() string { + if r == nil || r.TargetURL == nil { + return "" + } + return *r.TargetURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetUpdatedAt() time.Time { + if r == nil || r.UpdatedAt == nil { + return time.Time{} + } + return *r.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (s *ServiceHook) GetName() string { + if s == nil || s.Name == nil { + return "" + } + return *s.Name +} + +// GetPayload returns the Payload field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetPayload() string { + if s == nil || s.Payload == nil { + return "" + } + return *s.Payload +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetReason() string { + if s == nil || s.Reason == nil { + return "" + } + return *s.Reason +} + +// GetSignature returns the Signature field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetSignature() string { + if s == nil || s.Signature == nil { + return "" + } + return *s.Signature +} + +// GetVerified returns the Verified field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetVerified() bool { + if s == nil || s.Verified == nil { + return false + } + return *s.Verified +} + +// GetActor returns the Actor field. +func (s *Source) GetActor() *User { + if s == nil { + return nil + } + return s.Actor +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *Source) GetID() int64 { + if s == nil || s.ID == nil { + return 0 + } + return *s.ID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *Source) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetEmail() string { + if s == nil || s.Email == nil { + return "" + } + return *s.Email +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetID() int64 { + if s == nil || s.ID == nil { + return 0 + } + return *s.ID +} + +// GetImportURL returns the ImportURL field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetImportURL() string { + if s == nil || s.ImportURL == nil { + return "" + } + return *s.ImportURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetName() string { + if s == nil || s.Name == nil { + return "" + } + return *s.Name +} + +// GetRemoteID returns the RemoteID field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetRemoteID() string { + if s == nil || s.RemoteID == nil { + return "" + } + return *s.RemoteID +} + +// GetRemoteName returns the RemoteName field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetRemoteName() string { + if s == nil || s.RemoteName == nil { + return "" + } + return *s.RemoteName +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + +// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. +func (s *Stargazer) GetStarredAt() Timestamp { + if s == nil || s.StarredAt == nil { + return Timestamp{} + } + return *s.StarredAt +} + +// GetUser returns the User field. +func (s *Stargazer) GetUser() *User { + if s == nil { + return nil + } + return s.User +} + +// GetRepository returns the Repository field. +func (s *StarredRepository) GetRepository() *Repository { + if s == nil { + return nil + } + return s.Repository +} + +// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. +func (s *StarredRepository) GetStarredAt() Timestamp { + if s == nil || s.StarredAt == nil { + return Timestamp{} + } + return *s.StarredAt +} + +// GetCommit returns the Commit field. +func (s *StatusEvent) GetCommit() *RepositoryCommit { + if s == nil { + return nil + } + return s.Commit +} + +// GetContext returns the Context field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetContext() string { + if s == nil || s.Context == nil { + return "" + } + return *s.Context +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetDescription() string { + if s == nil || s.Description == nil { + return "" + } + return *s.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetID() int64 { + if s == nil || s.ID == nil { + return 0 + } + return *s.ID +} + +// GetInstallation returns the Installation field. +func (s *StatusEvent) GetInstallation() *Installation { + if s == nil { + return nil + } + return s.Installation +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetName() string { + if s == nil || s.Name == nil { + return "" + } + return *s.Name +} + +// GetRepo returns the Repo field. +func (s *StatusEvent) GetRepo() *Repository { + if s == nil { + return nil + } + return s.Repo +} + +// GetSender returns the Sender field. +func (s *StatusEvent) GetSender() *User { + if s == nil { + return nil + } + return s.Sender +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetSHA() string { + if s == nil || s.SHA == nil { + return "" + } + return *s.SHA +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetState() string { + if s == nil || s.State == nil { + return "" + } + return *s.State +} + +// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetTargetURL() string { + if s == nil || s.TargetURL == nil { + return "" + } + return *s.TargetURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetUpdatedAt() Timestamp { + if s == nil || s.UpdatedAt == nil { + return Timestamp{} + } + return *s.UpdatedAt +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *Subscription) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetIgnored returns the Ignored field if it's non-nil, zero value otherwise. +func (s *Subscription) GetIgnored() bool { + if s == nil || s.Ignored == nil { + return false + } + return *s.Ignored +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (s *Subscription) GetReason() string { + if s == nil || s.Reason == nil { + return "" + } + return *s.Reason +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (s *Subscription) GetRepositoryURL() string { + if s == nil || s.RepositoryURL == nil { + return "" + } + return *s.RepositoryURL +} + +// GetSubscribed returns the Subscribed field if it's non-nil, zero value otherwise. +func (s *Subscription) GetSubscribed() bool { + if s == nil || s.Subscribed == nil { + return false + } + return *s.Subscribed +} + +// GetThreadURL returns the ThreadURL field if it's non-nil, zero value otherwise. +func (s *Subscription) GetThreadURL() string { + if s == nil || s.ThreadURL == nil { + return "" + } + return *s.ThreadURL +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *Subscription) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (t *Tag) GetMessage() string { + if t == nil || t.Message == nil { + return "" + } + return *t.Message +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (t *Tag) GetNodeID() string { + if t == nil || t.NodeID == nil { + return "" + } + return *t.NodeID +} + +// GetObject returns the Object field. +func (t *Tag) GetObject() *GitObject { + if t == nil { + return nil + } + return t.Object +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (t *Tag) GetSHA() string { + if t == nil || t.SHA == nil { + return "" + } + return *t.SHA +} + +// GetTag returns the Tag field if it's non-nil, zero value otherwise. +func (t *Tag) GetTag() string { + if t == nil || t.Tag == nil { + return "" + } + return *t.Tag +} + +// GetTagger returns the Tagger field. +func (t *Tag) GetTagger() *CommitAuthor { + if t == nil { + return nil + } + return t.Tagger +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *Tag) GetURL() string { + if t == nil || t.URL == nil { + return "" + } + return *t.URL +} + +// GetVerification returns the Verification field. +func (t *Tag) GetVerification() *SignatureVerification { + if t == nil { + return nil + } + return t.Verification +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (t *Team) GetDescription() string { + if t == nil || t.Description == nil { + return "" + } + return *t.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *Team) GetID() int64 { + if t == nil || t.ID == nil { + return 0 + } + return *t.ID +} + +// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. +func (t *Team) GetLDAPDN() string { + if t == nil || t.LDAPDN == nil { + return "" + } + return *t.LDAPDN +} + +// GetMembersCount returns the MembersCount field if it's non-nil, zero value otherwise. +func (t *Team) GetMembersCount() int { + if t == nil || t.MembersCount == nil { + return 0 + } + return *t.MembersCount +} + +// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. +func (t *Team) GetMembersURL() string { + if t == nil || t.MembersURL == nil { + return "" + } + return *t.MembersURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (t *Team) GetName() string { + if t == nil || t.Name == nil { + return "" + } + return *t.Name +} + +// GetOrganization returns the Organization field. +func (t *Team) GetOrganization() *Organization { + if t == nil { + return nil + } + return t.Organization +} + +// GetParent returns the Parent field. +func (t *Team) GetParent() *Team { + if t == nil { + return nil + } + return t.Parent +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (t *Team) GetPermission() string { + if t == nil || t.Permission == nil { + return "" + } + return *t.Permission +} + +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (t *Team) GetPrivacy() string { + if t == nil || t.Privacy == nil { + return "" + } + return *t.Privacy +} + +// GetReposCount returns the ReposCount field if it's non-nil, zero value otherwise. +func (t *Team) GetReposCount() int { + if t == nil || t.ReposCount == nil { + return 0 + } + return *t.ReposCount +} + +// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. +func (t *Team) GetRepositoriesURL() string { + if t == nil || t.RepositoriesURL == nil { + return "" + } + return *t.RepositoriesURL +} + +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (t *Team) GetSlug() string { + if t == nil || t.Slug == nil { + return "" + } + return *t.Slug +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *Team) GetURL() string { + if t == nil || t.URL == nil { + return "" + } + return *t.URL +} + +// GetInstallation returns the Installation field. +func (t *TeamAddEvent) GetInstallation() *Installation { + if t == nil { + return nil + } + return t.Installation +} + +// GetOrg returns the Org field. +func (t *TeamAddEvent) GetOrg() *Organization { + if t == nil { + return nil + } + return t.Org +} + +// GetRepo returns the Repo field. +func (t *TeamAddEvent) GetRepo() *Repository { + if t == nil { + return nil + } + return t.Repo +} + +// GetSender returns the Sender field. +func (t *TeamAddEvent) GetSender() *User { + if t == nil { + return nil + } + return t.Sender +} + +// GetTeam returns the Team field. +func (t *TeamAddEvent) GetTeam() *Team { + if t == nil { + return nil + } + return t.Team +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (t *TeamEvent) GetAction() string { + if t == nil || t.Action == nil { + return "" + } + return *t.Action +} + +// GetChanges returns the Changes field. +func (t *TeamEvent) GetChanges() *TeamChange { + if t == nil { + return nil + } + return t.Changes +} + +// GetInstallation returns the Installation field. +func (t *TeamEvent) GetInstallation() *Installation { + if t == nil { + return nil + } + return t.Installation +} + +// GetOrg returns the Org field. +func (t *TeamEvent) GetOrg() *Organization { + if t == nil { + return nil + } + return t.Org +} + +// GetRepo returns the Repo field. +func (t *TeamEvent) GetRepo() *Repository { + if t == nil { + return nil + } + return t.Repo +} + +// GetSender returns the Sender field. +func (t *TeamEvent) GetSender() *User { + if t == nil { + return nil + } + return t.Sender +} + +// GetTeam returns the Team field. +func (t *TeamEvent) GetTeam() *Team { + if t == nil { + return nil + } + return t.Team +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetDescription() string { + if t == nil || t.Description == nil { + return "" + } + return *t.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetID() int64 { + if t == nil || t.ID == nil { + return 0 + } + return *t.ID +} + +// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetLDAPDN() string { + if t == nil || t.LDAPDN == nil { + return "" + } + return *t.LDAPDN +} + +// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetMembersURL() string { + if t == nil || t.MembersURL == nil { + return "" + } + return *t.MembersURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetName() string { + if t == nil || t.Name == nil { + return "" + } + return *t.Name +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetPermission() string { + if t == nil || t.Permission == nil { + return "" + } + return *t.Permission +} + +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetPrivacy() string { + if t == nil || t.Privacy == nil { + return "" + } + return *t.Privacy +} + +// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetRepositoriesURL() string { + if t == nil || t.RepositoriesURL == nil { + return "" + } + return *t.RepositoriesURL +} + +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetSlug() string { + if t == nil || t.Slug == nil { + return "" + } + return *t.Slug +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetURL() string { + if t == nil || t.URL == nil { + return "" + } + return *t.URL +} + +// GetFragment returns the Fragment field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetFragment() string { + if t == nil || t.Fragment == nil { + return "" + } + return *t.Fragment +} + +// GetObjectType returns the ObjectType field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetObjectType() string { + if t == nil || t.ObjectType == nil { + return "" + } + return *t.ObjectType +} + +// GetObjectURL returns the ObjectURL field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetObjectURL() string { + if t == nil || t.ObjectURL == nil { + return "" + } + return *t.ObjectURL +} + +// GetProperty returns the Property field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetProperty() string { + if t == nil || t.Property == nil { + return "" + } + return *t.Property +} + +// GetActor returns the Actor field. +func (t *Timeline) GetActor() *User { + if t == nil { + return nil + } + return t.Actor +} + +// GetAssignee returns the Assignee field. +func (t *Timeline) GetAssignee() *User { + if t == nil { + return nil + } + return t.Assignee +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (t *Timeline) GetCommitID() string { + if t == nil || t.CommitID == nil { + return "" + } + return *t.CommitID +} + +// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. +func (t *Timeline) GetCommitURL() string { + if t == nil || t.CommitURL == nil { + return "" + } + return *t.CommitURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (t *Timeline) GetCreatedAt() time.Time { + if t == nil || t.CreatedAt == nil { + return time.Time{} + } + return *t.CreatedAt +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (t *Timeline) GetEvent() string { + if t == nil || t.Event == nil { + return "" + } + return *t.Event +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *Timeline) GetID() int64 { + if t == nil || t.ID == nil { + return 0 + } + return *t.ID +} + +// GetLabel returns the Label field. +func (t *Timeline) GetLabel() *Label { + if t == nil { + return nil + } + return t.Label +} + +// GetMilestone returns the Milestone field. +func (t *Timeline) GetMilestone() *Milestone { + if t == nil { + return nil + } + return t.Milestone +} + +// GetRename returns the Rename field. +func (t *Timeline) GetRename() *Rename { + if t == nil { + return nil + } + return t.Rename +} + +// GetSource returns the Source field. +func (t *Timeline) GetSource() *Source { + if t == nil { + return nil + } + return t.Source +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *Timeline) GetURL() string { + if t == nil || t.URL == nil { + return "" + } + return *t.URL +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficClones) GetCount() int { + if t == nil || t.Count == nil { + return 0 + } + return *t.Count +} + +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficClones) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 + } + return *t.Uniques +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficData) GetCount() int { + if t == nil || t.Count == nil { + return 0 + } + return *t.Count +} + +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (t *TrafficData) GetTimestamp() Timestamp { + if t == nil || t.Timestamp == nil { + return Timestamp{} + } + return *t.Timestamp +} + +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficData) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 + } + return *t.Uniques +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetCount() int { + if t == nil || t.Count == nil { + return 0 + } + return *t.Count +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetPath() string { + if t == nil || t.Path == nil { + return "" + } + return *t.Path +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetTitle() string { + if t == nil || t.Title == nil { + return "" + } + return *t.Title +} + +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 + } + return *t.Uniques +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficReferrer) GetCount() int { + if t == nil || t.Count == nil { + return 0 + } + return *t.Count +} + +// GetReferrer returns the Referrer field if it's non-nil, zero value otherwise. +func (t *TrafficReferrer) GetReferrer() string { + if t == nil || t.Referrer == nil { + return "" + } + return *t.Referrer +} + +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficReferrer) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 + } + return *t.Uniques +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficViews) GetCount() int { + if t == nil || t.Count == nil { + return 0 + } + return *t.Count +} + +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficViews) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 + } + return *t.Uniques +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (t *Tree) GetSHA() string { + if t == nil || t.SHA == nil { + return "" + } + return *t.SHA +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetContent() string { + if t == nil || t.Content == nil { + return "" + } + return *t.Content +} + +// GetMode returns the Mode field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetMode() string { + if t == nil || t.Mode == nil { + return "" + } + return *t.Mode +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetPath() string { + if t == nil || t.Path == nil { + return "" + } + return *t.Path +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetSHA() string { + if t == nil || t.SHA == nil { + return "" + } + return *t.SHA +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetSize() int { + if t == nil || t.Size == nil { + return 0 + } + return *t.Size +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetType() string { + if t == nil || t.Type == nil { + return "" + } + return *t.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetURL() string { + if t == nil || t.URL == nil { + return "" + } + return *t.URL +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (u *User) GetAvatarURL() string { + if u == nil || u.AvatarURL == nil { + return "" + } + return *u.AvatarURL +} + +// GetBio returns the Bio field if it's non-nil, zero value otherwise. +func (u *User) GetBio() string { + if u == nil || u.Bio == nil { + return "" + } + return *u.Bio +} + +// GetBlog returns the Blog field if it's non-nil, zero value otherwise. +func (u *User) GetBlog() string { + if u == nil || u.Blog == nil { + return "" + } + return *u.Blog +} + +// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. +func (u *User) GetCollaborators() int { + if u == nil || u.Collaborators == nil { + return 0 + } + return *u.Collaborators +} + +// GetCompany returns the Company field if it's non-nil, zero value otherwise. +func (u *User) GetCompany() string { + if u == nil || u.Company == nil { + return "" + } + return *u.Company +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (u *User) GetCreatedAt() Timestamp { + if u == nil || u.CreatedAt == nil { + return Timestamp{} + } + return *u.CreatedAt +} + +// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. +func (u *User) GetDiskUsage() int { + if u == nil || u.DiskUsage == nil { + return 0 + } + return *u.DiskUsage +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (u *User) GetEmail() string { + if u == nil || u.Email == nil { + return "" + } + return *u.Email +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (u *User) GetEventsURL() string { + if u == nil || u.EventsURL == nil { + return "" + } + return *u.EventsURL +} + +// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. +func (u *User) GetFollowers() int { + if u == nil || u.Followers == nil { + return 0 + } + return *u.Followers +} + +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (u *User) GetFollowersURL() string { + if u == nil || u.FollowersURL == nil { + return "" + } + return *u.FollowersURL +} + +// GetFollowing returns the Following field if it's non-nil, zero value otherwise. +func (u *User) GetFollowing() int { + if u == nil || u.Following == nil { + return 0 + } + return *u.Following +} + +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (u *User) GetFollowingURL() string { + if u == nil || u.FollowingURL == nil { + return "" + } + return *u.FollowingURL +} + +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (u *User) GetGistsURL() string { + if u == nil || u.GistsURL == nil { + return "" + } + return *u.GistsURL +} + +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (u *User) GetGravatarID() string { + if u == nil || u.GravatarID == nil { + return "" + } + return *u.GravatarID +} + +// GetHireable returns the Hireable field if it's non-nil, zero value otherwise. +func (u *User) GetHireable() bool { + if u == nil || u.Hireable == nil { + return false + } + return *u.Hireable +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (u *User) GetHTMLURL() string { + if u == nil || u.HTMLURL == nil { + return "" + } + return *u.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (u *User) GetID() int64 { + if u == nil || u.ID == nil { + return 0 + } + return *u.ID +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (u *User) GetLocation() string { + if u == nil || u.Location == nil { + return "" + } + return *u.Location +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (u *User) GetLogin() string { + if u == nil || u.Login == nil { + return "" + } + return *u.Login +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *User) GetName() string { + if u == nil || u.Name == nil { + return "" + } + return *u.Name +} + +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (u *User) GetOrganizationsURL() string { + if u == nil || u.OrganizationsURL == nil { + return "" + } + return *u.OrganizationsURL +} + +// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. +func (u *User) GetOwnedPrivateRepos() int { + if u == nil || u.OwnedPrivateRepos == nil { + return 0 + } + return *u.OwnedPrivateRepos +} + +// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. +func (u *User) GetPermissions() map[string]bool { + if u == nil || u.Permissions == nil { + return map[string]bool{} + } + return *u.Permissions +} + +// GetPlan returns the Plan field. +func (u *User) GetPlan() *Plan { + if u == nil { + return nil + } + return u.Plan +} + +// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. +func (u *User) GetPrivateGists() int { + if u == nil || u.PrivateGists == nil { + return 0 + } + return *u.PrivateGists +} + +// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. +func (u *User) GetPublicGists() int { + if u == nil || u.PublicGists == nil { + return 0 + } + return *u.PublicGists +} + +// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. +func (u *User) GetPublicRepos() int { + if u == nil || u.PublicRepos == nil { + return 0 + } + return *u.PublicRepos +} + +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (u *User) GetReceivedEventsURL() string { + if u == nil || u.ReceivedEventsURL == nil { + return "" + } + return *u.ReceivedEventsURL +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (u *User) GetReposURL() string { + if u == nil || u.ReposURL == nil { + return "" + } + return *u.ReposURL +} + +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (u *User) GetSiteAdmin() bool { + if u == nil || u.SiteAdmin == nil { + return false + } + return *u.SiteAdmin +} + +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (u *User) GetStarredURL() string { + if u == nil || u.StarredURL == nil { + return "" + } + return *u.StarredURL +} + +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (u *User) GetSubscriptionsURL() string { + if u == nil || u.SubscriptionsURL == nil { + return "" + } + return *u.SubscriptionsURL +} + +// GetSuspendedAt returns the SuspendedAt field if it's non-nil, zero value otherwise. +func (u *User) GetSuspendedAt() Timestamp { + if u == nil || u.SuspendedAt == nil { + return Timestamp{} + } + return *u.SuspendedAt +} + +// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. +func (u *User) GetTotalPrivateRepos() int { + if u == nil || u.TotalPrivateRepos == nil { + return 0 + } + return *u.TotalPrivateRepos +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (u *User) GetType() string { + if u == nil || u.Type == nil { + return "" + } + return *u.Type +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (u *User) GetUpdatedAt() Timestamp { + if u == nil || u.UpdatedAt == nil { + return Timestamp{} + } + return *u.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (u *User) GetURL() string { + if u == nil || u.URL == nil { + return "" + } + return *u.URL +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (u *UserEmail) GetEmail() string { + if u == nil || u.Email == nil { + return "" + } + return *u.Email +} + +// GetPrimary returns the Primary field if it's non-nil, zero value otherwise. +func (u *UserEmail) GetPrimary() bool { + if u == nil || u.Primary == nil { + return false + } + return *u.Primary +} + +// GetVerified returns the Verified field if it's non-nil, zero value otherwise. +func (u *UserEmail) GetVerified() bool { + if u == nil || u.Verified == nil { + return false + } + return *u.Verified +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetAvatarURL() string { + if u == nil || u.AvatarURL == nil { + return "" + } + return *u.AvatarURL +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetEventsURL() string { + if u == nil || u.EventsURL == nil { + return "" + } + return *u.EventsURL +} + +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetFollowersURL() string { + if u == nil || u.FollowersURL == nil { + return "" + } + return *u.FollowersURL +} + +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetFollowingURL() string { + if u == nil || u.FollowingURL == nil { + return "" + } + return *u.FollowingURL +} + +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetGistsURL() string { + if u == nil || u.GistsURL == nil { + return "" + } + return *u.GistsURL +} + +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetGravatarID() string { + if u == nil || u.GravatarID == nil { + return "" + } + return *u.GravatarID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetID() int64 { + if u == nil || u.ID == nil { + return 0 + } + return *u.ID +} + +// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetLDAPDN() string { + if u == nil || u.LDAPDN == nil { + return "" + } + return *u.LDAPDN +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetLogin() string { + if u == nil || u.Login == nil { + return "" + } + return *u.Login +} + +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetOrganizationsURL() string { + if u == nil || u.OrganizationsURL == nil { + return "" + } + return *u.OrganizationsURL +} + +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetReceivedEventsURL() string { + if u == nil || u.ReceivedEventsURL == nil { + return "" + } + return *u.ReceivedEventsURL +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetReposURL() string { + if u == nil || u.ReposURL == nil { + return "" + } + return *u.ReposURL +} + +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetSiteAdmin() bool { + if u == nil || u.SiteAdmin == nil { + return false + } + return *u.SiteAdmin +} + +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetStarredURL() string { + if u == nil || u.StarredURL == nil { + return "" + } + return *u.StarredURL +} + +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetSubscriptionsURL() string { + if u == nil || u.SubscriptionsURL == nil { + return "" + } + return *u.SubscriptionsURL +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetType() string { + if u == nil || u.Type == nil { + return "" + } + return *u.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetURL() string { + if u == nil || u.URL == nil { + return "" + } + return *u.URL +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (u *UsersSearchResult) GetIncompleteResults() bool { + if u == nil || u.IncompleteResults == nil { + return false + } + return *u.IncompleteResults +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (u *UsersSearchResult) GetTotal() int { + if u == nil || u.Total == nil { + return 0 + } + return *u.Total +} + +// GetAdminUsers returns the AdminUsers field if it's non-nil, zero value otherwise. +func (u *UserStats) GetAdminUsers() int { + if u == nil || u.AdminUsers == nil { + return 0 + } + return *u.AdminUsers +} + +// GetSuspendedUsers returns the SuspendedUsers field if it's non-nil, zero value otherwise. +func (u *UserStats) GetSuspendedUsers() int { + if u == nil || u.SuspendedUsers == nil { + return 0 + } + return *u.SuspendedUsers +} + +// GetTotalUsers returns the TotalUsers field if it's non-nil, zero value otherwise. +func (u *UserStats) GetTotalUsers() int { + if u == nil || u.TotalUsers == nil { + return 0 + } + return *u.TotalUsers +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (w *WatchEvent) GetAction() string { + if w == nil || w.Action == nil { + return "" + } + return *w.Action +} + +// GetInstallation returns the Installation field. +func (w *WatchEvent) GetInstallation() *Installation { + if w == nil { + return nil + } + return w.Installation +} + +// GetRepo returns the Repo field. +func (w *WatchEvent) GetRepo() *Repository { + if w == nil { + return nil + } + return w.Repo +} + +// GetSender returns the Sender field. +func (w *WatchEvent) GetSender() *User { + if w == nil { + return nil + } + return w.Sender +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (w *WebHookAuthor) GetEmail() string { + if w == nil || w.Email == nil { + return "" + } + return *w.Email +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (w *WebHookAuthor) GetName() string { + if w == nil || w.Name == nil { + return "" + } + return *w.Name +} + +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (w *WebHookAuthor) GetUsername() string { + if w == nil || w.Username == nil { + return "" + } + return *w.Username +} + +// GetAuthor returns the Author field. +func (w *WebHookCommit) GetAuthor() *WebHookAuthor { + if w == nil { + return nil + } + return w.Author +} + +// GetCommitter returns the Committer field. +func (w *WebHookCommit) GetCommitter() *WebHookAuthor { + if w == nil { + return nil + } + return w.Committer +} + +// GetDistinct returns the Distinct field if it's non-nil, zero value otherwise. +func (w *WebHookCommit) GetDistinct() bool { + if w == nil || w.Distinct == nil { + return false + } + return *w.Distinct +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (w *WebHookCommit) GetID() string { + if w == nil || w.ID == nil { + return "" + } + return *w.ID +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (w *WebHookCommit) GetMessage() string { + if w == nil || w.Message == nil { + return "" + } + return *w.Message +} + +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (w *WebHookCommit) GetTimestamp() time.Time { + if w == nil || w.Timestamp == nil { + return time.Time{} + } + return *w.Timestamp +} + +// GetAfter returns the After field if it's non-nil, zero value otherwise. +func (w *WebHookPayload) GetAfter() string { + if w == nil || w.After == nil { + return "" + } + return *w.After +} + +// GetBefore returns the Before field if it's non-nil, zero value otherwise. +func (w *WebHookPayload) GetBefore() string { + if w == nil || w.Before == nil { + return "" + } + return *w.Before +} + +// GetCompare returns the Compare field if it's non-nil, zero value otherwise. +func (w *WebHookPayload) GetCompare() string { + if w == nil || w.Compare == nil { + return "" + } + return *w.Compare +} + +// GetCreated returns the Created field if it's non-nil, zero value otherwise. +func (w *WebHookPayload) GetCreated() bool { + if w == nil || w.Created == nil { + return false + } + return *w.Created +} + +// GetDeleted returns the Deleted field if it's non-nil, zero value otherwise. +func (w *WebHookPayload) GetDeleted() bool { + if w == nil || w.Deleted == nil { + return false + } + return *w.Deleted +} + +// GetForced returns the Forced field if it's non-nil, zero value otherwise. +func (w *WebHookPayload) GetForced() bool { + if w == nil || w.Forced == nil { + return false + } + return *w.Forced +} + +// GetHeadCommit returns the HeadCommit field. +func (w *WebHookPayload) GetHeadCommit() *WebHookCommit { + if w == nil { + return nil + } + return w.HeadCommit +} + +// GetPusher returns the Pusher field. +func (w *WebHookPayload) GetPusher() *User { + if w == nil { + return nil + } + return w.Pusher +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (w *WebHookPayload) GetRef() string { + if w == nil || w.Ref == nil { + return "" + } + return *w.Ref +} + +// GetRepo returns the Repo field. +func (w *WebHookPayload) GetRepo() *Repository { + if w == nil { + return nil + } + return w.Repo +} + +// GetSender returns the Sender field. +func (w *WebHookPayload) GetSender() *User { + if w == nil { + return nil + } + return w.Sender +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (w *WeeklyCommitActivity) GetTotal() int { + if w == nil || w.Total == nil { + return 0 + } + return *w.Total +} + +// GetWeek returns the Week field if it's non-nil, zero value otherwise. +func (w *WeeklyCommitActivity) GetWeek() Timestamp { + if w == nil || w.Week == nil { + return Timestamp{} + } + return *w.Week +} + +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetAdditions() int { + if w == nil || w.Additions == nil { + return 0 + } + return *w.Additions +} + +// GetCommits returns the Commits field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetCommits() int { + if w == nil || w.Commits == nil { + return 0 + } + return *w.Commits +} + +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetDeletions() int { + if w == nil || w.Deletions == nil { + return 0 + } + return *w.Deletions +} + +// GetWeek returns the Week field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetWeek() Timestamp { + if w == nil || w.Week == nil { + return Timestamp{} + } + return *w.Week +} diff --git a/vendor/github.com/google/go-github/github/github.go b/vendor/github.com/google/go-github/github/github.go new file mode 100644 index 00000000..08247226 --- /dev/null +++ b/vendor/github.com/google/go-github/github/github.go @@ -0,0 +1,980 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run gen-accessors.go + +package github + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "strconv" + "strings" + "sync" + "time" + + "github.com/google/go-querystring/query" +) + +const ( + libraryVersion = "15" + defaultBaseURL = "https://api.github.com/" + uploadBaseURL = "https://uploads.github.com/" + userAgent = "go-github/" + libraryVersion + + headerRateLimit = "X-RateLimit-Limit" + headerRateRemaining = "X-RateLimit-Remaining" + headerRateReset = "X-RateLimit-Reset" + headerOTP = "X-GitHub-OTP" + + mediaTypeV3 = "application/vnd.github.v3+json" + defaultMediaType = "application/octet-stream" + mediaTypeV3SHA = "application/vnd.github.v3.sha" + mediaTypeV3Diff = "application/vnd.github.v3.diff" + mediaTypeV3Patch = "application/vnd.github.v3.patch" + mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json" + + // Media Type values to access preview APIs + + // https://developer.github.com/changes/2015-03-09-licenses-api/ + mediaTypeLicensesPreview = "application/vnd.github.drax-preview+json" + + // https://developer.github.com/changes/2014-12-09-new-attributes-for-stars-api/ + mediaTypeStarringPreview = "application/vnd.github.v3.star+json" + + // https://developer.github.com/changes/2015-11-11-protected-branches-api/ + mediaTypeProtectedBranchesPreview = "application/vnd.github.loki-preview+json" + + // https://help.github.com/enterprise/2.4/admin/guides/migrations/exporting-the-github-com-organization-s-repositories/ + mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json" + + // https://developer.github.com/changes/2016-04-06-deployment-and-deployment-status-enhancements/ + mediaTypeDeploymentStatusPreview = "application/vnd.github.ant-man-preview+json" + + // https://developer.github.com/changes/2016-02-19-source-import-preview-api/ + mediaTypeImportPreview = "application/vnd.github.barred-rock-preview" + + // https://developer.github.com/changes/2016-05-12-reactions-api-preview/ + mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview" + + // https://developer.github.com/changes/2016-04-04-git-signing-api-preview/ + mediaTypeGitSigningPreview = "application/vnd.github.cryptographer-preview+json" + + // https://developer.github.com/changes/2016-05-23-timeline-preview-api/ + mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json" + + // https://developer.github.com/changes/2016-06-14-repository-invitations/ + mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json" + + // https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/ + mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json" + + // https://developer.github.com/changes/2016-09-14-projects-api/ + mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json" + + // https://developer.github.com/changes/2016-09-14-Integrations-Early-Access/ + mediaTypeIntegrationPreview = "application/vnd.github.machine-man-preview+json" + + // https://developer.github.com/changes/2017-01-05-commit-search-api/ + mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json" + + // https://developer.github.com/changes/2017-02-28-user-blocking-apis-and-webhook/ + mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json" + + // https://developer.github.com/changes/2017-02-09-community-health/ + mediaTypeRepositoryCommunityHealthMetricsPreview = "application/vnd.github.black-panther-preview+json" + + // https://developer.github.com/changes/2017-05-23-coc-api/ + mediaTypeCodesOfConductPreview = "application/vnd.github.scarlet-witch-preview+json" + + // https://developer.github.com/changes/2017-07-17-update-topics-on-repositories/ + mediaTypeTopicsPreview = "application/vnd.github.mercy-preview+json" + + // https://developer.github.com/changes/2017-07-26-team-review-request-thor-preview/ + mediaTypeTeamReviewPreview = "application/vnd.github.thor-preview+json" + + // https://developer.github.com/v3/apps/marketplace/ + mediaTypeMarketplacePreview = "application/vnd.github.valkyrie-preview+json" + + // https://developer.github.com/changes/2017-08-30-preview-nested-teams/ + mediaTypeNestedTeamsPreview = "application/vnd.github.hellcat-preview+json" + + // https://developer.github.com/changes/2017-11-09-repository-transfer-api-preview/ + mediaTypeRepositoryTransferPreview = "application/vnd.github.nightshade-preview+json" + + // https://developer.github.com/changes/2017-12-19-graphql-node-id/ + mediaTypeGraphQLNodeIDPreview = "application/vnd.github.jean-grey-preview+json" +) + +// A Client manages communication with the GitHub API. +type Client struct { + clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func. + client *http.Client // HTTP client used to communicate with the API. + + // Base URL for API requests. Defaults to the public GitHub API, but can be + // set to a domain endpoint to use with GitHub Enterprise. BaseURL should + // always be specified with a trailing slash. + BaseURL *url.URL + + // Base URL for uploading files. + UploadURL *url.URL + + // User agent used when communicating with the GitHub API. + UserAgent string + + rateMu sync.Mutex + rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls. + + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // Services used for talking to different parts of the GitHub API. + Activity *ActivityService + Admin *AdminService + Apps *AppsService + Authorizations *AuthorizationsService + Gists *GistsService + Git *GitService + Gitignores *GitignoresService + Issues *IssuesService + Licenses *LicensesService + Marketplace *MarketplaceService + Migrations *MigrationService + Organizations *OrganizationsService + Projects *ProjectsService + PullRequests *PullRequestsService + Reactions *ReactionsService + Repositories *RepositoriesService + Search *SearchService + Users *UsersService +} + +type service struct { + client *Client +} + +// ListOptions specifies the optional parameters to various List methods that +// support pagination. +type ListOptions struct { + // For paginated result sets, page of results to retrieve. + Page int `url:"page,omitempty"` + + // For paginated result sets, the number of results to include per page. + PerPage int `url:"per_page,omitempty"` +} + +// UploadOptions specifies the parameters to methods that support uploads. +type UploadOptions struct { + Name string `url:"name,omitempty"` +} + +// RawType represents type of raw format of a request instead of JSON. +type RawType uint8 + +const ( + // Diff format. + Diff RawType = 1 + iota + // Patch format. + Patch +) + +// RawOptions specifies parameters when user wants to get raw format of +// a response instead of JSON. +type RawOptions struct { + Type RawType +} + +// addOptions adds the parameters in opt as URL query parameters to s. opt +// must be a struct whose fields may contain "url" tags. +func addOptions(s string, opt interface{}) (string, error) { + v := reflect.ValueOf(opt) + if v.Kind() == reflect.Ptr && v.IsNil() { + return s, nil + } + + u, err := url.Parse(s) + if err != nil { + return s, err + } + + qs, err := query.Values(opt) + if err != nil { + return s, err + } + + u.RawQuery = qs.Encode() + return u.String(), nil +} + +// NewClient returns a new GitHub API client. If a nil httpClient is +// provided, http.DefaultClient will be used. To use API methods which require +// authentication, provide an http.Client that will perform the authentication +// for you (such as that provided by the golang.org/x/oauth2 library). +func NewClient(httpClient *http.Client) *Client { + if httpClient == nil { + httpClient = http.DefaultClient + } + baseURL, _ := url.Parse(defaultBaseURL) + uploadURL, _ := url.Parse(uploadBaseURL) + + c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL} + c.common.client = c + c.Activity = (*ActivityService)(&c.common) + c.Admin = (*AdminService)(&c.common) + c.Apps = (*AppsService)(&c.common) + c.Authorizations = (*AuthorizationsService)(&c.common) + c.Gists = (*GistsService)(&c.common) + c.Git = (*GitService)(&c.common) + c.Gitignores = (*GitignoresService)(&c.common) + c.Issues = (*IssuesService)(&c.common) + c.Licenses = (*LicensesService)(&c.common) + c.Marketplace = &MarketplaceService{client: c} + c.Migrations = (*MigrationService)(&c.common) + c.Organizations = (*OrganizationsService)(&c.common) + c.Projects = (*ProjectsService)(&c.common) + c.PullRequests = (*PullRequestsService)(&c.common) + c.Reactions = (*ReactionsService)(&c.common) + c.Repositories = (*RepositoriesService)(&c.common) + c.Search = (*SearchService)(&c.common) + c.Users = (*UsersService)(&c.common) + return c +} + +// NewEnterpriseClient returns a new GitHub API client with provided +// base URL and upload URL (often the same URL). +// If either URL does not have a trailing slash, one is added automatically. +// If a nil httpClient is provided, http.DefaultClient will be used. +// +// Note that NewEnterpriseClient is a convenience helper only; +// its behavior is equivalent to using NewClient, followed by setting +// the BaseURL and UploadURL fields. +func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*Client, error) { + baseEndpoint, err := url.Parse(baseURL) + if err != nil { + return nil, err + } + if !strings.HasSuffix(baseEndpoint.Path, "/") { + baseEndpoint.Path += "/" + } + + uploadEndpoint, err := url.Parse(uploadURL) + if err != nil { + return nil, err + } + if !strings.HasSuffix(uploadEndpoint.Path, "/") { + uploadEndpoint.Path += "/" + } + + c := NewClient(httpClient) + c.BaseURL = baseEndpoint + c.UploadURL = uploadEndpoint + return c, nil +} + +// NewRequest creates an API request. A relative URL can be provided in urlStr, +// in which case it is resolved relative to the BaseURL of the Client. +// Relative URLs should always be specified without a preceding slash. If +// specified, the value pointed to by body is JSON encoded and included as the +// request body. +func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { + if !strings.HasSuffix(c.BaseURL.Path, "/") { + return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) + } + u, err := c.BaseURL.Parse(urlStr) + if err != nil { + return nil, err + } + + var buf io.ReadWriter + if body != nil { + buf = new(bytes.Buffer) + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + err := enc.Encode(body) + if err != nil { + return nil, err + } + } + + req, err := http.NewRequest(method, u.String(), buf) + if err != nil { + return nil, err + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", mediaTypeV3) + if c.UserAgent != "" { + req.Header.Set("User-Agent", c.UserAgent) + } + return req, nil +} + +// NewUploadRequest creates an upload request. A relative URL can be provided in +// urlStr, in which case it is resolved relative to the UploadURL of the Client. +// Relative URLs should always be specified without a preceding slash. +func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { + if !strings.HasSuffix(c.UploadURL.Path, "/") { + return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL) + } + u, err := c.UploadURL.Parse(urlStr) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", u.String(), reader) + if err != nil { + return nil, err + } + req.ContentLength = size + + if mediaType == "" { + mediaType = defaultMediaType + } + req.Header.Set("Content-Type", mediaType) + req.Header.Set("Accept", mediaTypeV3) + req.Header.Set("User-Agent", c.UserAgent) + return req, nil +} + +// Response is a GitHub API response. This wraps the standard http.Response +// returned from GitHub and provides convenient access to things like +// pagination links. +type Response struct { + *http.Response + + // These fields provide the page values for paginating through a set of + // results. Any or all of these may be set to the zero value for + // responses that are not part of a paginated set, or for which there + // are no additional pages. + + NextPage int + PrevPage int + FirstPage int + LastPage int + + Rate +} + +// newResponse creates a new Response for the provided http.Response. +// r must not be nil. +func newResponse(r *http.Response) *Response { + response := &Response{Response: r} + response.populatePageValues() + response.Rate = parseRate(r) + return response +} + +// populatePageValues parses the HTTP Link response headers and populates the +// various pagination link values in the Response. +func (r *Response) populatePageValues() { + if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 { + for _, link := range strings.Split(links[0], ",") { + segments := strings.Split(strings.TrimSpace(link), ";") + + // link must at least have href and rel + if len(segments) < 2 { + continue + } + + // ensure href is properly formatted + if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") { + continue + } + + // try to pull out page parameter + url, err := url.Parse(segments[0][1 : len(segments[0])-1]) + if err != nil { + continue + } + page := url.Query().Get("page") + if page == "" { + continue + } + + for _, segment := range segments[1:] { + switch strings.TrimSpace(segment) { + case `rel="next"`: + r.NextPage, _ = strconv.Atoi(page) + case `rel="prev"`: + r.PrevPage, _ = strconv.Atoi(page) + case `rel="first"`: + r.FirstPage, _ = strconv.Atoi(page) + case `rel="last"`: + r.LastPage, _ = strconv.Atoi(page) + } + + } + } + } +} + +// parseRate parses the rate related headers. +func parseRate(r *http.Response) Rate { + var rate Rate + if limit := r.Header.Get(headerRateLimit); limit != "" { + rate.Limit, _ = strconv.Atoi(limit) + } + if remaining := r.Header.Get(headerRateRemaining); remaining != "" { + rate.Remaining, _ = strconv.Atoi(remaining) + } + if reset := r.Header.Get(headerRateReset); reset != "" { + if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 { + rate.Reset = Timestamp{time.Unix(v, 0)} + } + } + return rate +} + +// Do sends an API request and returns the API response. The API response is +// JSON decoded and stored in the value pointed to by v, or returned as an +// error if an API error has occurred. If v implements the io.Writer +// interface, the raw response body will be written to v, without attempting to +// first decode it. If rate limit is exceeded and reset time is in the future, +// Do returns *RateLimitError immediately without making a network API call. +// +// The provided ctx must be non-nil. If it is canceled or times out, +// ctx.Err() will be returned. +func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) { + req = withContext(ctx, req) + + rateLimitCategory := category(req.URL.Path) + + // If we've hit rate limit, don't make further requests before Reset time. + if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil { + return &Response{ + Response: err.Response, + Rate: err.Rate, + }, err + } + + resp, err := c.client.Do(req) + if err != nil { + // If we got an error, and the context has been canceled, + // the context's error is probably more useful. + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + // If the error type is *url.Error, sanitize its URL before returning. + if e, ok := err.(*url.Error); ok { + if url, err := url.Parse(e.URL); err == nil { + e.URL = sanitizeURL(url).String() + return nil, e + } + } + + return nil, err + } + + defer func() { + // Drain up to 512 bytes and close the body to let the Transport reuse the connection + io.CopyN(ioutil.Discard, resp.Body, 512) + resp.Body.Close() + }() + + response := newResponse(resp) + + c.rateMu.Lock() + c.rateLimits[rateLimitCategory] = response.Rate + c.rateMu.Unlock() + + err = CheckResponse(resp) + if err != nil { + // even though there was an error, we still return the response + // in case the caller wants to inspect it further + return response, err + } + + if v != nil { + if w, ok := v.(io.Writer); ok { + io.Copy(w, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(v) + if err == io.EOF { + err = nil // ignore EOF errors caused by empty response body + } + } + } + + return response, err +} + +// checkRateLimitBeforeDo does not make any network calls, but uses existing knowledge from +// current client state in order to quickly check if *RateLimitError can be immediately returned +// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unnecessarily. +// Otherwise it returns nil, and Client.Do should proceed normally. +func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) *RateLimitError { + c.rateMu.Lock() + rate := c.rateLimits[rateLimitCategory] + c.rateMu.Unlock() + if !rate.Reset.Time.IsZero() && rate.Remaining == 0 && time.Now().Before(rate.Reset.Time) { + // Create a fake response. + resp := &http.Response{ + Status: http.StatusText(http.StatusForbidden), + StatusCode: http.StatusForbidden, + Request: req, + Header: make(http.Header), + Body: ioutil.NopCloser(strings.NewReader("")), + } + return &RateLimitError{ + Rate: rate, + Response: resp, + Message: fmt.Sprintf("API rate limit of %v still exceeded until %v, not making remote request.", rate.Limit, rate.Reset.Time), + } + } + + return nil +} + +/* +An ErrorResponse reports one or more errors caused by an API request. + +GitHub API docs: https://developer.github.com/v3/#client-errors +*/ +type ErrorResponse struct { + Response *http.Response // HTTP response that caused this error + Message string `json:"message"` // error message + Errors []Error `json:"errors"` // more detail on individual errors + // Block is only populated on certain types of errors such as code 451. + // See https://developer.github.com/changes/2016-03-17-the-451-status-code-is-now-supported/ + // for more information. + Block *struct { + Reason string `json:"reason,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + } `json:"block,omitempty"` + // Most errors will also include a documentation_url field pointing + // to some content that might help you resolve the error, see + // https://developer.github.com/v3/#client-errors + DocumentationURL string `json:"documentation_url,omitempty"` +} + +func (r *ErrorResponse) Error() string { + return fmt.Sprintf("%v %v: %d %v %+v", + r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), + r.Response.StatusCode, r.Message, r.Errors) +} + +// TwoFactorAuthError occurs when using HTTP Basic Authentication for a user +// that has two-factor authentication enabled. The request can be reattempted +// by providing a one-time password in the request. +type TwoFactorAuthError ErrorResponse + +func (r *TwoFactorAuthError) Error() string { return (*ErrorResponse)(r).Error() } + +// RateLimitError occurs when GitHub returns 403 Forbidden response with a rate limit +// remaining value of 0, and error message starts with "API rate limit exceeded for ". +type RateLimitError struct { + Rate Rate // Rate specifies last known rate limit for the client + Response *http.Response // HTTP response that caused this error + Message string `json:"message"` // error message +} + +func (r *RateLimitError) Error() string { + return fmt.Sprintf("%v %v: %d %v %v", + r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), + r.Response.StatusCode, r.Message, formatRateReset(r.Rate.Reset.Time.Sub(time.Now()))) +} + +// AcceptedError occurs when GitHub returns 202 Accepted response with an +// empty body, which means a job was scheduled on the GitHub side to process +// the information needed and cache it. +// Technically, 202 Accepted is not a real error, it's just used to +// indicate that results are not ready yet, but should be available soon. +// The request can be repeated after some time. +type AcceptedError struct{} + +func (*AcceptedError) Error() string { + return "job scheduled on GitHub side; try again later" +} + +// AbuseRateLimitError occurs when GitHub returns 403 Forbidden response with the +// "documentation_url" field value equal to "https://developer.github.com/v3/#abuse-rate-limits". +type AbuseRateLimitError struct { + Response *http.Response // HTTP response that caused this error + Message string `json:"message"` // error message + + // RetryAfter is provided with some abuse rate limit errors. If present, + // it is the amount of time that the client should wait before retrying. + // Otherwise, the client should try again later (after an unspecified amount of time). + RetryAfter *time.Duration +} + +func (r *AbuseRateLimitError) Error() string { + return fmt.Sprintf("%v %v: %d %v", + r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), + r.Response.StatusCode, r.Message) +} + +// sanitizeURL redacts the client_secret parameter from the URL which may be +// exposed to the user. +func sanitizeURL(uri *url.URL) *url.URL { + if uri == nil { + return nil + } + params := uri.Query() + if len(params.Get("client_secret")) > 0 { + params.Set("client_secret", "REDACTED") + uri.RawQuery = params.Encode() + } + return uri +} + +/* +An Error reports more details on an individual error in an ErrorResponse. +These are the possible validation error codes: + + missing: + resource does not exist + missing_field: + a required field on a resource has not been set + invalid: + the formatting of a field is invalid + already_exists: + another resource has the same valid as this field + custom: + some resources return this (e.g. github.User.CreateKey()), additional + information is set in the Message field of the Error + +GitHub API docs: https://developer.github.com/v3/#client-errors +*/ +type Error struct { + Resource string `json:"resource"` // resource on which the error occurred + Field string `json:"field"` // field on which the error occurred + Code string `json:"code"` // validation error code + Message string `json:"message"` // Message describing the error. Errors with Code == "custom" will always have this set. +} + +func (e *Error) Error() string { + return fmt.Sprintf("%v error caused by %v field on %v resource", + e.Code, e.Field, e.Resource) +} + +// CheckResponse checks the API response for errors, and returns them if +// present. A response is considered an error if it has a status code outside +// the 200 range or equal to 202 Accepted. +// API error responses are expected to have either no response +// body, or a JSON response body that maps to ErrorResponse. Any other +// response body will be silently ignored. +// +// The error type will be *RateLimitError for rate limit exceeded errors, +// *AcceptedError for 202 Accepted status codes, +// and *TwoFactorAuthError for two-factor authentication errors. +func CheckResponse(r *http.Response) error { + if r.StatusCode == http.StatusAccepted { + return &AcceptedError{} + } + if c := r.StatusCode; 200 <= c && c <= 299 { + return nil + } + errorResponse := &ErrorResponse{Response: r} + data, err := ioutil.ReadAll(r.Body) + if err == nil && data != nil { + json.Unmarshal(data, errorResponse) + } + switch { + case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"): + return (*TwoFactorAuthError)(errorResponse) + case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0" && strings.HasPrefix(errorResponse.Message, "API rate limit exceeded for "): + return &RateLimitError{ + Rate: parseRate(r), + Response: errorResponse.Response, + Message: errorResponse.Message, + } + case r.StatusCode == http.StatusForbidden && errorResponse.DocumentationURL == "https://developer.github.com/v3/#abuse-rate-limits": + abuseRateLimitError := &AbuseRateLimitError{ + Response: errorResponse.Response, + Message: errorResponse.Message, + } + if v := r.Header["Retry-After"]; len(v) > 0 { + // According to GitHub support, the "Retry-After" header value will be + // an integer which represents the number of seconds that one should + // wait before resuming making requests. + retryAfterSeconds, _ := strconv.ParseInt(v[0], 10, 64) // Error handling is noop. + retryAfter := time.Duration(retryAfterSeconds) * time.Second + abuseRateLimitError.RetryAfter = &retryAfter + } + return abuseRateLimitError + default: + return errorResponse + } +} + +// parseBoolResponse determines the boolean result from a GitHub API response. +// Several GitHub API methods return boolean responses indicated by the HTTP +// status code in the response (true indicated by a 204, false indicated by a +// 404). This helper function will determine that result and hide the 404 +// error if present. Any other error will be returned through as-is. +func parseBoolResponse(err error) (bool, error) { + if err == nil { + return true, nil + } + + if err, ok := err.(*ErrorResponse); ok && err.Response.StatusCode == http.StatusNotFound { + // Simply false. In this one case, we do not pass the error through. + return false, nil + } + + // some other real error occurred + return false, err +} + +// Rate represents the rate limit for the current client. +type Rate struct { + // The number of requests per hour the client is currently limited to. + Limit int `json:"limit"` + + // The number of remaining requests the client can make this hour. + Remaining int `json:"remaining"` + + // The time at which the current rate limit will reset. + Reset Timestamp `json:"reset"` +} + +func (r Rate) String() string { + return Stringify(r) +} + +// RateLimits represents the rate limits for the current client. +type RateLimits struct { + // The rate limit for non-search API requests. Unauthenticated + // requests are limited to 60 per hour. Authenticated requests are + // limited to 5,000 per hour. + // + // GitHub API docs: https://developer.github.com/v3/#rate-limiting + Core *Rate `json:"core"` + + // The rate limit for search API requests. Unauthenticated requests + // are limited to 10 requests per minutes. Authenticated requests are + // limited to 30 per minute. + // + // GitHub API docs: https://developer.github.com/v3/search/#rate-limit + Search *Rate `json:"search"` +} + +func (r RateLimits) String() string { + return Stringify(r) +} + +type rateLimitCategory uint8 + +const ( + coreCategory rateLimitCategory = iota + searchCategory + + categories // An array of this length will be able to contain all rate limit categories. +) + +// category returns the rate limit category of the endpoint, determined by Request.URL.Path. +func category(path string) rateLimitCategory { + switch { + default: + return coreCategory + case strings.HasPrefix(path, "/search/"): + return searchCategory + } +} + +// RateLimits returns the rate limits for the current client. +func (c *Client) RateLimits(ctx context.Context) (*RateLimits, *Response, error) { + req, err := c.NewRequest("GET", "rate_limit", nil) + if err != nil { + return nil, nil, err + } + + response := new(struct { + Resources *RateLimits `json:"resources"` + }) + resp, err := c.Do(ctx, req, response) + if err != nil { + return nil, nil, err + } + + if response.Resources != nil { + c.rateMu.Lock() + if response.Resources.Core != nil { + c.rateLimits[coreCategory] = *response.Resources.Core + } + if response.Resources.Search != nil { + c.rateLimits[searchCategory] = *response.Resources.Search + } + c.rateMu.Unlock() + } + + return response.Resources, resp, nil +} + +/* +UnauthenticatedRateLimitedTransport allows you to make unauthenticated calls +that need to use a higher rate limit associated with your OAuth application. + + t := &github.UnauthenticatedRateLimitedTransport{ + ClientID: "your app's client ID", + ClientSecret: "your app's client secret", + } + client := github.NewClient(t.Client()) + +This will append the querystring params client_id=xxx&client_secret=yyy to all +requests. + +See https://developer.github.com/v3/#unauthenticated-rate-limited-requests for +more information. +*/ +type UnauthenticatedRateLimitedTransport struct { + // ClientID is the GitHub OAuth client ID of the current application, which + // can be found by selecting its entry in the list at + // https://github.com/settings/applications. + ClientID string + + // ClientSecret is the GitHub OAuth client secret of the current + // application. + ClientSecret string + + // Transport is the underlying HTTP transport to use when making requests. + // It will default to http.DefaultTransport if nil. + Transport http.RoundTripper +} + +// RoundTrip implements the RoundTripper interface. +func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*http.Response, error) { + if t.ClientID == "" { + return nil, errors.New("t.ClientID is empty") + } + if t.ClientSecret == "" { + return nil, errors.New("t.ClientSecret is empty") + } + + // To set extra querystring params, we must make a copy of the Request so + // that we don't modify the Request we were given. This is required by the + // specification of http.RoundTripper. + // + // Since we are going to modify only req.URL here, we only need a deep copy + // of req.URL. + req2 := new(http.Request) + *req2 = *req + req2.URL = new(url.URL) + *req2.URL = *req.URL + + q := req2.URL.Query() + q.Set("client_id", t.ClientID) + q.Set("client_secret", t.ClientSecret) + req2.URL.RawQuery = q.Encode() + + // Make the HTTP request. + return t.transport().RoundTrip(req2) +} + +// Client returns an *http.Client that makes requests which are subject to the +// rate limit of your OAuth application. +func (t *UnauthenticatedRateLimitedTransport) Client() *http.Client { + return &http.Client{Transport: t} +} + +func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper { + if t.Transport != nil { + return t.Transport + } + return http.DefaultTransport +} + +// BasicAuthTransport is an http.RoundTripper that authenticates all requests +// using HTTP Basic Authentication with the provided username and password. It +// additionally supports users who have two-factor authentication enabled on +// their GitHub account. +type BasicAuthTransport struct { + Username string // GitHub username + Password string // GitHub password + OTP string // one-time password for users with two-factor auth enabled + + // Transport is the underlying HTTP transport to use when making requests. + // It will default to http.DefaultTransport if nil. + Transport http.RoundTripper +} + +// RoundTrip implements the RoundTripper interface. +func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { + // To set extra headers, we must make a copy of the Request so + // that we don't modify the Request we were given. This is required by the + // specification of http.RoundTripper. + // + // Since we are going to modify only req.Header here, we only need a deep copy + // of req.Header. + req2 := new(http.Request) + *req2 = *req + req2.Header = make(http.Header, len(req.Header)) + for k, s := range req.Header { + req2.Header[k] = append([]string(nil), s...) + } + + req2.SetBasicAuth(t.Username, t.Password) + if t.OTP != "" { + req2.Header.Set(headerOTP, t.OTP) + } + return t.transport().RoundTrip(req2) +} + +// Client returns an *http.Client that makes requests that are authenticated +// using HTTP Basic Authentication. +func (t *BasicAuthTransport) Client() *http.Client { + return &http.Client{Transport: t} +} + +func (t *BasicAuthTransport) transport() http.RoundTripper { + if t.Transport != nil { + return t.Transport + } + return http.DefaultTransport +} + +// formatRateReset formats d to look like "[rate reset in 2s]" or +// "[rate reset in 87m02s]" for the positive durations. And like "[rate limit was reset 87m02s ago]" +// for the negative cases. +func formatRateReset(d time.Duration) string { + isNegative := d < 0 + if isNegative { + d *= -1 + } + secondsTotal := int(0.5 + d.Seconds()) + minutes := secondsTotal / 60 + seconds := secondsTotal - minutes*60 + + var timeString string + if minutes > 0 { + timeString = fmt.Sprintf("%dm%02ds", minutes, seconds) + } else { + timeString = fmt.Sprintf("%ds", seconds) + } + + if isNegative { + return fmt.Sprintf("[rate limit was reset %v ago]", timeString) + } + return fmt.Sprintf("[rate reset in %v]", timeString) +} + +// Bool is a helper routine that allocates a new bool value +// to store v and returns a pointer to it. +func Bool(v bool) *bool { return &v } + +// Int is a helper routine that allocates a new int value +// to store v and returns a pointer to it. +func Int(v int) *int { return &v } + +// Int64 is a helper routine that allocates a new int64 value +// to store v and returns a pointer to it. +func Int64(v int64) *int64 { return &v } + +// String is a helper routine that allocates a new string value +// to store v and returns a pointer to it. +func String(v string) *string { return &v } diff --git a/vendor/github.com/google/go-github/github/github_test.go b/vendor/github.com/google/go-github/github/github_test.go new file mode 100644 index 00000000..cb4b002a --- /dev/null +++ b/vendor/github.com/google/go-github/github/github_test.go @@ -0,0 +1,1049 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path" + "reflect" + "strings" + "testing" + "time" +) + +const ( + // baseURLPath is a non-empty Client.BaseURL path to use during tests, + // to ensure relative URLs are used for all endpoints. See issue #752. + baseURLPath = "/api-v3" +) + +// setup sets up a test HTTP server along with a github.Client that is +// configured to talk to that test server. Tests should register handlers on +// mux which provide mock responses for the API method being tested. +func setup() (client *Client, mux *http.ServeMux, serverURL string, teardown func()) { + // mux is the HTTP request multiplexer used with the test server. + mux = http.NewServeMux() + + // We want to ensure that tests catch mistakes where the endpoint URL is + // specified as absolute rather than relative. It only makes a difference + // when there's a non-empty base URL path. So, use that. See issue #752. + apiHandler := http.NewServeMux() + apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux)) + apiHandler.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprintln(os.Stderr, "FAIL: Client.BaseURL path prefix is not preserved in the request URL:") + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "\t"+req.URL.String()) + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "\tDid you accidentally use an absolute endpoint URL rather than relative?") + fmt.Fprintln(os.Stderr, "\tSee https://github.com/google/go-github/issues/752 for information.") + http.Error(w, "Client.BaseURL path prefix is not preserved in the request URL.", http.StatusInternalServerError) + }) + + // server is a test HTTP server used to provide mock API responses. + server := httptest.NewServer(apiHandler) + + // client is the GitHub client being tested and is + // configured to use test server. + client = NewClient(nil) + url, _ := url.Parse(server.URL + baseURLPath + "/") + client.BaseURL = url + client.UploadURL = url + + return client, mux, server.URL, server.Close +} + +// openTestFile creates a new file with the given name and content for testing. +// In order to ensure the exact file name, this function will create a new temp +// directory, and create the file in that directory. It is the caller's +// responsibility to remove the directory and its contents when no longer needed. +func openTestFile(name, content string) (file *os.File, dir string, err error) { + dir, err = ioutil.TempDir("", "go-github") + if err != nil { + return nil, dir, err + } + + file, err = os.OpenFile(path.Join(dir, name), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if err != nil { + return nil, dir, err + } + + fmt.Fprint(file, content) + + // close and re-open the file to keep file.Stat() happy + file.Close() + file, err = os.Open(file.Name()) + if err != nil { + return nil, dir, err + } + + return file, dir, err +} + +func testMethod(t *testing.T, r *http.Request, want string) { + if got := r.Method; got != want { + t.Errorf("Request method: %v, want %v", got, want) + } +} + +type values map[string]string + +func testFormValues(t *testing.T, r *http.Request, values values) { + want := url.Values{} + for k, v := range values { + want.Set(k, v) + } + + r.ParseForm() + if got := r.Form; !reflect.DeepEqual(got, want) { + t.Errorf("Request parameters: %v, want %v", got, want) + } +} + +func testHeader(t *testing.T, r *http.Request, header string, want string) { + if got := r.Header.Get(header); got != want { + t.Errorf("Header.Get(%q) returned %q, want %q", header, got, want) + } +} + +func testURLParseError(t *testing.T, err error) { + if err == nil { + t.Errorf("Expected error to be returned") + } + if err, ok := err.(*url.Error); !ok || err.Op != "parse" { + t.Errorf("Expected URL parse error, got %+v", err) + } +} + +func testBody(t *testing.T, r *http.Request, want string) { + b, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Error reading request body: %v", err) + } + if got := string(b); got != want { + t.Errorf("request Body is %s, want %s", got, want) + } +} + +// Helper function to test that a value is marshalled to JSON as expected. +func testJSONMarshal(t *testing.T, v interface{}, want string) { + j, err := json.Marshal(v) + if err != nil { + t.Errorf("Unable to marshal JSON for %v", v) + } + + w := new(bytes.Buffer) + err = json.Compact(w, []byte(want)) + if err != nil { + t.Errorf("String is not valid json: %s", want) + } + + if w.String() != string(j) { + t.Errorf("json.Marshal(%q) returned %s, want %s", v, j, w) + } + + // now go the other direction and make sure things unmarshal as expected + u := reflect.ValueOf(v).Interface() + if err := json.Unmarshal([]byte(want), u); err != nil { + t.Errorf("Unable to unmarshal JSON for %v", want) + } + + if !reflect.DeepEqual(v, u) { + t.Errorf("json.Unmarshal(%q) returned %s, want %s", want, u, v) + } +} + +func TestNewClient(t *testing.T) { + c := NewClient(nil) + + if got, want := c.BaseURL.String(), defaultBaseURL; got != want { + t.Errorf("NewClient BaseURL is %v, want %v", got, want) + } + if got, want := c.UserAgent, userAgent; got != want { + t.Errorf("NewClient UserAgent is %v, want %v", got, want) + } +} + +func TestNewEnterpriseClient(t *testing.T) { + baseURL := "https://custom-url/" + uploadURL := "https://custom-upload-url/" + c, err := NewEnterpriseClient(baseURL, uploadURL, nil) + if err != nil { + t.Fatalf("NewEnterpriseClient returned unexpected error: %v", err) + } + + if got, want := c.BaseURL.String(), baseURL; got != want { + t.Errorf("NewClient BaseURL is %v, want %v", got, want) + } + if got, want := c.UploadURL.String(), uploadURL; got != want { + t.Errorf("NewClient UploadURL is %v, want %v", got, want) + } +} + +func TestNewEnterpriseClient_addsTrailingSlashToURLs(t *testing.T) { + baseURL := "https://custom-url" + uploadURL := "https://custom-upload-url" + formattedBaseURL := baseURL + "/" + formattedUploadURL := uploadURL + "/" + + c, err := NewEnterpriseClient(baseURL, uploadURL, nil) + if err != nil { + t.Fatalf("NewEnterpriseClient returned unexpected error: %v", err) + } + + if got, want := c.BaseURL.String(), formattedBaseURL; got != want { + t.Errorf("NewClient BaseURL is %v, want %v", got, want) + } + if got, want := c.UploadURL.String(), formattedUploadURL; got != want { + t.Errorf("NewClient UploadURL is %v, want %v", got, want) + } +} + +// Ensure that length of Client.rateLimits is the same as number of fields in RateLimits struct. +func TestClient_rateLimits(t *testing.T) { + if got, want := len(Client{}.rateLimits), reflect.TypeOf(RateLimits{}).NumField(); got != want { + t.Errorf("len(Client{}.rateLimits) is %v, want %v", got, want) + } +} + +func TestNewRequest(t *testing.T) { + c := NewClient(nil) + + inURL, outURL := "/foo", defaultBaseURL+"foo" + inBody, outBody := &User{Login: String("l")}, `{"login":"l"}`+"\n" + req, _ := c.NewRequest("GET", inURL, inBody) + + // test that relative URL was expanded + if got, want := req.URL.String(), outURL; got != want { + t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want) + } + + // test that body was JSON encoded + body, _ := ioutil.ReadAll(req.Body) + if got, want := string(body), outBody; got != want { + t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want) + } + + // test that default user-agent is attached to the request + if got, want := req.Header.Get("User-Agent"), c.UserAgent; got != want { + t.Errorf("NewRequest() User-Agent is %v, want %v", got, want) + } +} + +func TestNewRequest_invalidJSON(t *testing.T) { + c := NewClient(nil) + + type T struct { + A map[interface{}]interface{} + } + _, err := c.NewRequest("GET", ".", &T{}) + + if err == nil { + t.Error("Expected error to be returned.") + } + if err, ok := err.(*json.UnsupportedTypeError); !ok { + t.Errorf("Expected a JSON error; got %#v.", err) + } +} + +func TestNewRequest_badURL(t *testing.T) { + c := NewClient(nil) + _, err := c.NewRequest("GET", ":", nil) + testURLParseError(t, err) +} + +// ensure that no User-Agent header is set if the client's UserAgent is empty. +// This caused a problem with Google's internal http client. +func TestNewRequest_emptyUserAgent(t *testing.T) { + c := NewClient(nil) + c.UserAgent = "" + req, err := c.NewRequest("GET", ".", nil) + if err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + if _, ok := req.Header["User-Agent"]; ok { + t.Fatal("constructed request contains unexpected User-Agent header") + } +} + +// If a nil body is passed to github.NewRequest, make sure that nil is also +// passed to http.NewRequest. In most cases, passing an io.Reader that returns +// no content is fine, since there is no difference between an HTTP request +// body that is an empty string versus one that is not set at all. However in +// certain cases, intermediate systems may treat these differently resulting in +// subtle errors. +func TestNewRequest_emptyBody(t *testing.T) { + c := NewClient(nil) + req, err := c.NewRequest("GET", ".", nil) + if err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + if req.Body != nil { + t.Fatalf("constructed request contains a non-nil Body") + } +} + +func TestNewRequest_errorForNoTrailingSlash(t *testing.T) { + tests := []struct { + rawurl string + wantError bool + }{ + {rawurl: "https://example.com/api/v3", wantError: true}, + {rawurl: "https://example.com/api/v3/", wantError: false}, + } + c := NewClient(nil) + for _, test := range tests { + u, err := url.Parse(test.rawurl) + if err != nil { + t.Fatalf("url.Parse returned unexpected error: %v.", err) + } + c.BaseURL = u + if _, err := c.NewRequest(http.MethodGet, "test", nil); test.wantError && err == nil { + t.Fatalf("Expected error to be returned.") + } else if !test.wantError && err != nil { + t.Fatalf("NewRequest returned unexpected error: %v.", err) + } + } +} + +func TestNewUploadRequest_errorForNoTrailingSlash(t *testing.T) { + tests := []struct { + rawurl string + wantError bool + }{ + {rawurl: "https://example.com/api/uploads", wantError: true}, + {rawurl: "https://example.com/api/uploads/", wantError: false}, + } + c := NewClient(nil) + for _, test := range tests { + u, err := url.Parse(test.rawurl) + if err != nil { + t.Fatalf("url.Parse returned unexpected error: %v.", err) + } + c.UploadURL = u + if _, err = c.NewUploadRequest("test", nil, 0, ""); test.wantError && err == nil { + t.Fatalf("Expected error to be returned.") + } else if !test.wantError && err != nil { + t.Fatalf("NewUploadRequest returned unexpected error: %v.", err) + } + } +} + +func TestResponse_populatePageValues(t *testing.T) { + r := http.Response{ + Header: http.Header{ + "Link": {`; rel="first",` + + ` ; rel="prev",` + + ` ; rel="next",` + + ` ; rel="last"`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 1; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 2; want != got { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 4; want != got { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 5; want != got { + t.Errorf("response.LastPage: %v, want %v", got, want) + } +} + +func TestResponse_populatePageValues_invalid(t *testing.T) { + r := http.Response{ + Header: http.Header{ + "Link": {`,` + + `; rel="first",` + + `https://api.github.com/?page=2; rel="prev",` + + `; rel="next",` + + `; rel="last"`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 0; got != want { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 0; got != want { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 0; got != want { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + + // more invalid URLs + r = http.Response{ + Header: http.Header{ + "Link": {`; rel="first"`}, + }, + } + + response = newResponse(&r) + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } +} + +func TestDo(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + type foo struct { + A string + } + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"A":"a"}`) + }) + + req, _ := client.NewRequest("GET", ".", nil) + body := new(foo) + client.Do(context.Background(), req, body) + + want := &foo{"a"} + if !reflect.DeepEqual(body, want) { + t.Errorf("Response body = %v, want %v", body, want) + } +} + +func TestDo_httpError(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "Bad Request", 400) + }) + + req, _ := client.NewRequest("GET", ".", nil) + resp, err := client.Do(context.Background(), req, nil) + + if err == nil { + t.Fatal("Expected HTTP 400 error, got no error.") + } + if resp.StatusCode != 400 { + t.Errorf("Expected HTTP 400 error, got %d status code.", resp.StatusCode) + } +} + +// Test handling of an error caused by the internal http client's Do() +// function. A redirect loop is pretty unlikely to occur within the GitHub +// API, but does allow us to exercise the right code path. +func TestDo_redirectLoop(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, baseURLPath, http.StatusFound) + }) + + req, _ := client.NewRequest("GET", ".", nil) + _, err := client.Do(context.Background(), req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + if err, ok := err.(*url.Error); !ok { + t.Errorf("Expected a URL error; got %#v.", err) + } +} + +// Test that an error caused by the internal http client's Do() function +// does not leak the client secret. +func TestDo_sanitizeURL(t *testing.T) { + tp := &UnauthenticatedRateLimitedTransport{ + ClientID: "id", + ClientSecret: "secret", + } + unauthedClient := NewClient(tp.Client()) + unauthedClient.BaseURL = &url.URL{Scheme: "http", Host: "127.0.0.1:0", Path: "/"} // Use port 0 on purpose to trigger a dial TCP error, expect to get "dial tcp 127.0.0.1:0: connect: can't assign requested address". + req, err := unauthedClient.NewRequest("GET", ".", nil) + if err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + _, err = unauthedClient.Do(context.Background(), req, nil) + if err == nil { + t.Fatal("Expected error to be returned.") + } + if strings.Contains(err.Error(), "client_secret=secret") { + t.Errorf("Do error contains secret, should be redacted:\n%q", err) + } +} + +func TestDo_rateLimit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set(headerRateLimit, "60") + w.Header().Set(headerRateRemaining, "59") + w.Header().Set(headerRateReset, "1372700873") + }) + + req, _ := client.NewRequest("GET", ".", nil) + resp, err := client.Do(context.Background(), req, nil) + if err != nil { + t.Errorf("Do returned unexpected error: %v", err) + } + if got, want := resp.Rate.Limit, 60; got != want { + t.Errorf("Client rate limit = %v, want %v", got, want) + } + if got, want := resp.Rate.Remaining, 59; got != want { + t.Errorf("Client rate remaining = %v, want %v", got, want) + } + reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) + if resp.Rate.Reset.UTC() != reset { + t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset, reset) + } +} + +// ensure rate limit is still parsed, even for error responses +func TestDo_rateLimit_errorResponse(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set(headerRateLimit, "60") + w.Header().Set(headerRateRemaining, "59") + w.Header().Set(headerRateReset, "1372700873") + http.Error(w, "Bad Request", 400) + }) + + req, _ := client.NewRequest("GET", ".", nil) + resp, err := client.Do(context.Background(), req, nil) + if err == nil { + t.Error("Expected error to be returned.") + } + if _, ok := err.(*RateLimitError); ok { + t.Errorf("Did not expect a *RateLimitError error; got %#v.", err) + } + if got, want := resp.Rate.Limit, 60; got != want { + t.Errorf("Client rate limit = %v, want %v", got, want) + } + if got, want := resp.Rate.Remaining, 59; got != want { + t.Errorf("Client rate remaining = %v, want %v", got, want) + } + reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) + if resp.Rate.Reset.UTC() != reset { + t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset, reset) + } +} + +// Ensure *RateLimitError is returned when API rate limit is exceeded. +func TestDo_rateLimit_rateLimitError(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set(headerRateLimit, "60") + w.Header().Set(headerRateRemaining, "0") + w.Header().Set(headerRateReset, "1372700873") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://developer.github.com/v3/#rate-limiting" +}`) + }) + + req, _ := client.NewRequest("GET", ".", nil) + _, err := client.Do(context.Background(), req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + rateLimitErr, ok := err.(*RateLimitError) + if !ok { + t.Fatalf("Expected a *RateLimitError error; got %#v.", err) + } + if got, want := rateLimitErr.Rate.Limit, 60; got != want { + t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) + } + if got, want := rateLimitErr.Rate.Remaining, 0; got != want { + t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) + } + reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) + if rateLimitErr.Rate.Reset.UTC() != reset { + t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset) + } +} + +// Ensure a network call is not made when it's known that API rate limit is still exceeded. +func TestDo_rateLimit_noNetworkCall(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + reset := time.Now().UTC().Add(time.Minute).Round(time.Second) // Rate reset is a minute from now, with 1 second precision. + + mux.HandleFunc("/first", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set(headerRateLimit, "60") + w.Header().Set(headerRateRemaining, "0") + w.Header().Set(headerRateReset, fmt.Sprint(reset.Unix())) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://developer.github.com/v3/#rate-limiting" +}`) + }) + + madeNetworkCall := false + mux.HandleFunc("/second", func(w http.ResponseWriter, r *http.Request) { + madeNetworkCall = true + }) + + // First request is made, and it makes the client aware of rate reset time being in the future. + req, _ := client.NewRequest("GET", "first", nil) + client.Do(context.Background(), req, nil) + + // Second request should not cause a network call to be made, since client can predict a rate limit error. + req, _ = client.NewRequest("GET", "second", nil) + _, err := client.Do(context.Background(), req, nil) + + if madeNetworkCall { + t.Fatal("Network call was made, even though rate limit is known to still be exceeded.") + } + + if err == nil { + t.Error("Expected error to be returned.") + } + rateLimitErr, ok := err.(*RateLimitError) + if !ok { + t.Fatalf("Expected a *RateLimitError error; got %#v.", err) + } + if got, want := rateLimitErr.Rate.Limit, 60; got != want { + t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) + } + if got, want := rateLimitErr.Rate.Remaining, 0; got != want { + t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) + } + if rateLimitErr.Rate.Reset.UTC() != reset { + t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset) + } +} + +// Ensure *AbuseRateLimitError is returned when the response indicates that +// the client has triggered an abuse detection mechanism. +func TestDo_rateLimit_abuseRateLimitError(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + // When the abuse rate limit error is of the "temporarily blocked from content creation" type, + // there is no "Retry-After" header. + fmt.Fprintln(w, `{ + "message": "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later.", + "documentation_url": "https://developer.github.com/v3/#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest("GET", ".", nil) + _, err := client.Do(context.Background(), req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + abuseRateLimitErr, ok := err.(*AbuseRateLimitError) + if !ok { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v.", err) + } + if got, want := abuseRateLimitErr.RetryAfter, (*time.Duration)(nil); got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } +} + +// Ensure *AbuseRateLimitError.RetryAfter is parsed correctly. +func TestDo_rateLimit_abuseRateLimitError_retryAfter(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Header().Set("Retry-After", "123") // Retry after value of 123 seconds. + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "You have triggered an abuse detection mechanism ...", + "documentation_url": "https://developer.github.com/v3/#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest("GET", ".", nil) + _, err := client.Do(context.Background(), req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + abuseRateLimitErr, ok := err.(*AbuseRateLimitError) + if !ok { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v.", err) + } + if abuseRateLimitErr.RetryAfter == nil { + t.Fatalf("abuseRateLimitErr RetryAfter is nil, expected not-nil") + } + if got, want := *abuseRateLimitErr.RetryAfter, 123*time.Second; got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } +} + +func TestDo_noContent(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNoContent) + }) + + var body json.RawMessage + + req, _ := client.NewRequest("GET", ".", nil) + _, err := client.Do(context.Background(), req, &body) + if err != nil { + t.Fatalf("Do returned unexpected error: %v", err) + } +} + +func TestSanitizeURL(t *testing.T) { + tests := []struct { + in, want string + }{ + {"/?a=b", "/?a=b"}, + {"/?a=b&client_secret=secret", "/?a=b&client_secret=REDACTED"}, + {"/?a=b&client_id=id&client_secret=secret", "/?a=b&client_id=id&client_secret=REDACTED"}, + } + + for _, tt := range tests { + inURL, _ := url.Parse(tt.in) + want, _ := url.Parse(tt.want) + + if got := sanitizeURL(inURL); !reflect.DeepEqual(got, want) { + t.Errorf("sanitizeURL(%v) returned %v, want %v", tt.in, got, want) + } + } +} + +func TestCheckResponse(t *testing.T) { + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusBadRequest, + Body: ioutil.NopCloser(strings.NewReader(`{"message":"m", + "errors": [{"resource": "r", "field": "f", "code": "c"}], + "block": {"reason": "dmca", "created_at": "2016-03-17T15:39:46Z"}}`)), + } + err := CheckResponse(res).(*ErrorResponse) + + if err == nil { + t.Errorf("Expected error response.") + } + + want := &ErrorResponse{ + Response: res, + Message: "m", + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Block: &struct { + Reason string `json:"reason,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + }{ + Reason: "dmca", + CreatedAt: &Timestamp{time.Date(2016, 3, 17, 15, 39, 46, 0, time.UTC)}, + }, + } + if !reflect.DeepEqual(err, want) { + t.Errorf("Error = %#v, want %#v", err, want) + } +} + +// ensure that we properly handle API errors that do not contain a response body +func TestCheckResponse_noBody(t *testing.T) { + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusBadRequest, + Body: ioutil.NopCloser(strings.NewReader("")), + } + err := CheckResponse(res).(*ErrorResponse) + + if err == nil { + t.Errorf("Expected error response.") + } + + want := &ErrorResponse{ + Response: res, + } + if !reflect.DeepEqual(err, want) { + t.Errorf("Error = %#v, want %#v", err, want) + } +} + +func TestParseBooleanResponse_true(t *testing.T) { + result, err := parseBoolResponse(nil) + if err != nil { + t.Errorf("parseBoolResponse returned error: %+v", err) + } + + if want := true; result != want { + t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) + } +} + +func TestParseBooleanResponse_false(t *testing.T) { + v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusNotFound}} + result, err := parseBoolResponse(v) + if err != nil { + t.Errorf("parseBoolResponse returned error: %+v", err) + } + + if want := false; result != want { + t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) + } +} + +func TestParseBooleanResponse_error(t *testing.T) { + v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusBadRequest}} + result, err := parseBoolResponse(v) + + if err == nil { + t.Errorf("Expected error to be returned.") + } + + if want := false; result != want { + t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) + } +} + +func TestErrorResponse_Error(t *testing.T) { + res := &http.Response{Request: &http.Request{}} + err := ErrorResponse{Message: "m", Response: res} + if err.Error() == "" { + t.Errorf("Expected non-empty ErrorResponse.Error()") + } +} + +func TestError_Error(t *testing.T) { + err := Error{} + if err.Error() == "" { + t.Errorf("Expected non-empty Error.Error()") + } +} + +func TestRateLimits(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"resources":{ + "core": {"limit":2,"remaining":1,"reset":1372700873}, + "search": {"limit":3,"remaining":2,"reset":1372700874} + }}`) + }) + + rate, _, err := client.RateLimits(context.Background()) + if err != nil { + t.Errorf("RateLimits returned error: %v", err) + } + + want := &RateLimits{ + Core: &Rate{ + Limit: 2, + Remaining: 1, + Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local()}, + }, + Search: &Rate{ + Limit: 3, + Remaining: 2, + Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 54, 0, time.UTC).Local()}, + }, + } + if !reflect.DeepEqual(rate, want) { + t.Errorf("RateLimits returned %+v, want %+v", rate, want) + } + + if got, want := client.rateLimits[coreCategory], *want.Core; got != want { + t.Errorf("client.rateLimits[coreCategory] is %+v, want %+v", got, want) + } + if got, want := client.rateLimits[searchCategory], *want.Search; got != want { + t.Errorf("client.rateLimits[searchCategory] is %+v, want %+v", got, want) + } +} + +func TestUnauthenticatedRateLimitedTransport(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + var v, want string + q := r.URL.Query() + if v, want = q.Get("client_id"), "id"; v != want { + t.Errorf("OAuth Client ID = %v, want %v", v, want) + } + if v, want = q.Get("client_secret"), "secret"; v != want { + t.Errorf("OAuth Client Secret = %v, want %v", v, want) + } + }) + + tp := &UnauthenticatedRateLimitedTransport{ + ClientID: "id", + ClientSecret: "secret", + } + unauthedClient := NewClient(tp.Client()) + unauthedClient.BaseURL = client.BaseURL + req, _ := unauthedClient.NewRequest("GET", ".", nil) + unauthedClient.Do(context.Background(), req, nil) +} + +func TestUnauthenticatedRateLimitedTransport_missingFields(t *testing.T) { + // missing ClientID + tp := &UnauthenticatedRateLimitedTransport{ + ClientSecret: "secret", + } + _, err := tp.RoundTrip(nil) + if err == nil { + t.Errorf("Expected error to be returned") + } + + // missing ClientSecret + tp = &UnauthenticatedRateLimitedTransport{ + ClientID: "id", + } + _, err = tp.RoundTrip(nil) + if err == nil { + t.Errorf("Expected error to be returned") + } +} + +func TestUnauthenticatedRateLimitedTransport_transport(t *testing.T) { + // default transport + tp := &UnauthenticatedRateLimitedTransport{ + ClientID: "id", + ClientSecret: "secret", + } + if tp.transport() != http.DefaultTransport { + t.Errorf("Expected http.DefaultTransport to be used.") + } + + // custom transport + tp = &UnauthenticatedRateLimitedTransport{ + ClientID: "id", + ClientSecret: "secret", + Transport: &http.Transport{}, + } + if tp.transport() == http.DefaultTransport { + t.Errorf("Expected custom transport to be used.") + } +} + +func TestBasicAuthTransport(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + username, password, otp := "u", "p", "123456" + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + u, p, ok := r.BasicAuth() + if !ok { + t.Errorf("request does not contain basic auth credentials") + } + if u != username { + t.Errorf("request contained basic auth username %q, want %q", u, username) + } + if p != password { + t.Errorf("request contained basic auth password %q, want %q", p, password) + } + if got, want := r.Header.Get(headerOTP), otp; got != want { + t.Errorf("request contained OTP %q, want %q", got, want) + } + }) + + tp := &BasicAuthTransport{ + Username: username, + Password: password, + OTP: otp, + } + basicAuthClient := NewClient(tp.Client()) + basicAuthClient.BaseURL = client.BaseURL + req, _ := basicAuthClient.NewRequest("GET", ".", nil) + basicAuthClient.Do(context.Background(), req, nil) +} + +func TestBasicAuthTransport_transport(t *testing.T) { + // default transport + tp := &BasicAuthTransport{} + if tp.transport() != http.DefaultTransport { + t.Errorf("Expected http.DefaultTransport to be used.") + } + + // custom transport + tp = &BasicAuthTransport{ + Transport: &http.Transport{}, + } + if tp.transport() == http.DefaultTransport { + t.Errorf("Expected custom transport to be used.") + } +} + +func TestFormatRateReset(t *testing.T) { + d := 120*time.Minute + 12*time.Second + got := formatRateReset(d) + want := "[rate reset in 120m12s]" + if got != want { + t.Errorf("Format is wrong. got: %v, want: %v", got, want) + } + + d = 14*time.Minute + 2*time.Second + got = formatRateReset(d) + want = "[rate reset in 14m02s]" + if got != want { + t.Errorf("Format is wrong. got: %v, want: %v", got, want) + } + + d = 2*time.Minute + 2*time.Second + got = formatRateReset(d) + want = "[rate reset in 2m02s]" + if got != want { + t.Errorf("Format is wrong. got: %v, want: %v", got, want) + } + + d = 12 * time.Second + got = formatRateReset(d) + want = "[rate reset in 12s]" + if got != want { + t.Errorf("Format is wrong. got: %v, want: %v", got, want) + } + + d = -1 * (2*time.Hour + 2*time.Second) + got = formatRateReset(d) + want = "[rate limit was reset 120m02s ago]" + if got != want { + t.Errorf("Format is wrong. got: %v, want: %v", got, want) + } +} + +func TestNestedStructAccessorNoPanic(t *testing.T) { + issue := &Issue{User: nil} + got := issue.GetUser().GetPlan().GetName() + want := "" + if got != want { + t.Errorf("Issues.Get.GetUser().GetPlan().GetName() returned %+v, want %+v", got, want) + } +} diff --git a/vendor/github.com/google/go-github/github/gitignore.go b/vendor/github.com/google/go-github/github/gitignore.go new file mode 100644 index 00000000..2f691bc3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/gitignore.go @@ -0,0 +1,64 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GitignoresService provides access to the gitignore related functions in the +// GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/gitignore/ +type GitignoresService service + +// Gitignore represents a .gitignore file as returned by the GitHub API. +type Gitignore struct { + Name *string `json:"name,omitempty"` + Source *string `json:"source,omitempty"` +} + +func (g Gitignore) String() string { + return Stringify(g) +} + +// List all available Gitignore templates. +// +// GitHub API docs: https://developer.github.com/v3/gitignore/#listing-available-templates +func (s GitignoresService) List(ctx context.Context) ([]string, *Response, error) { + req, err := s.client.NewRequest("GET", "gitignore/templates", nil) + if err != nil { + return nil, nil, err + } + + var availableTemplates []string + resp, err := s.client.Do(ctx, req, &availableTemplates) + if err != nil { + return nil, resp, err + } + + return availableTemplates, resp, nil +} + +// Get a Gitignore by name. +// +// GitHub API docs: https://developer.github.com/v3/gitignore/#get-a-single-template +func (s GitignoresService) Get(ctx context.Context, name string) (*Gitignore, *Response, error) { + u := fmt.Sprintf("gitignore/templates/%v", name) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + gitignore := new(Gitignore) + resp, err := s.client.Do(ctx, req, gitignore) + if err != nil { + return nil, resp, err + } + + return gitignore, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/gitignore_test.go b/vendor/github.com/google/go-github/github/gitignore_test.go new file mode 100644 index 00000000..cdb82237 --- /dev/null +++ b/vendor/github.com/google/go-github/github/gitignore_test.go @@ -0,0 +1,62 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestGitignoresService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gitignore/templates", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `["C", "Go"]`) + }) + + available, _, err := client.Gitignores.List(context.Background()) + if err != nil { + t.Errorf("Gitignores.List returned error: %v", err) + } + + want := []string{"C", "Go"} + if !reflect.DeepEqual(available, want) { + t.Errorf("Gitignores.List returned %+v, want %+v", available, want) + } +} + +func TestGitignoresService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/gitignore/templates/name", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"Name","source":"template source"}`) + }) + + gitignore, _, err := client.Gitignores.Get(context.Background(), "name") + if err != nil { + t.Errorf("Gitignores.List returned error: %v", err) + } + + want := &Gitignore{Name: String("Name"), Source: String("template source")} + if !reflect.DeepEqual(gitignore, want) { + t.Errorf("Gitignores.Get returned %+v, want %+v", gitignore, want) + } +} + +func TestGitignoresService_Get_invalidTemplate(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Gitignores.Get(context.Background(), "%") + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/issues.go b/vendor/github.com/google/go-github/github/issues.go new file mode 100644 index 00000000..f865ea20 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues.go @@ -0,0 +1,330 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "strings" + "time" +) + +// IssuesService handles communication with the issue related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/issues/ +type IssuesService service + +// Issue represents a GitHub issue on a repository. +// +// Note: As far as the GitHub API is concerned, every pull request is an issue, +// but not every issue is a pull request. Some endpoints, events, and webhooks +// may also return pull requests via this struct. If PullRequestLinks is nil, +// this is an issue, and if PullRequestLinks is not nil, this is a pull request. +// The IsPullRequest helper method can be used to check that. +type Issue struct { + ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` + State *string `json:"state,omitempty"` + Locked *bool `json:"locked,omitempty"` + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + User *User `json:"user,omitempty"` + Labels []Label `json:"labels,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Comments *int `json:"comments,omitempty"` + ClosedAt *time.Time `json:"closed_at,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + ClosedBy *User `json:"closed_by,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + LabelsURL *string `json:"labels_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Reactions *Reactions `json:"reactions,omitempty"` + Assignees []*User `json:"assignees,omitempty"` + NodeID *string `json:"node_id,omitempty"` + + // TextMatches is only populated from search results that request text matches + // See: search.go and https://developer.github.com/v3/search/#text-match-metadata + TextMatches []TextMatch `json:"text_matches,omitempty"` +} + +func (i Issue) String() string { + return Stringify(i) +} + +// IsPullRequest reports whether the issue is also a pull request. It uses the +// method recommended by GitHub's API documentation, which is to check whether +// PullRequestLinks is non-nil. +func (i Issue) IsPullRequest() bool { + return i.PullRequestLinks != nil +} + +// IssueRequest represents a request to create/edit an issue. +// It is separate from Issue above because otherwise Labels +// and Assignee fail to serialize to the correct JSON. +type IssueRequest struct { + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + Labels *[]string `json:"labels,omitempty"` + Assignee *string `json:"assignee,omitempty"` + State *string `json:"state,omitempty"` + Milestone *int `json:"milestone,omitempty"` + Assignees *[]string `json:"assignees,omitempty"` +} + +// IssueListOptions specifies the optional parameters to the IssuesService.List +// and IssuesService.ListByOrg methods. +type IssueListOptions struct { + // Filter specifies which issues to list. Possible values are: assigned, + // created, mentioned, subscribed, all. Default is "assigned". + Filter string `url:"filter,omitempty"` + + // State filters issues based on their state. Possible values are: open, + // closed, all. Default is "open". + State string `url:"state,omitempty"` + + // Labels filters issues based on their label. + Labels []string `url:"labels,comma,omitempty"` + + // Sort specifies how to sort issues. Possible values are: created, updated, + // and comments. Default value is "created". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort issues. Possible values are: asc, desc. + // Default is "desc". + Direction string `url:"direction,omitempty"` + + // Since filters issues by time. + Since time.Time `url:"since,omitempty"` + + ListOptions +} + +// PullRequestLinks object is added to the Issue object when it's an issue included +// in the IssueCommentEvent webhook payload, if the webhook is fired by a comment on a PR. +type PullRequestLinks struct { + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + DiffURL *string `json:"diff_url,omitempty"` + PatchURL *string `json:"patch_url,omitempty"` +} + +// List the issues for the authenticated user. If all is true, list issues +// across all the user's visible repositories including owned, member, and +// organization repositories; if false, list only owned and member +// repositories. +// +// GitHub API docs: https://developer.github.com/v3/issues/#list-issues +func (s *IssuesService) List(ctx context.Context, all bool, opt *IssueListOptions) ([]*Issue, *Response, error) { + var u string + if all { + u = "issues" + } else { + u = "user/issues" + } + return s.listIssues(ctx, u, opt) +} + +// ListByOrg fetches the issues in the specified organization for the +// authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/issues/#list-issues +func (s *IssuesService) ListByOrg(ctx context.Context, org string, opt *IssueListOptions) ([]*Issue, *Response, error) { + u := fmt.Sprintf("orgs/%v/issues", org) + return s.listIssues(ctx, u, opt) +} + +func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueListOptions) ([]*Issue, *Response, error) { + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + var issues []*Issue + resp, err := s.client.Do(ctx, req, &issues) + if err != nil { + return nil, resp, err + } + + return issues, resp, nil +} + +// IssueListByRepoOptions specifies the optional parameters to the +// IssuesService.ListByRepo method. +type IssueListByRepoOptions struct { + // Milestone limits issues for the specified milestone. Possible values are + // a milestone number, "none" for issues with no milestone, "*" for issues + // with any milestone. + Milestone string `url:"milestone,omitempty"` + + // State filters issues based on their state. Possible values are: open, + // closed, all. Default is "open". + State string `url:"state,omitempty"` + + // Assignee filters issues based on their assignee. Possible values are a + // user name, "none" for issues that are not assigned, "*" for issues with + // any assigned user. + Assignee string `url:"assignee,omitempty"` + + // Creator filters issues based on their creator. + Creator string `url:"creator,omitempty"` + + // Mentioned filters issues to those mentioned a specific user. + Mentioned string `url:"mentioned,omitempty"` + + // Labels filters issues based on their label. + Labels []string `url:"labels,omitempty,comma"` + + // Sort specifies how to sort issues. Possible values are: created, updated, + // and comments. Default value is "created". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort issues. Possible values are: asc, desc. + // Default is "desc". + Direction string `url:"direction,omitempty"` + + // Since filters issues by time. + Since time.Time `url:"since,omitempty"` + + ListOptions +} + +// ListByRepo lists the issues for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/#list-issues-for-a-repository +func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo string, opt *IssueListByRepoOptions) ([]*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + var issues []*Issue + resp, err := s.client.Do(ctx, req, &issues) + if err != nil { + return nil, resp, err + } + + return issues, resp, nil +} + +// Get a single issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/#get-a-single-issue +func (s *IssuesService) Get(ctx context.Context, owner string, repo string, number int) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + issue := new(Issue) + resp, err := s.client.Do(ctx, req, issue) + if err != nil { + return nil, resp, err + } + + return issue, resp, nil +} + +// Create a new issue on the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/#create-an-issue +func (s *IssuesService) Create(ctx context.Context, owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) + req, err := s.client.NewRequest("POST", u, issue) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + i := new(Issue) + resp, err := s.client.Do(ctx, req, i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} + +// Edit an issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/#edit-an-issue +func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) + req, err := s.client.NewRequest("PATCH", u, issue) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + i := new(Issue) + resp, err := s.client.Do(ctx, req, i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} + +// Lock an issue's conversation. +// +// GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue +func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Unlock an issue's conversation. +// +// GitHub API docs: https://developer.github.com/v3/issues/#unlock-an-issue +func (s *IssuesService) Unlock(ctx context.Context, owner string, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/issues_assignees.go b/vendor/github.com/google/go-github/github/issues_assignees.go new file mode 100644 index 00000000..9cb366f5 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_assignees.go @@ -0,0 +1,85 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListAssignees fetches all available assignees (owners and collaborators) to +// which issues may be assigned. +// +// GitHub API docs: https://developer.github.com/v3/issues/assignees/#list-assignees +func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + var assignees []*User + resp, err := s.client.Do(ctx, req, &assignees) + if err != nil { + return nil, resp, err + } + + return assignees, resp, nil +} + +// IsAssignee checks if a user is an assignee for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/assignees/#check-assignee +func (s *IssuesService) IsAssignee(ctx context.Context, owner, repo, user string) (bool, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/assignees/%v", owner, repo, user) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + resp, err := s.client.Do(ctx, req, nil) + assignee, err := parseBoolResponse(err) + return assignee, resp, err +} + +// AddAssignees adds the provided GitHub users as assignees to the issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue +func (s *IssuesService) AddAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { + users := &struct { + Assignees []string `json:"assignees,omitempty"` + }{Assignees: assignees} + u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) + req, err := s.client.NewRequest("POST", u, users) + if err != nil { + return nil, nil, err + } + + issue := &Issue{} + resp, err := s.client.Do(ctx, req, issue) + return issue, resp, err +} + +// RemoveAssignees removes the provided GitHub users as assignees from the issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue +func (s *IssuesService) RemoveAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { + users := &struct { + Assignees []string `json:"assignees,omitempty"` + }{Assignees: assignees} + u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) + req, err := s.client.NewRequest("DELETE", u, users) + if err != nil { + return nil, nil, err + } + + issue := &Issue{} + resp, err := s.client.Do(ctx, req, issue) + return issue, resp, err +} diff --git a/vendor/github.com/google/go-github/github/issues_assignees_test.go b/vendor/github.com/google/go-github/github/issues_assignees_test.go new file mode 100644 index 00000000..bb2bf4ff --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_assignees_test.go @@ -0,0 +1,164 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestIssuesService_ListAssignees(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/assignees", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + assignees, _, err := client.Issues.ListAssignees(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Issues.ListAssignees returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(assignees, want) { + t.Errorf("Issues.ListAssignees returned %+v, want %+v", assignees, want) + } +} + +func TestIssuesService_ListAssignees_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListAssignees(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestIssuesService_IsAssignee_true(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + + assignee, _, err := client.Issues.IsAssignee(context.Background(), "o", "r", "u") + if err != nil { + t.Errorf("Issues.IsAssignee returned error: %v", err) + } + if want := true; assignee != want { + t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) + } +} + +func TestIssuesService_IsAssignee_false(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + assignee, _, err := client.Issues.IsAssignee(context.Background(), "o", "r", "u") + if err != nil { + t.Errorf("Issues.IsAssignee returned error: %v", err) + } + if want := false; assignee != want { + t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) + } +} + +func TestIssuesService_IsAssignee_error(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Error(w, "BadRequest", http.StatusBadRequest) + }) + + assignee, _, err := client.Issues.IsAssignee(context.Background(), "o", "r", "u") + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if want := false; assignee != want { + t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) + } +} + +func TestIssuesService_IsAssignee_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.IsAssignee(context.Background(), "%", "r", "u") + testURLParseError(t, err) +} + +func TestIssuesService_AddAssignees(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) { + var assignees struct { + Assignees []string `json:"assignees,omitempty"` + } + json.NewDecoder(r.Body).Decode(&assignees) + + testMethod(t, r, "POST") + want := []string{"user1", "user2"} + if !reflect.DeepEqual(assignees.Assignees, want) { + t.Errorf("assignees = %+v, want %+v", assignees, want) + } + fmt.Fprint(w, `{"number":1,"assignees":[{"login":"user1"},{"login":"user2"}]}`) + }) + + got, _, err := client.Issues.AddAssignees(context.Background(), "o", "r", 1, []string{"user1", "user2"}) + if err != nil { + t.Errorf("Issues.AddAssignees returned error: %v", err) + } + + want := &Issue{Number: Int(1), Assignees: []*User{{Login: String("user1")}, {Login: String("user2")}}} + if !reflect.DeepEqual(got, want) { + t.Errorf("Issues.AddAssignees = %+v, want %+v", got, want) + } +} + +func TestIssuesService_RemoveAssignees(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) { + var assignees struct { + Assignees []string `json:"assignees,omitempty"` + } + json.NewDecoder(r.Body).Decode(&assignees) + + testMethod(t, r, "DELETE") + want := []string{"user1", "user2"} + if !reflect.DeepEqual(assignees.Assignees, want) { + t.Errorf("assignees = %+v, want %+v", assignees, want) + } + fmt.Fprint(w, `{"number":1,"assignees":[]}`) + }) + + got, _, err := client.Issues.RemoveAssignees(context.Background(), "o", "r", 1, []string{"user1", "user2"}) + if err != nil { + t.Errorf("Issues.RemoveAssignees returned error: %v", err) + } + + want := &Issue{Number: Int(1), Assignees: []*User{}} + if !reflect.DeepEqual(got, want) { + t.Errorf("Issues.RemoveAssignees = %+v, want %+v", got, want) + } +} diff --git a/vendor/github.com/google/go-github/github/issues_comments.go b/vendor/github.com/google/go-github/github/issues_comments.go new file mode 100644 index 00000000..70047453 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_comments.go @@ -0,0 +1,148 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// IssueComment represents a comment left on an issue. +type IssueComment struct { + ID *int64 `json:"id,omitempty"` + Body *string `json:"body,omitempty"` + User *User `json:"user,omitempty"` + Reactions *Reactions `json:"reactions,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + IssueURL *string `json:"issue_url,omitempty"` +} + +func (i IssueComment) String() string { + return Stringify(i) +} + +// IssueListCommentsOptions specifies the optional parameters to the +// IssuesService.ListComments method. +type IssueListCommentsOptions struct { + // Sort specifies how to sort comments. Possible values are: created, updated. + Sort string `url:"sort,omitempty"` + + // Direction in which to sort comments. Possible values are: asc, desc. + Direction string `url:"direction,omitempty"` + + // Since filters comments by time. + Since time.Time `url:"since,omitempty"` + + ListOptions +} + +// ListComments lists all comments on the specified issue. Specifying an issue +// number of 0 will return all comments on all issues for the repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue +func (s *IssuesService) ListComments(ctx context.Context, owner string, repo string, number int, opt *IssueListCommentsOptions) ([]*IssueComment, *Response, error) { + var u string + if number == 0 { + u = fmt.Sprintf("repos/%v/%v/issues/comments", owner, repo) + } else { + u = fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var comments []*IssueComment + resp, err := s.client.Do(ctx, req, &comments) + if err != nil { + return nil, resp, err + } + + return comments, resp, nil +} + +// GetComment fetches the specified issue comment. +// +// GitHub API docs: https://developer.github.com/v3/issues/comments/#get-a-single-comment +func (s *IssuesService) GetComment(ctx context.Context, owner string, repo string, id int) (*IssueComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + comment := new(IssueComment) + resp, err := s.client.Do(ctx, req, comment) + if err != nil { + return nil, resp, err + } + + return comment, resp, nil +} + +// CreateComment creates a new comment on the specified issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/comments/#create-a-comment +func (s *IssuesService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *IssueComment) (*IssueComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) + req, err := s.client.NewRequest("POST", u, comment) + if err != nil { + return nil, nil, err + } + c := new(IssueComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// EditComment updates an issue comment. +// +// GitHub API docs: https://developer.github.com/v3/issues/comments/#edit-a-comment +func (s *IssuesService) EditComment(ctx context.Context, owner string, repo string, id int, comment *IssueComment) (*IssueComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) + req, err := s.client.NewRequest("PATCH", u, comment) + if err != nil { + return nil, nil, err + } + c := new(IssueComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// DeleteComment deletes an issue comment. +// +// GitHub API docs: https://developer.github.com/v3/issues/comments/#delete-a-comment +func (s *IssuesService) DeleteComment(ctx context.Context, owner string, repo string, id int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/issues_comments_test.go b/vendor/github.com/google/go-github/github/issues_comments_test.go new file mode 100644 index 00000000..6911a798 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_comments_test.go @@ -0,0 +1,203 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestIssuesService_ListComments_allIssues(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{ + "sort": "updated", + "direction": "desc", + "since": "2002-02-10T15:30:00Z", + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &IssueListCommentsOptions{ + Sort: "updated", + Direction: "desc", + Since: time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), + ListOptions: ListOptions{Page: 2}, + } + comments, _, err := client.Issues.ListComments(context.Background(), "o", "r", 0, opt) + if err != nil { + t.Errorf("Issues.ListComments returned error: %v", err) + } + + want := []*IssueComment{{ID: Int64(1)}} + if !reflect.DeepEqual(comments, want) { + t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) + } +} + +func TestIssuesService_ListComments_specificIssue(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + comments, _, err := client.Issues.ListComments(context.Background(), "o", "r", 1, nil) + if err != nil { + t.Errorf("Issues.ListComments returned error: %v", err) + } + + want := []*IssueComment{{ID: Int64(1)}} + if !reflect.DeepEqual(comments, want) { + t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) + } +} + +func TestIssuesService_ListComments_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListComments(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_GetComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Issues.GetComment(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Issues.GetComment returned error: %v", err) + } + + want := &IssueComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Issues.GetComment returned %+v, want %+v", comment, want) + } +} + +func TestIssuesService_GetComment_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.GetComment(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} + +func TestIssuesService_CreateComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &IssueComment{Body: String("b")} + + mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { + v := new(IssueComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Issues.CreateComment(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Issues.CreateComment returned error: %v", err) + } + + want := &IssueComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Issues.CreateComment returned %+v, want %+v", comment, want) + } +} + +func TestIssuesService_CreateComment_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.CreateComment(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_EditComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &IssueComment{Body: String("b")} + + mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { + v := new(IssueComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Issues.EditComment(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Issues.EditComment returned error: %v", err) + } + + want := &IssueComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Issues.EditComment returned %+v, want %+v", comment, want) + } +} + +func TestIssuesService_EditComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.EditComment(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_DeleteComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Issues.DeleteComment(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Issues.DeleteComments returned error: %v", err) + } +} + +func TestIssuesService_DeleteComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Issues.DeleteComment(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/issues_events.go b/vendor/github.com/google/go-github/github/issues_events.go new file mode 100644 index 00000000..55e6d431 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_events.go @@ -0,0 +1,151 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// IssueEvent represents an event that occurred around an Issue or Pull Request. +type IssueEvent struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + + // The User that generated this event. + Actor *User `json:"actor,omitempty"` + + // Event identifies the actual type of Event that occurred. Possible + // values are: + // + // closed + // The Actor closed the issue. + // If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit. + // + // merged + // The Actor merged into master a branch containing a commit mentioning the issue. + // CommitID holds the SHA1 of the merge commit. + // + // referenced + // The Actor committed to master a commit mentioning the issue in its commit message. + // CommitID holds the SHA1 of the commit. + // + // reopened, locked, unlocked + // The Actor did that to the issue. + // + // renamed + // The Actor changed the issue title from Rename.From to Rename.To. + // + // mentioned + // Someone unspecified @mentioned the Actor [sic] in an issue comment body. + // + // assigned, unassigned + // The Assigner assigned the issue to or removed the assignment from the Assignee. + // + // labeled, unlabeled + // The Actor added or removed the Label from the issue. + // + // milestoned, demilestoned + // The Actor added or removed the issue from the Milestone. + // + // subscribed, unsubscribed + // The Actor subscribed to or unsubscribed from notifications for an issue. + // + // head_ref_deleted, head_ref_restored + // The pull request’s branch was deleted or restored. + // + Event *string `json:"event,omitempty"` + + CreatedAt *time.Time `json:"created_at,omitempty"` + Issue *Issue `json:"issue,omitempty"` + + // Only present on certain events; see above. + Assignee *User `json:"assignee,omitempty"` + Assigner *User `json:"assigner,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + Label *Label `json:"label,omitempty"` + Rename *Rename `json:"rename,omitempty"` +} + +// ListIssueEvents lists events for the specified issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue +func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*IssueEvent, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*IssueEvent + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// ListRepositoryEvents lists events for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository +func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var events []*IssueEvent + resp, err := s.client.Do(ctx, req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil +} + +// GetEvent returns the specified issue event. +// +// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event +func (s *IssuesService) GetEvent(ctx context.Context, owner, repo string, id int64) (*IssueEvent, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + event := new(IssueEvent) + resp, err := s.client.Do(ctx, req, event) + if err != nil { + return nil, resp, err + } + + return event, resp, nil +} + +// Rename contains details for 'renamed' events. +type Rename struct { + From *string `json:"from,omitempty"` + To *string `json:"to,omitempty"` +} + +func (r Rename) String() string { + return Stringify(r) +} diff --git a/vendor/github.com/google/go-github/github/issues_events_test.go b/vendor/github.com/google/go-github/github/issues_events_test.go new file mode 100644 index 00000000..fe21ecbe --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_events_test.go @@ -0,0 +1,84 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestIssuesService_ListIssueEvents(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + events, _, err := client.Issues.ListIssueEvents(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("Issues.ListIssueEvents returned error: %v", err) + } + + want := []*IssueEvent{{ID: Int64(1)}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Issues.ListIssueEvents returned %+v, want %+v", events, want) + } +} + +func TestIssuesService_ListRepositoryEvents(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + events, _, err := client.Issues.ListRepositoryEvents(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Issues.ListRepositoryEvents returned error: %v", err) + } + + want := []*IssueEvent{{ID: Int64(1)}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Issues.ListRepositoryEvents returned %+v, want %+v", events, want) + } +} + +func TestIssuesService_GetEvent(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/events/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + event, _, err := client.Issues.GetEvent(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Issues.GetEvent returned error: %v", err) + } + + want := &IssueEvent{ID: Int64(1)} + if !reflect.DeepEqual(event, want) { + t.Errorf("Issues.GetEvent returned %+v, want %+v", event, want) + } +} diff --git a/vendor/github.com/google/go-github/github/issues_labels.go b/vendor/github.com/google/go-github/github/issues_labels.go new file mode 100644 index 00000000..aacf7d7c --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_labels.go @@ -0,0 +1,251 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Label represents a GitHub label on an Issue +type Label struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + Name *string `json:"name,omitempty"` + Color *string `json:"color,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +func (l Label) String() string { + return Stringify(l) +} + +// ListLabels lists all labels for a repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository +func (s *IssuesService) ListLabels(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var labels []*Label + resp, err := s.client.Do(ctx, req, &labels) + if err != nil { + return nil, resp, err + } + + return labels, resp, nil +} + +// GetLabel gets a single label. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-a-single-label +func (s *IssuesService) GetLabel(ctx context.Context, owner string, repo string, name string) (*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + label := new(Label) + resp, err := s.client.Do(ctx, req, label) + if err != nil { + return nil, resp, err + } + + return label, resp, nil +} + +// CreateLabel creates a new label on the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#create-a-label +func (s *IssuesService) CreateLabel(ctx context.Context, owner string, repo string, label *Label) (*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) + req, err := s.client.NewRequest("POST", u, label) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + l := new(Label) + resp, err := s.client.Do(ctx, req, l) + if err != nil { + return nil, resp, err + } + + return l, resp, nil +} + +// EditLabel edits a label. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#update-a-label +func (s *IssuesService) EditLabel(ctx context.Context, owner string, repo string, name string, label *Label) (*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) + req, err := s.client.NewRequest("PATCH", u, label) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + l := new(Label) + resp, err := s.client.Do(ctx, req, l) + if err != nil { + return nil, resp, err + } + + return l, resp, nil +} + +// DeleteLabel deletes a label. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#delete-a-label +func (s *IssuesService) DeleteLabel(ctx context.Context, owner string, repo string, name string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// ListLabelsByIssue lists all labels for an issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue +func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var labels []*Label + resp, err := s.client.Do(ctx, req, &labels) + if err != nil { + return nil, resp, err + } + + return labels, resp, nil +} + +// AddLabelsToIssue adds labels to an issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue +func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) + req, err := s.client.NewRequest("POST", u, labels) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var l []*Label + resp, err := s.client.Do(ctx, req, &l) + if err != nil { + return nil, resp, err + } + + return l, resp, nil +} + +// RemoveLabelForIssue removes a label for an issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue +func (s *IssuesService) RemoveLabelForIssue(ctx context.Context, owner string, repo string, number int, label string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/labels/%v", owner, repo, number, label) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// ReplaceLabelsForIssue replaces all labels for an issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue +func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) + req, err := s.client.NewRequest("PUT", u, labels) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var l []*Label + resp, err := s.client.Do(ctx, req, &l) + if err != nil { + return nil, resp, err + } + + return l, resp, nil +} + +// RemoveLabelsForIssue removes all labels for an issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue +func (s *IssuesService) RemoveLabelsForIssue(ctx context.Context, owner string, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// ListLabelsForMilestone lists labels for every issue in a milestone. +// +// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone +func (s *IssuesService) ListLabelsForMilestone(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%d/labels", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var labels []*Label + resp, err := s.client.Do(ctx, req, &labels) + if err != nil { + return nil, resp, err + } + + return labels, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/issues_labels_test.go b/vendor/github.com/google/go-github/github/issues_labels_test.go new file mode 100644 index 00000000..02ac9041 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_labels_test.go @@ -0,0 +1,358 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestIssuesService_ListLabels(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) + }) + + opt := &ListOptions{Page: 2} + labels, _, err := client.Issues.ListLabels(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Issues.ListLabels returned error: %v", err) + } + + want := []*Label{{Name: String("a")}, {Name: String("b")}} + if !reflect.DeepEqual(labels, want) { + t.Errorf("Issues.ListLabels returned %+v, want %+v", labels, want) + } +} + +func TestIssuesService_ListLabels_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListLabels(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestIssuesService_GetLabel(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"url":"u", "name": "n", "color": "c"}`) + }) + + label, _, err := client.Issues.GetLabel(context.Background(), "o", "r", "n") + if err != nil { + t.Errorf("Issues.GetLabel returned error: %v", err) + } + + want := &Label{URL: String("u"), Name: String("n"), Color: String("c")} + if !reflect.DeepEqual(label, want) { + t.Errorf("Issues.GetLabel returned %+v, want %+v", label, want) + } +} + +func TestIssuesService_GetLabel_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.GetLabel(context.Background(), "%", "%", "%") + testURLParseError(t, err) +} + +func TestIssuesService_CreateLabel(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Label{Name: String("n")} + + mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { + v := new(Label) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"url":"u"}`) + }) + + label, _, err := client.Issues.CreateLabel(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Issues.CreateLabel returned error: %v", err) + } + + want := &Label{URL: String("u")} + if !reflect.DeepEqual(label, want) { + t.Errorf("Issues.CreateLabel returned %+v, want %+v", label, want) + } +} + +func TestIssuesService_CreateLabel_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.CreateLabel(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestIssuesService_EditLabel(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Label{Name: String("z")} + + mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { + v := new(Label) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"url":"u"}`) + }) + + label, _, err := client.Issues.EditLabel(context.Background(), "o", "r", "n", input) + if err != nil { + t.Errorf("Issues.EditLabel returned error: %v", err) + } + + want := &Label{URL: String("u")} + if !reflect.DeepEqual(label, want) { + t.Errorf("Issues.EditLabel returned %+v, want %+v", label, want) + } +} + +func TestIssuesService_EditLabel_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.EditLabel(context.Background(), "%", "%", "%", nil) + testURLParseError(t, err) +} + +func TestIssuesService_DeleteLabel(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Issues.DeleteLabel(context.Background(), "o", "r", "n") + if err != nil { + t.Errorf("Issues.DeleteLabel returned error: %v", err) + } +} + +func TestIssuesService_DeleteLabel_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Issues.DeleteLabel(context.Background(), "%", "%", "%") + testURLParseError(t, err) +} + +func TestIssuesService_ListLabelsByIssue(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"name":"a","id":1},{"name":"b","id":2}]`) + }) + + opt := &ListOptions{Page: 2} + labels, _, err := client.Issues.ListLabelsByIssue(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("Issues.ListLabelsByIssue returned error: %v", err) + } + + want := []*Label{ + {Name: String("a"), ID: Int64(1)}, + {Name: String("b"), ID: Int64(2)}, + } + if !reflect.DeepEqual(labels, want) { + t.Errorf("Issues.ListLabelsByIssue returned %+v, want %+v", labels, want) + } +} + +func TestIssuesService_ListLabelsByIssue_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListLabelsByIssue(context.Background(), "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_AddLabelsToIssue(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := []string{"a", "b"} + + mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { + var v []string + json.NewDecoder(r.Body).Decode(&v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `[{"url":"u"}]`) + }) + + labels, _, err := client.Issues.AddLabelsToIssue(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Issues.AddLabelsToIssue returned error: %v", err) + } + + want := []*Label{{URL: String("u")}} + if !reflect.DeepEqual(labels, want) { + t.Errorf("Issues.AddLabelsToIssue returned %+v, want %+v", labels, want) + } +} + +func TestIssuesService_AddLabelsToIssue_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.AddLabelsToIssue(context.Background(), "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_RemoveLabelForIssue(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/labels/l", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Issues.RemoveLabelForIssue(context.Background(), "o", "r", 1, "l") + if err != nil { + t.Errorf("Issues.RemoveLabelForIssue returned error: %v", err) + } +} + +func TestIssuesService_RemoveLabelForIssue_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Issues.RemoveLabelForIssue(context.Background(), "%", "%", 1, "%") + testURLParseError(t, err) +} + +func TestIssuesService_ReplaceLabelsForIssue(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := []string{"a", "b"} + + mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { + var v []string + json.NewDecoder(r.Body).Decode(&v) + + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `[{"url":"u"}]`) + }) + + labels, _, err := client.Issues.ReplaceLabelsForIssue(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Issues.ReplaceLabelsForIssue returned error: %v", err) + } + + want := []*Label{{URL: String("u")}} + if !reflect.DeepEqual(labels, want) { + t.Errorf("Issues.ReplaceLabelsForIssue returned %+v, want %+v", labels, want) + } +} + +func TestIssuesService_ReplaceLabelsForIssue_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ReplaceLabelsForIssue(context.Background(), "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_RemoveLabelsForIssue(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Issues.RemoveLabelsForIssue(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Issues.RemoveLabelsForIssue returned error: %v", err) + } +} + +func TestIssuesService_RemoveLabelsForIssue_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Issues.RemoveLabelsForIssue(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} + +func TestIssuesService_ListLabelsForMilestone(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/milestones/1/labels", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) + }) + + opt := &ListOptions{Page: 2} + labels, _, err := client.Issues.ListLabelsForMilestone(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("Issues.ListLabelsForMilestone returned error: %v", err) + } + + want := []*Label{{Name: String("a")}, {Name: String("b")}} + if !reflect.DeepEqual(labels, want) { + t.Errorf("Issues.ListLabelsForMilestone returned %+v, want %+v", labels, want) + } +} + +func TestIssuesService_ListLabelsForMilestone_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListLabelsForMilestone(context.Background(), "%", "%", 1, nil) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/issues_milestones.go b/vendor/github.com/google/go-github/github/issues_milestones.go new file mode 100644 index 00000000..6af1cc03 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_milestones.go @@ -0,0 +1,160 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// Milestone represents a GitHub repository milestone. +type Milestone struct { + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + LabelsURL *string `json:"labels_url,omitempty"` + ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` + State *string `json:"state,omitempty"` + Title *string `json:"title,omitempty"` + Description *string `json:"description,omitempty"` + Creator *User `json:"creator,omitempty"` + OpenIssues *int `json:"open_issues,omitempty"` + ClosedIssues *int `json:"closed_issues,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + ClosedAt *time.Time `json:"closed_at,omitempty"` + DueOn *time.Time `json:"due_on,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +func (m Milestone) String() string { + return Stringify(m) +} + +// MilestoneListOptions specifies the optional parameters to the +// IssuesService.ListMilestones method. +type MilestoneListOptions struct { + // State filters milestones based on their state. Possible values are: + // open, closed, all. Default is "open". + State string `url:"state,omitempty"` + + // Sort specifies how to sort milestones. Possible values are: due_on, completeness. + // Default value is "due_on". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort milestones. Possible values are: asc, desc. + // Default is "asc". + Direction string `url:"direction,omitempty"` + + ListOptions +} + +// ListMilestones lists all milestones for a repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository +func (s *IssuesService) ListMilestones(ctx context.Context, owner string, repo string, opt *MilestoneListOptions) ([]*Milestone, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var milestones []*Milestone + resp, err := s.client.Do(ctx, req, &milestones) + if err != nil { + return nil, resp, err + } + + return milestones, resp, nil +} + +// GetMilestone gets a single milestone. +// +// GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone +func (s *IssuesService) GetMilestone(ctx context.Context, owner string, repo string, number int) (*Milestone, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + milestone := new(Milestone) + resp, err := s.client.Do(ctx, req, milestone) + if err != nil { + return nil, resp, err + } + + return milestone, resp, nil +} + +// CreateMilestone creates a new milestone on the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone +func (s *IssuesService) CreateMilestone(ctx context.Context, owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) + req, err := s.client.NewRequest("POST", u, milestone) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + m := new(Milestone) + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// EditMilestone edits a milestone. +// +// GitHub API docs: https://developer.github.com/v3/issues/milestones/#update-a-milestone +func (s *IssuesService) EditMilestone(ctx context.Context, owner string, repo string, number int, milestone *Milestone) (*Milestone, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) + req, err := s.client.NewRequest("PATCH", u, milestone) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + m := new(Milestone) + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// DeleteMilestone deletes a milestone. +// +// GitHub API docs: https://developer.github.com/v3/issues/milestones/#delete-a-milestone +func (s *IssuesService) DeleteMilestone(ctx context.Context, owner string, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/issues_milestones_test.go b/vendor/github.com/google/go-github/github/issues_milestones_test.go new file mode 100644 index 00000000..006e4817 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_milestones_test.go @@ -0,0 +1,178 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestIssuesService_ListMilestones(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{ + "state": "closed", + "sort": "due_date", + "direction": "asc", + "page": "2", + }) + fmt.Fprint(w, `[{"number":1}]`) + }) + + opt := &MilestoneListOptions{"closed", "due_date", "asc", ListOptions{Page: 2}} + milestones, _, err := client.Issues.ListMilestones(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("IssuesService.ListMilestones returned error: %v", err) + } + + want := []*Milestone{{Number: Int(1)}} + if !reflect.DeepEqual(milestones, want) { + t.Errorf("IssuesService.ListMilestones returned %+v, want %+v", milestones, want) + } +} + +func TestIssuesService_ListMilestones_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListMilestones(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestIssuesService_GetMilestone(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"number":1}`) + }) + + milestone, _, err := client.Issues.GetMilestone(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("IssuesService.GetMilestone returned error: %v", err) + } + + want := &Milestone{Number: Int(1)} + if !reflect.DeepEqual(milestone, want) { + t.Errorf("IssuesService.GetMilestone returned %+v, want %+v", milestone, want) + } +} + +func TestIssuesService_GetMilestone_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.GetMilestone(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} + +func TestIssuesService_CreateMilestone(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Milestone{Title: String("t")} + + mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { + v := new(Milestone) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"number":1}`) + }) + + milestone, _, err := client.Issues.CreateMilestone(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("IssuesService.CreateMilestone returned error: %v", err) + } + + want := &Milestone{Number: Int(1)} + if !reflect.DeepEqual(milestone, want) { + t.Errorf("IssuesService.CreateMilestone returned %+v, want %+v", milestone, want) + } +} + +func TestIssuesService_CreateMilestone_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.CreateMilestone(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestIssuesService_EditMilestone(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Milestone{Title: String("t")} + + mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { + v := new(Milestone) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"number":1}`) + }) + + milestone, _, err := client.Issues.EditMilestone(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("IssuesService.EditMilestone returned error: %v", err) + } + + want := &Milestone{Number: Int(1)} + if !reflect.DeepEqual(milestone, want) { + t.Errorf("IssuesService.EditMilestone returned %+v, want %+v", milestone, want) + } +} + +func TestIssuesService_EditMilestone_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.EditMilestone(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_DeleteMilestone(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Issues.DeleteMilestone(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("IssuesService.DeleteMilestone returned error: %v", err) + } +} + +func TestIssuesService_DeleteMilestone_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Issues.DeleteMilestone(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/issues_test.go b/vendor/github.com/google/go-github/github/issues_test.go new file mode 100644 index 00000000..aeca2469 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_test.go @@ -0,0 +1,311 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" + "testing" + "time" +) + +func TestIssuesService_List_all(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + testFormValues(t, r, values{ + "filter": "all", + "state": "closed", + "labels": "a,b", + "sort": "updated", + "direction": "asc", + "since": "2002-02-10T15:30:00Z", + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{"number":1}]`) + }) + + opt := &IssueListOptions{ + "all", "closed", []string{"a", "b"}, "updated", "asc", + time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), + ListOptions{Page: 1, PerPage: 2}, + } + issues, _, err := client.Issues.List(context.Background(), true, opt) + if err != nil { + t.Errorf("Issues.List returned error: %v", err) + } + + want := []*Issue{{Number: Int(1)}} + if !reflect.DeepEqual(issues, want) { + t.Errorf("Issues.List returned %+v, want %+v", issues, want) + } +} + +func TestIssuesService_List_owned(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/user/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `[{"number":1}]`) + }) + + issues, _, err := client.Issues.List(context.Background(), false, nil) + if err != nil { + t.Errorf("Issues.List returned error: %v", err) + } + + want := []*Issue{{Number: Int(1)}} + if !reflect.DeepEqual(issues, want) { + t.Errorf("Issues.List returned %+v, want %+v", issues, want) + } +} + +func TestIssuesService_ListByOrg(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/orgs/o/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `[{"number":1}]`) + }) + + issues, _, err := client.Issues.ListByOrg(context.Background(), "o", nil) + if err != nil { + t.Errorf("Issues.ListByOrg returned error: %v", err) + } + + want := []*Issue{{Number: Int(1)}} + if !reflect.DeepEqual(issues, want) { + t.Errorf("Issues.List returned %+v, want %+v", issues, want) + } +} + +func TestIssuesService_ListByOrg_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListByOrg(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestIssuesService_ListByRepo(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + testFormValues(t, r, values{ + "milestone": "*", + "state": "closed", + "assignee": "a", + "creator": "c", + "mentioned": "m", + "labels": "a,b", + "sort": "updated", + "direction": "asc", + "since": "2002-02-10T15:30:00Z", + }) + fmt.Fprint(w, `[{"number":1}]`) + }) + + opt := &IssueListByRepoOptions{ + "*", "closed", "a", "c", "m", []string{"a", "b"}, "updated", "asc", + time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), + ListOptions{0, 0}, + } + issues, _, err := client.Issues.ListByRepo(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Issues.ListByOrg returned error: %v", err) + } + + want := []*Issue{{Number: Int(1)}} + if !reflect.DeepEqual(issues, want) { + t.Errorf("Issues.List returned %+v, want %+v", issues, want) + } +} + +func TestIssuesService_ListByRepo_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.ListByRepo(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestIssuesService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `{"number":1, "labels": [{"url": "u", "name": "n", "color": "c"}]}`) + }) + + issue, _, err := client.Issues.Get(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Issues.Get returned error: %v", err) + } + + want := &Issue{ + Number: Int(1), + Labels: []Label{{ + URL: String("u"), + Name: String("n"), + Color: String("c"), + }}, + } + if !reflect.DeepEqual(issue, want) { + t.Errorf("Issues.Get returned %+v, want %+v", issue, want) + } +} + +func TestIssuesService_Get_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.Get(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} + +func TestIssuesService_Create(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &IssueRequest{ + Title: String("t"), + Body: String("b"), + Assignee: String("a"), + Labels: &[]string{"l1", "l2"}, + } + + mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { + v := new(IssueRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"number":1}`) + }) + + issue, _, err := client.Issues.Create(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Issues.Create returned error: %v", err) + } + + want := &Issue{Number: Int(1)} + if !reflect.DeepEqual(issue, want) { + t.Errorf("Issues.Create returned %+v, want %+v", issue, want) + } +} + +func TestIssuesService_Create_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.Create(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestIssuesService_Edit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &IssueRequest{Title: String("t")} + + mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { + v := new(IssueRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"number":1}`) + }) + + issue, _, err := client.Issues.Edit(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Issues.Edit returned error: %v", err) + } + + want := &Issue{Number: Int(1)} + if !reflect.DeepEqual(issue, want) { + t.Errorf("Issues.Edit returned %+v, want %+v", issue, want) + } +} + +func TestIssuesService_Edit_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Issues.Edit(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_Lock(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + + w.WriteHeader(http.StatusNoContent) + }) + + if _, err := client.Issues.Lock(context.Background(), "o", "r", 1); err != nil { + t.Errorf("Issues.Lock returned error: %v", err) + } +} + +func TestIssuesService_Unlock(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + + w.WriteHeader(http.StatusNoContent) + }) + + if _, err := client.Issues.Unlock(context.Background(), "o", "r", 1); err != nil { + t.Errorf("Issues.Unlock returned error: %v", err) + } +} + +func TestIsPullRequest(t *testing.T) { + i := new(Issue) + if i.IsPullRequest() == true { + t.Errorf("expected i.IsPullRequest (%v) to return false, got true", i) + } + i.PullRequestLinks = &PullRequestLinks{URL: String("http://example.com")} + if i.IsPullRequest() == false { + t.Errorf("expected i.IsPullRequest (%v) to return true, got false", i) + } +} diff --git a/vendor/github.com/google/go-github/github/issues_timeline.go b/vendor/github.com/google/go-github/github/issues_timeline.go new file mode 100644 index 00000000..9cfda832 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_timeline.go @@ -0,0 +1,149 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// Timeline represents an event that occurred around an Issue or Pull Request. +// +// It is similar to an IssueEvent but may contain more information. +// GitHub API docs: https://developer.github.com/v3/issues/timeline/ +type Timeline struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + CommitURL *string `json:"commit_url,omitempty"` + + // The User object that generated the event. + Actor *User `json:"actor,omitempty"` + + // Event identifies the actual type of Event that occurred. Possible values + // are: + // + // assigned + // The issue was assigned to the assignee. + // + // closed + // The issue was closed by the actor. When the commit_id is present, it + // identifies the commit that closed the issue using "closes / fixes #NN" + // syntax. + // + // commented + // A comment was added to the issue. + // + // committed + // A commit was added to the pull request's 'HEAD' branch. Only provided + // for pull requests. + // + // cross-referenced + // The issue was referenced from another issue. The 'source' attribute + // contains the 'id', 'actor', and 'url' of the reference's source. + // + // demilestoned + // The issue was removed from a milestone. + // + // head_ref_deleted + // The pull request's branch was deleted. + // + // head_ref_restored + // The pull request's branch was restored. + // + // labeled + // A label was added to the issue. + // + // locked + // The issue was locked by the actor. + // + // mentioned + // The actor was @mentioned in an issue body. + // + // merged + // The issue was merged by the actor. The 'commit_id' attribute is the + // SHA1 of the HEAD commit that was merged. + // + // milestoned + // The issue was added to a milestone. + // + // referenced + // The issue was referenced from a commit message. The 'commit_id' + // attribute is the commit SHA1 of where that happened. + // + // renamed + // The issue title was changed. + // + // reopened + // The issue was reopened by the actor. + // + // subscribed + // The actor subscribed to receive notifications for an issue. + // + // unassigned + // The assignee was unassigned from the issue. + // + // unlabeled + // A label was removed from the issue. + // + // unlocked + // The issue was unlocked by the actor. + // + // unsubscribed + // The actor unsubscribed to stop receiving notifications for an issue. + // + Event *string `json:"event,omitempty"` + + // The string SHA of a commit that referenced this Issue or Pull Request. + CommitID *string `json:"commit_id,omitempty"` + // The timestamp indicating when the event occurred. + CreatedAt *time.Time `json:"created_at,omitempty"` + // The Label object including `name` and `color` attributes. Only provided for + // 'labeled' and 'unlabeled' events. + Label *Label `json:"label,omitempty"` + // The User object which was assigned to (or unassigned from) this Issue or + // Pull Request. Only provided for 'assigned' and 'unassigned' events. + Assignee *User `json:"assignee,omitempty"` + // The Milestone object including a 'title' attribute. + // Only provided for 'milestoned' and 'demilestoned' events. + Milestone *Milestone `json:"milestone,omitempty"` + // The 'id', 'actor', and 'url' for the source of a reference from another issue. + // Only provided for 'cross-referenced' events. + Source *Source `json:"source,omitempty"` + // An object containing rename details including 'from' and 'to' attributes. + // Only provided for 'renamed' events. + Rename *Rename `json:"rename,omitempty"` +} + +// Source represents a reference's source. +type Source struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + Actor *User `json:"actor,omitempty"` +} + +// ListIssueTimeline lists events for the specified issue. +// +// GitHub API docs: https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue +func (s *IssuesService) ListIssueTimeline(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Timeline, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/timeline", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeTimelinePreview) + + var events []*Timeline + resp, err := s.client.Do(ctx, req, &events) + return events, resp, err +} diff --git a/vendor/github.com/google/go-github/github/issues_timeline_test.go b/vendor/github.com/google/go-github/github/issues_timeline_test.go new file mode 100644 index 00000000..93f62368 --- /dev/null +++ b/vendor/github.com/google/go-github/github/issues_timeline_test.go @@ -0,0 +1,40 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestIssuesService_ListIssueTimeline(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/timeline", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeTimelinePreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + events, _, err := client.Issues.ListIssueTimeline(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("Issues.ListIssueTimeline returned error: %v", err) + } + + want := []*Timeline{{ID: Int64(1)}} + if !reflect.DeepEqual(events, want) { + t.Errorf("Issues.ListIssueTimeline = %+v, want %+v", events, want) + } +} diff --git a/vendor/github.com/google/go-github/github/licenses.go b/vendor/github.com/google/go-github/github/licenses.go new file mode 100644 index 00000000..e9cd1777 --- /dev/null +++ b/vendor/github.com/google/go-github/github/licenses.go @@ -0,0 +1,103 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// LicensesService handles communication with the license related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/licenses/ +type LicensesService service + +// RepositoryLicense represents the license for a repository. +type RepositoryLicense struct { + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` + + SHA *string `json:"sha,omitempty"` + Size *int `json:"size,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + GitURL *string `json:"git_url,omitempty"` + DownloadURL *string `json:"download_url,omitempty"` + Type *string `json:"type,omitempty"` + Content *string `json:"content,omitempty"` + Encoding *string `json:"encoding,omitempty"` + License *License `json:"license,omitempty"` +} + +func (l RepositoryLicense) String() string { + return Stringify(l) +} + +// License represents an open source license. +type License struct { + Key *string `json:"key,omitempty"` + Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` + + SPDXID *string `json:"spdx_id,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Featured *bool `json:"featured,omitempty"` + Description *string `json:"description,omitempty"` + Implementation *string `json:"implementation,omitempty"` + Permissions *[]string `json:"permissions,omitempty"` + Conditions *[]string `json:"conditions,omitempty"` + Limitations *[]string `json:"limitations,omitempty"` + Body *string `json:"body,omitempty"` +} + +func (l License) String() string { + return Stringify(l) +} + +// List popular open source licenses. +// +// GitHub API docs: https://developer.github.com/v3/licenses/#list-all-licenses +func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, error) { + req, err := s.client.NewRequest("GET", "licenses", nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeLicensesPreview) + + var licenses []*License + resp, err := s.client.Do(ctx, req, &licenses) + if err != nil { + return nil, resp, err + } + + return licenses, resp, nil +} + +// Get extended metadata for one license. +// +// GitHub API docs: https://developer.github.com/v3/licenses/#get-an-individual-license +func (s *LicensesService) Get(ctx context.Context, licenseName string) (*License, *Response, error) { + u := fmt.Sprintf("licenses/%s", licenseName) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeLicensesPreview) + + license := new(License) + resp, err := s.client.Do(ctx, req, license) + if err != nil { + return nil, resp, err + } + + return license, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/licenses_test.go b/vendor/github.com/google/go-github/github/licenses_test.go new file mode 100644 index 00000000..6ab917dc --- /dev/null +++ b/vendor/github.com/google/go-github/github/licenses_test.go @@ -0,0 +1,70 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestLicensesService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/licenses", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeLicensesPreview) + fmt.Fprint(w, `[{"key":"mit","name":"MIT","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","featured":true}]`) + }) + + licenses, _, err := client.Licenses.List(context.Background()) + if err != nil { + t.Errorf("Licenses.List returned error: %v", err) + } + + want := []*License{{ + Key: String("mit"), + Name: String("MIT"), + SPDXID: String("MIT"), + URL: String("https://api.github.com/licenses/mit"), + Featured: Bool(true), + }} + if !reflect.DeepEqual(licenses, want) { + t.Errorf("Licenses.List returned %+v, want %+v", licenses, want) + } +} + +func TestLicensesService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/licenses/mit", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeLicensesPreview) + fmt.Fprint(w, `{"key":"mit","name":"MIT"}`) + }) + + license, _, err := client.Licenses.Get(context.Background(), "mit") + if err != nil { + t.Errorf("Licenses.Get returned error: %v", err) + } + + want := &License{Key: String("mit"), Name: String("MIT")} + if !reflect.DeepEqual(license, want) { + t.Errorf("Licenses.Get returned %+v, want %+v", license, want) + } +} + +func TestLicensesService_Get_invalidTemplate(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Licenses.Get(context.Background(), "%") + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/messages.go b/vendor/github.com/google/go-github/github/messages.go new file mode 100644 index 00000000..2396fd43 --- /dev/null +++ b/vendor/github.com/google/go-github/github/messages.go @@ -0,0 +1,245 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file provides functions for validating payloads from GitHub Webhooks. +// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github + +package github + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "hash" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +const ( + // sha1Prefix is the prefix used by GitHub before the HMAC hexdigest. + sha1Prefix = "sha1" + // sha256Prefix and sha512Prefix are provided for future compatibility. + sha256Prefix = "sha256" + sha512Prefix = "sha512" + // signatureHeader is the GitHub header key used to pass the HMAC hexdigest. + signatureHeader = "X-Hub-Signature" + // eventTypeHeader is the GitHub header key used to pass the event type. + eventTypeHeader = "X-Github-Event" + // deliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event. + deliveryIDHeader = "X-Github-Delivery" +) + +var ( + // eventTypeMapping maps webhooks types to their corresponding go-github struct types. + eventTypeMapping = map[string]string{ + "commit_comment": "CommitCommentEvent", + "create": "CreateEvent", + "delete": "DeleteEvent", + "deployment": "DeploymentEvent", + "deployment_status": "DeploymentStatusEvent", + "fork": "ForkEvent", + "gollum": "GollumEvent", + "installation": "InstallationEvent", + "installation_repositories": "InstallationRepositoriesEvent", + "issue_comment": "IssueCommentEvent", + "issues": "IssuesEvent", + "label": "LabelEvent", + "marketplace_purchase": "MarketplacePurchaseEvent", + "member": "MemberEvent", + "membership": "MembershipEvent", + "milestone": "MilestoneEvent", + "organization": "OrganizationEvent", + "org_block": "OrgBlockEvent", + "page_build": "PageBuildEvent", + "ping": "PingEvent", + "project": "ProjectEvent", + "project_card": "ProjectCardEvent", + "project_column": "ProjectColumnEvent", + "public": "PublicEvent", + "pull_request_review": "PullRequestReviewEvent", + "pull_request_review_comment": "PullRequestReviewCommentEvent", + "pull_request": "PullRequestEvent", + "push": "PushEvent", + "repository": "RepositoryEvent", + "release": "ReleaseEvent", + "status": "StatusEvent", + "team": "TeamEvent", + "team_add": "TeamAddEvent", + "watch": "WatchEvent", + } +) + +// genMAC generates the HMAC signature for a message provided the secret key +// and hashFunc. +func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte { + mac := hmac.New(hashFunc, key) + mac.Write(message) + return mac.Sum(nil) +} + +// checkMAC reports whether messageMAC is a valid HMAC tag for message. +func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool { + expectedMAC := genMAC(message, key, hashFunc) + return hmac.Equal(messageMAC, expectedMAC) +} + +// messageMAC returns the hex-decoded HMAC tag from the signature and its +// corresponding hash function. +func messageMAC(signature string) ([]byte, func() hash.Hash, error) { + if signature == "" { + return nil, nil, errors.New("missing signature") + } + sigParts := strings.SplitN(signature, "=", 2) + if len(sigParts) != 2 { + return nil, nil, fmt.Errorf("error parsing signature %q", signature) + } + + var hashFunc func() hash.Hash + switch sigParts[0] { + case sha1Prefix: + hashFunc = sha1.New + case sha256Prefix: + hashFunc = sha256.New + case sha512Prefix: + hashFunc = sha512.New + default: + return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0]) + } + + buf, err := hex.DecodeString(sigParts[1]) + if err != nil { + return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err) + } + return buf, hashFunc, nil +} + +// ValidatePayload validates an incoming GitHub Webhook event request +// and returns the (JSON) payload. +// The Content-Type header of the payload can be "application/json" or "application/x-www-form-urlencoded". +// If the Content-Type is neither then an error is returned. +// secretKey is the GitHub Webhook secret message. +// +// Example usage: +// +// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { +// payload, err := github.ValidatePayload(r, s.webhookSecretKey) +// if err != nil { ... } +// // Process payload... +// } +// +func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err error) { + var body []byte // Raw body that GitHub uses to calculate the signature. + + switch ct := r.Header.Get("Content-Type"); ct { + case "application/json": + var err error + if body, err = ioutil.ReadAll(r.Body); err != nil { + return nil, err + } + + // If the content type is application/json, + // the JSON payload is just the original body. + payload = body + + case "application/x-www-form-urlencoded": + // payloadFormParam is the name of the form parameter that the JSON payload + // will be in if a webhook has its content type set to application/x-www-form-urlencoded. + const payloadFormParam = "payload" + + var err error + if body, err = ioutil.ReadAll(r.Body); err != nil { + return nil, err + } + + // If the content type is application/x-www-form-urlencoded, + // the JSON payload will be under the "payload" form param. + form, err := url.ParseQuery(string(body)) + if err != nil { + return nil, err + } + payload = []byte(form.Get(payloadFormParam)) + + default: + return nil, fmt.Errorf("Webhook request has unsupported Content-Type %q", ct) + } + + sig := r.Header.Get(signatureHeader) + if err := validateSignature(sig, body, secretKey); err != nil { + return nil, err + } + return payload, nil +} + +// validateSignature validates the signature for the given payload. +// signature is the GitHub hash signature delivered in the X-Hub-Signature header. +// payload is the JSON payload sent by GitHub Webhooks. +// secretKey is the GitHub Webhook secret message. +// +// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github +func validateSignature(signature string, payload, secretKey []byte) error { + messageMAC, hashFunc, err := messageMAC(signature) + if err != nil { + return err + } + if !checkMAC(payload, messageMAC, secretKey, hashFunc) { + return errors.New("payload signature check failed") + } + return nil +} + +// WebHookType returns the event type of webhook request r. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers +func WebHookType(r *http.Request) string { + return r.Header.Get(eventTypeHeader) +} + +// DeliveryID returns the unique delivery ID of webhook request r. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers +func DeliveryID(r *http.Request) string { + return r.Header.Get(deliveryIDHeader) +} + +// ParseWebHook parses the event payload. For recognized event types, a +// value of the corresponding struct type will be returned (as returned +// by Event.ParsePayload()). An error will be returned for unrecognized event +// types. +// +// Example usage: +// +// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { +// payload, err := github.ValidatePayload(r, s.webhookSecretKey) +// if err != nil { ... } +// event, err := github.ParseWebHook(github.WebHookType(r), payload) +// if err != nil { ... } +// switch event := event.(type) { +// case *github.CommitCommentEvent: +// processCommitCommentEvent(event) +// case *github.CreateEvent: +// processCreateEvent(event) +// ... +// } +// } +// +func ParseWebHook(messageType string, payload []byte) (interface{}, error) { + eventType, ok := eventTypeMapping[messageType] + if !ok { + return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType) + } + + event := Event{ + Type: &eventType, + RawPayload: (*json.RawMessage)(&payload), + } + return event.ParsePayload() +} diff --git a/vendor/github.com/google/go-github/github/messages_test.go b/vendor/github.com/google/go-github/github/messages_test.go new file mode 100644 index 00000000..bb7cbabe --- /dev/null +++ b/vendor/github.com/google/go-github/github/messages_test.go @@ -0,0 +1,330 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "encoding/json" + "net/http" + "net/url" + "reflect" + "strings" + "testing" +) + +func TestValidatePayload(t *testing.T) { + const defaultBody = `{"yo":true}` // All tests below use the default request body and signature. + const defaultSignature = "sha1=126f2c800419c60137ce748d7672e77b65cf16d6" + secretKey := []byte("0123456789abcdef") + tests := []struct { + signature string + eventID string + event string + wantEventID string + wantEvent string + wantPayload string + }{ + // The following tests generate expected errors: + {}, // Missing signature + {signature: "yo"}, // Missing signature prefix + {signature: "sha1=yo"}, // Signature not hex string + {signature: "sha1=012345"}, // Invalid signature + // The following tests expect err=nil: + { + signature: defaultSignature, + eventID: "dead-beef", + event: "ping", + wantEventID: "dead-beef", + wantEvent: "ping", + wantPayload: defaultBody, + }, + { + signature: defaultSignature, + event: "ping", + wantEvent: "ping", + wantPayload: defaultBody, + }, + { + signature: "sha256=b1f8020f5b4cd42042f807dd939015c4a418bc1ff7f604dd55b0a19b5d953d9b", + event: "ping", + wantEvent: "ping", + wantPayload: defaultBody, + }, + { + signature: "sha512=8456767023c1195682e182a23b3f5d19150ecea598fde8cb85918f7281b16079471b1329f92b912c4d8bd7455cb159777db8f29608b20c7c87323ba65ae62e1f", + event: "ping", + wantEvent: "ping", + wantPayload: defaultBody, + }, + } + + for _, test := range tests { + buf := bytes.NewBufferString(defaultBody) + req, err := http.NewRequest("GET", "http://localhost/event", buf) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + if test.signature != "" { + req.Header.Set(signatureHeader, test.signature) + } + req.Header.Set("Content-Type", "application/json") + + got, err := ValidatePayload(req, secretKey) + if err != nil { + if test.wantPayload != "" { + t.Errorf("ValidatePayload(%#v): err = %v, want nil", test, err) + } + continue + } + if string(got) != test.wantPayload { + t.Errorf("ValidatePayload = %q, want %q", got, test.wantPayload) + } + } +} + +func TestValidatePayload_FormGet(t *testing.T) { + payload := `{"yo":true}` + signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368" + secretKey := []byte("0123456789abcdef") + + form := url.Values{} + form.Add("payload", payload) + req, err := http.NewRequest("POST", "http://localhost/event", strings.NewReader(form.Encode())) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + req.PostForm = form + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set(signatureHeader, signature) + + got, err := ValidatePayload(req, secretKey) + if err != nil { + t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err) + } + if string(got) != payload { + t.Errorf("ValidatePayload = %q, want %q", got, payload) + } + + // check that if payload is invalid we get error + req.Header.Set(signatureHeader, "invalid signature") + if _, err = ValidatePayload(req, nil); err == nil { + t.Error("ValidatePayload = nil, want err") + } +} + +func TestValidatePayload_FormPost(t *testing.T) { + payload := `{"yo":true}` + signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368" + secretKey := []byte("0123456789abcdef") + + form := url.Values{} + form.Set("payload", payload) + buf := bytes.NewBufferString(form.Encode()) + req, err := http.NewRequest("POST", "http://localhost/event", buf) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set(signatureHeader, signature) + + got, err := ValidatePayload(req, secretKey) + if err != nil { + t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err) + } + if string(got) != payload { + t.Errorf("ValidatePayload = %q, want %q", got, payload) + } + + // check that if payload is invalid we get error + req.Header.Set(signatureHeader, "invalid signature") + if _, err = ValidatePayload(req, nil); err == nil { + t.Error("ValidatePayload = nil, want err") + } +} + +func TestValidatePayload_InvalidContentType(t *testing.T) { + req, err := http.NewRequest("POST", "http://localhost/event", nil) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + req.Header.Set("Content-Type", "invalid content type") + if _, err = ValidatePayload(req, nil); err == nil { + t.Error("ValidatePayload = nil, want err") + } +} + +func TestParseWebHook(t *testing.T) { + tests := []struct { + payload interface{} + messageType string + }{ + { + payload: &CommitCommentEvent{}, + messageType: "commit_comment", + }, + { + payload: &CreateEvent{}, + messageType: "create", + }, + { + payload: &DeleteEvent{}, + messageType: "delete", + }, + { + payload: &DeploymentEvent{}, + messageType: "deployment", + }, + + { + payload: &DeploymentStatusEvent{}, + messageType: "deployment_status", + }, + { + payload: &ForkEvent{}, + messageType: "fork", + }, + { + payload: &GollumEvent{}, + messageType: "gollum", + }, + { + payload: &InstallationEvent{}, + messageType: "installation", + }, + { + payload: &InstallationRepositoriesEvent{}, + messageType: "installation_repositories", + }, + { + payload: &IssueCommentEvent{}, + messageType: "issue_comment", + }, + { + payload: &IssuesEvent{}, + messageType: "issues", + }, + { + payload: &LabelEvent{}, + messageType: "label", + }, + { + payload: &MarketplacePurchaseEvent{}, + messageType: "marketplace_purchase", + }, + { + payload: &MemberEvent{}, + messageType: "member", + }, + { + payload: &MembershipEvent{}, + messageType: "membership", + }, + { + payload: &MilestoneEvent{}, + messageType: "milestone", + }, + { + payload: &OrganizationEvent{}, + messageType: "organization", + }, + { + payload: &OrgBlockEvent{}, + messageType: "org_block", + }, + { + payload: &PageBuildEvent{}, + messageType: "page_build", + }, + { + payload: &PingEvent{}, + messageType: "ping", + }, + { + payload: &ProjectEvent{}, + messageType: "project", + }, + { + payload: &ProjectCardEvent{}, + messageType: "project_card", + }, + { + payload: &ProjectColumnEvent{}, + messageType: "project_column", + }, + { + payload: &PublicEvent{}, + messageType: "public", + }, + { + payload: &PullRequestEvent{}, + messageType: "pull_request", + }, + { + payload: &PullRequestReviewEvent{}, + messageType: "pull_request_review", + }, + { + payload: &PullRequestReviewCommentEvent{}, + messageType: "pull_request_review_comment", + }, + { + payload: &PushEvent{}, + messageType: "push", + }, + { + payload: &ReleaseEvent{}, + messageType: "release", + }, + { + payload: &RepositoryEvent{}, + messageType: "repository", + }, + { + payload: &StatusEvent{}, + messageType: "status", + }, + { + payload: &TeamEvent{}, + messageType: "team", + }, + { + payload: &TeamAddEvent{}, + messageType: "team_add", + }, + { + payload: &WatchEvent{}, + messageType: "watch", + }, + } + + for _, test := range tests { + p, err := json.Marshal(test.payload) + if err != nil { + t.Fatalf("Marshal(%#v): %v", test.payload, err) + } + got, err := ParseWebHook(test.messageType, p) + if err != nil { + t.Fatalf("ParseWebHook: %v", err) + } + if want := test.payload; !reflect.DeepEqual(got, want) { + t.Errorf("ParseWebHook(%#v, %#v) = %#v, want %#v", test.messageType, p, got, want) + } + } +} + +func TestDeliveryID(t *testing.T) { + id := "8970a780-244e-11e7-91ca-da3aabcb9793" + req, err := http.NewRequest("POST", "http://localhost", nil) + if err != nil { + t.Fatalf("DeliveryID: %v", err) + } + req.Header.Set("X-Github-Delivery", id) + + got := DeliveryID(req) + if got != id { + t.Errorf("DeliveryID(%#v) = %q, want %q", req, got, id) + } +} diff --git a/vendor/github.com/google/go-github/github/migrations.go b/vendor/github.com/google/go-github/github/migrations.go new file mode 100644 index 00000000..90cc1fae --- /dev/null +++ b/vendor/github.com/google/go-github/github/migrations.go @@ -0,0 +1,224 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" + "fmt" + "net/http" + "strings" +) + +// MigrationService provides access to the migration related functions +// in the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/migration/ +type MigrationService service + +// Migration represents a GitHub migration (archival). +type Migration struct { + ID *int64 `json:"id,omitempty"` + GUID *string `json:"guid,omitempty"` + // State is the current state of a migration. + // Possible values are: + // "pending" which means the migration hasn't started yet, + // "exporting" which means the migration is in progress, + // "exported" which means the migration finished successfully, or + // "failed" which means the migration failed. + State *string `json:"state,omitempty"` + // LockRepositories indicates whether repositories are locked (to prevent + // manipulation) while migrating data. + LockRepositories *bool `json:"lock_repositories,omitempty"` + // ExcludeAttachments indicates whether attachments should be excluded from + // the migration (to reduce migration archive file size). + ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` + URL *string `json:"url,omitempty"` + CreatedAt *string `json:"created_at,omitempty"` + UpdatedAt *string `json:"updated_at,omitempty"` + Repositories []*Repository `json:"repositories,omitempty"` +} + +func (m Migration) String() string { + return Stringify(m) +} + +// MigrationOptions specifies the optional parameters to Migration methods. +type MigrationOptions struct { + // LockRepositories indicates whether repositories should be locked (to prevent + // manipulation) while migrating data. + LockRepositories bool + + // ExcludeAttachments indicates whether attachments should be excluded from + // the migration (to reduce migration archive file size). + ExcludeAttachments bool +} + +// startMigration represents the body of a StartMigration request. +type startMigration struct { + // Repositories is a slice of repository names to migrate. + Repositories []string `json:"repositories,omitempty"` + + // LockRepositories indicates whether repositories should be locked (to prevent + // manipulation) while migrating data. + LockRepositories *bool `json:"lock_repositories,omitempty"` + + // ExcludeAttachments indicates whether attachments should be excluded from + // the migration (to reduce migration archive file size). + ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` +} + +// StartMigration starts the generation of a migration archive. +// repos is a slice of repository names to migrate. +// +// GitHub API docs: https://developer.github.com/v3/migration/migrations/#start-a-migration +func (s *MigrationService) StartMigration(ctx context.Context, org string, repos []string, opt *MigrationOptions) (*Migration, *Response, error) { + u := fmt.Sprintf("orgs/%v/migrations", org) + + body := &startMigration{Repositories: repos} + if opt != nil { + body.LockRepositories = Bool(opt.LockRepositories) + body.ExcludeAttachments = Bool(opt.ExcludeAttachments) + } + + req, err := s.client.NewRequest("POST", u, body) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMigrationsPreview) + + m := &Migration{} + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// ListMigrations lists the most recent migrations. +// +// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-a-list-of-migrations +func (s *MigrationService) ListMigrations(ctx context.Context, org string) ([]*Migration, *Response, error) { + u := fmt.Sprintf("orgs/%v/migrations", org) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMigrationsPreview) + + var m []*Migration + resp, err := s.client.Do(ctx, req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// MigrationStatus gets the status of a specific migration archive. +// id is the migration ID. +// +// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-the-status-of-a-migration +func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id int64) (*Migration, *Response, error) { + u := fmt.Sprintf("orgs/%v/migrations/%v", org, id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMigrationsPreview) + + m := &Migration{} + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// MigrationArchiveURL fetches a migration archive URL. +// id is the migration ID. +// +// GitHub API docs: https://developer.github.com/v3/migration/migrations/#download-a-migration-archive +func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string, id int64) (url string, err error) { + u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return "", err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMigrationsPreview) + + s.client.clientMu.Lock() + defer s.client.clientMu.Unlock() + + // Disable the redirect mechanism because AWS fails if the GitHub auth token is provided. + var loc string + saveRedirect := s.client.client.CheckRedirect + s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + loc = req.URL.String() + return errors.New("disable redirect") + } + defer func() { s.client.client.CheckRedirect = saveRedirect }() + + _, err = s.client.Do(ctx, req, nil) // expect error from disable redirect + if err == nil { + return "", errors.New("expected redirect, none provided") + } + if !strings.Contains(err.Error(), "disable redirect") { + return "", err + } + return loc, nil +} + +// DeleteMigration deletes a previous migration archive. +// id is the migration ID. +// +// GitHub API docs: https://developer.github.com/v3/migration/migrations/#delete-a-migration-archive +func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMigrationsPreview) + + return s.client.Do(ctx, req, nil) +} + +// UnlockRepo unlocks a repository that was locked for migration. +// id is the migration ID. +// You should unlock each migrated repository and delete them when the migration +// is complete and you no longer need the source data. +// +// GitHub API docs: https://developer.github.com/v3/migration/migrations/#unlock-a-repository +func (s *MigrationService) UnlockRepo(ctx context.Context, org string, id int64, repo string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/migrations/%v/repos/%v/lock", org, id, repo) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMigrationsPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/migrations_source_import.go b/vendor/github.com/google/go-github/github/migrations_source_import.go new file mode 100644 index 00000000..fd45e780 --- /dev/null +++ b/vendor/github.com/google/go-github/github/migrations_source_import.go @@ -0,0 +1,329 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Import represents a repository import request. +type Import struct { + // The URL of the originating repository. + VCSURL *string `json:"vcs_url,omitempty"` + // The originating VCS type. Can be one of 'subversion', 'git', + // 'mercurial', or 'tfvc'. Without this parameter, the import job will + // take additional time to detect the VCS type before beginning the + // import. This detection step will be reflected in the response. + VCS *string `json:"vcs,omitempty"` + // VCSUsername and VCSPassword are only used for StartImport calls that + // are importing a password-protected repository. + VCSUsername *string `json:"vcs_username,omitempty"` + VCSPassword *string `json:"vcs_password,omitempty"` + // For a tfvc import, the name of the project that is being imported. + TFVCProject *string `json:"tfvc_project,omitempty"` + + // LFS related fields that may be preset in the Import Progress response + + // Describes whether the import has been opted in or out of using Git + // LFS. The value can be 'opt_in', 'opt_out', or 'undecided' if no + // action has been taken. + UseLFS *string `json:"use_lfs,omitempty"` + // Describes whether files larger than 100MB were found during the + // importing step. + HasLargeFiles *bool `json:"has_large_files,omitempty"` + // The total size in gigabytes of files larger than 100MB found in the + // originating repository. + LargeFilesSize *int `json:"large_files_size,omitempty"` + // The total number of files larger than 100MB found in the originating + // repository. To see a list of these files, call LargeFiles. + LargeFilesCount *int `json:"large_files_count,omitempty"` + + // Identifies the current status of an import. An import that does not + // have errors will progress through these steps: + // + // detecting - the "detection" step of the import is in progress + // because the request did not include a VCS parameter. The + // import is identifying the type of source control present at + // the URL. + // importing - the "raw" step of the import is in progress. This is + // where commit data is fetched from the original repository. + // The import progress response will include CommitCount (the + // total number of raw commits that will be imported) and + // Percent (0 - 100, the current progress through the import). + // mapping - the "rewrite" step of the import is in progress. This + // is where SVN branches are converted to Git branches, and + // where author updates are applied. The import progress + // response does not include progress information. + // pushing - the "push" step of the import is in progress. This is + // where the importer updates the repository on GitHub. The + // import progress response will include PushPercent, which is + // the percent value reported by git push when it is "Writing + // objects". + // complete - the import is complete, and the repository is ready + // on GitHub. + // + // If there are problems, you will see one of these in the status field: + // + // auth_failed - the import requires authentication in order to + // connect to the original repository. Make an UpdateImport + // request, and include VCSUsername and VCSPassword. + // error - the import encountered an error. The import progress + // response will include the FailedStep and an error message. + // Contact GitHub support for more information. + // detection_needs_auth - the importer requires authentication for + // the originating repository to continue detection. Make an + // UpdatImport request, and include VCSUsername and + // VCSPassword. + // detection_found_nothing - the importer didn't recognize any + // source control at the URL. + // detection_found_multiple - the importer found several projects + // or repositories at the provided URL. When this is the case, + // the Import Progress response will also include a + // ProjectChoices field with the possible project choices as + // values. Make an UpdateImport request, and include VCS and + // (if applicable) TFVCProject. + Status *string `json:"status,omitempty"` + CommitCount *int `json:"commit_count,omitempty"` + StatusText *string `json:"status_text,omitempty"` + AuthorsCount *int `json:"authors_count,omitempty"` + Percent *int `json:"percent,omitempty"` + PushPercent *int `json:"push_percent,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + AuthorsURL *string `json:"authors_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + Message *string `json:"message,omitempty"` + FailedStep *string `json:"failed_step,omitempty"` + + // Human readable display name, provided when the Import appears as + // part of ProjectChoices. + HumanName *string `json:"human_name,omitempty"` + + // When the importer finds several projects or repositories at the + // provided URLs, this will identify the available choices. Call + // UpdateImport with the selected Import value. + ProjectChoices []Import `json:"project_choices,omitempty"` +} + +func (i Import) String() string { + return Stringify(i) +} + +// SourceImportAuthor identifies an author imported from a source repository. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors +type SourceImportAuthor struct { + ID *int64 `json:"id,omitempty"` + RemoteID *string `json:"remote_id,omitempty"` + RemoteName *string `json:"remote_name,omitempty"` + Email *string `json:"email,omitempty"` + Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` + ImportURL *string `json:"import_url,omitempty"` +} + +func (a SourceImportAuthor) String() string { + return Stringify(a) +} + +// LargeFile identifies a file larger than 100MB found during a repository import. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files +type LargeFile struct { + RefName *string `json:"ref_name,omitempty"` + Path *string `json:"path,omitempty"` + OID *string `json:"oid,omitempty"` + Size *int `json:"size,omitempty"` +} + +func (f LargeFile) String() string { + return Stringify(f) +} + +// StartImport initiates a repository import. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#start-an-import +func (s *MigrationService) StartImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import", owner, repo) + req, err := s.client.NewRequest("PUT", u, in) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + out := new(Import) + resp, err := s.client.Do(ctx, req, out) + if err != nil { + return nil, resp, err + } + + return out, resp, nil +} + +// ImportProgress queries for the status and progress of an ongoing repository import. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-import-progress +func (s *MigrationService) ImportProgress(ctx context.Context, owner, repo string) (*Import, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + out := new(Import) + resp, err := s.client.Do(ctx, req, out) + if err != nil { + return nil, resp, err + } + + return out, resp, nil +} + +// UpdateImport initiates a repository import. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#update-existing-import +func (s *MigrationService) UpdateImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import", owner, repo) + req, err := s.client.NewRequest("PATCH", u, in) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + out := new(Import) + resp, err := s.client.Do(ctx, req, out) + if err != nil { + return nil, resp, err + } + + return out, resp, nil +} + +// CommitAuthors gets the authors mapped from the original repository. +// +// Each type of source control system represents authors in a different way. +// For example, a Git commit author has a display name and an email address, +// but a Subversion commit author just has a username. The GitHub Importer will +// make the author information valid, but the author might not be correct. For +// example, it will change the bare Subversion username "hubot" into something +// like "hubot ". +// +// This method and MapCommitAuthor allow you to provide correct Git author +// information. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors +func (s *MigrationService) CommitAuthors(ctx context.Context, owner, repo string) ([]*SourceImportAuthor, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import/authors", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + var authors []*SourceImportAuthor + resp, err := s.client.Do(ctx, req, &authors) + if err != nil { + return nil, resp, err + } + + return authors, resp, nil +} + +// MapCommitAuthor updates an author's identity for the import. Your +// application can continue updating authors any time before you push new +// commits to the repository. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#map-a-commit-author +func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int64, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import/authors/%v", owner, repo, id) + req, err := s.client.NewRequest("PATCH", u, author) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + out := new(SourceImportAuthor) + resp, err := s.client.Do(ctx, req, out) + if err != nil { + return nil, resp, err + } + + return out, resp, nil +} + +// SetLFSPreference sets whether imported repositories should use Git LFS for +// files larger than 100MB. Only the UseLFS field on the provided Import is +// used. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#set-git-lfs-preference +func (s *MigrationService) SetLFSPreference(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import/lfs", owner, repo) + req, err := s.client.NewRequest("PATCH", u, in) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + out := new(Import) + resp, err := s.client.Do(ctx, req, out) + if err != nil { + return nil, resp, err + } + + return out, resp, nil +} + +// LargeFiles lists files larger than 100MB found during the import. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files +func (s *MigrationService) LargeFiles(ctx context.Context, owner, repo string) ([]*LargeFile, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import/large_files", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + var files []*LargeFile + resp, err := s.client.Do(ctx, req, &files) + if err != nil { + return nil, resp, err + } + + return files, resp, nil +} + +// CancelImport stops an import for a repository. +// +// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#cancel-an-import +func (s *MigrationService) CancelImport(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/import", owner, repo) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeImportPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/migrations_source_import_test.go b/vendor/github.com/google/go-github/github/migrations_source_import_test.go new file mode 100644 index 00000000..e9e9be06 --- /dev/null +++ b/vendor/github.com/google/go-github/github/migrations_source_import_test.go @@ -0,0 +1,226 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestMigrationService_StartImport(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Import{ + VCS: String("git"), + VCSURL: String("url"), + VCSUsername: String("u"), + VCSPassword: String("p"), + } + + mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { + v := new(Import) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeImportPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"status":"importing"}`) + }) + + got, _, err := client.Migrations.StartImport(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("StartImport returned error: %v", err) + } + want := &Import{Status: String("importing")} + if !reflect.DeepEqual(got, want) { + t.Errorf("StartImport = %+v, want %+v", got, want) + } +} + +func TestMigrationService_ImportProgress(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeImportPreview) + fmt.Fprint(w, `{"status":"complete"}`) + }) + + got, _, err := client.Migrations.ImportProgress(context.Background(), "o", "r") + if err != nil { + t.Errorf("ImportProgress returned error: %v", err) + } + want := &Import{Status: String("complete")} + if !reflect.DeepEqual(got, want) { + t.Errorf("ImportProgress = %+v, want %+v", got, want) + } +} + +func TestMigrationService_UpdateImport(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Import{ + VCS: String("git"), + VCSURL: String("url"), + VCSUsername: String("u"), + VCSPassword: String("p"), + } + + mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { + v := new(Import) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeImportPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"status":"importing"}`) + }) + + got, _, err := client.Migrations.UpdateImport(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("UpdateImport returned error: %v", err) + } + want := &Import{Status: String("importing")} + if !reflect.DeepEqual(got, want) { + t.Errorf("UpdateImport = %+v, want %+v", got, want) + } +} + +func TestMigrationService_CommitAuthors(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/import/authors", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeImportPreview) + fmt.Fprint(w, `[{"id":1,"name":"a"},{"id":2,"name":"b"}]`) + }) + + got, _, err := client.Migrations.CommitAuthors(context.Background(), "o", "r") + if err != nil { + t.Errorf("CommitAuthors returned error: %v", err) + } + want := []*SourceImportAuthor{ + {ID: Int64(1), Name: String("a")}, + {ID: Int64(2), Name: String("b")}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("CommitAuthors = %+v, want %+v", got, want) + } +} + +func TestMigrationService_MapCommitAuthor(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &SourceImportAuthor{Name: String("n"), Email: String("e")} + + mux.HandleFunc("/repos/o/r/import/authors/1", func(w http.ResponseWriter, r *http.Request) { + v := new(SourceImportAuthor) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeImportPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id": 1}`) + }) + + got, _, err := client.Migrations.MapCommitAuthor(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("MapCommitAuthor returned error: %v", err) + } + want := &SourceImportAuthor{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("MapCommitAuthor = %+v, want %+v", got, want) + } +} + +func TestMigrationService_SetLFSPreference(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Import{UseLFS: String("opt_in")} + + mux.HandleFunc("/repos/o/r/import/lfs", func(w http.ResponseWriter, r *http.Request) { + v := new(Import) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeImportPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"status":"importing"}`) + }) + + got, _, err := client.Migrations.SetLFSPreference(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("SetLFSPreference returned error: %v", err) + } + want := &Import{Status: String("importing")} + if !reflect.DeepEqual(got, want) { + t.Errorf("SetLFSPreference = %+v, want %+v", got, want) + } +} + +func TestMigrationService_LargeFiles(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/import/large_files", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeImportPreview) + fmt.Fprint(w, `[{"oid":"a"},{"oid":"b"}]`) + }) + + got, _, err := client.Migrations.LargeFiles(context.Background(), "o", "r") + if err != nil { + t.Errorf("LargeFiles returned error: %v", err) + } + want := []*LargeFile{ + {OID: String("a")}, + {OID: String("b")}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("LargeFiles = %+v, want %+v", got, want) + } +} + +func TestMigrationService_CancelImport(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeImportPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Migrations.CancelImport(context.Background(), "o", "r") + if err != nil { + t.Errorf("CancelImport returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/migrations_test.go b/vendor/github.com/google/go-github/github/migrations_test.go new file mode 100644 index 00000000..687f89a3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/migrations_test.go @@ -0,0 +1,178 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestMigrationService_StartMigration(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeMigrationsPreview) + + w.WriteHeader(http.StatusCreated) + w.Write(migrationJSON) + }) + + opt := &MigrationOptions{ + LockRepositories: true, + ExcludeAttachments: false, + } + got, _, err := client.Migrations.StartMigration(context.Background(), "o", []string{"r"}, opt) + if err != nil { + t.Errorf("StartMigration returned error: %v", err) + } + if want := wantMigration; !reflect.DeepEqual(got, want) { + t.Errorf("StartMigration = %+v, want %+v", got, want) + } +} + +func TestMigrationService_ListMigrations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMigrationsPreview) + + w.WriteHeader(http.StatusOK) + w.Write([]byte(fmt.Sprintf("[%s]", migrationJSON))) + }) + + got, _, err := client.Migrations.ListMigrations(context.Background(), "o") + if err != nil { + t.Errorf("ListMigrations returned error: %v", err) + } + if want := []*Migration{wantMigration}; !reflect.DeepEqual(got, want) { + t.Errorf("ListMigrations = %+v, want %+v", got, want) + } +} + +func TestMigrationService_MigrationStatus(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/migrations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMigrationsPreview) + + w.WriteHeader(http.StatusOK) + w.Write(migrationJSON) + }) + + got, _, err := client.Migrations.MigrationStatus(context.Background(), "o", 1) + if err != nil { + t.Errorf("MigrationStatus returned error: %v", err) + } + if want := wantMigration; !reflect.DeepEqual(got, want) { + t.Errorf("MigrationStatus = %+v, want %+v", got, want) + } +} + +func TestMigrationService_MigrationArchiveURL(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMigrationsPreview) + + http.Redirect(w, r, "/yo", http.StatusFound) + }) + mux.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + w.Write([]byte("0123456789abcdef")) + }) + + got, err := client.Migrations.MigrationArchiveURL(context.Background(), "o", 1) + if err != nil { + t.Errorf("MigrationStatus returned error: %v", err) + } + if want := "/yo"; !strings.HasSuffix(got, want) { + t.Errorf("MigrationArchiveURL = %+v, want %+v", got, want) + } +} + +func TestMigrationService_DeleteMigration(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeMigrationsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + if _, err := client.Migrations.DeleteMigration(context.Background(), "o", 1); err != nil { + t.Errorf("DeleteMigration returned error: %v", err) + } +} + +func TestMigrationService_UnlockRepo(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/migrations/1/repos/r/lock", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeMigrationsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + if _, err := client.Migrations.UnlockRepo(context.Background(), "o", 1, "r"); err != nil { + t.Errorf("UnlockRepo returned error: %v", err) + } +} + +var migrationJSON = []byte(`{ + "id": 79, + "guid": "0b989ba4-242f-11e5-81e1-c7b6966d2516", + "state": "pending", + "lock_repositories": true, + "exclude_attachments": false, + "url": "https://api.github.com/orgs/octo-org/migrations/79", + "created_at": "2015-07-06T15:33:38-07:00", + "updated_at": "2015-07-06T15:33:38-07:00", + "repositories": [ + { + "id": 1296269, + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "description": "This your first repo!" + } + ] +}`) + +var wantMigration = &Migration{ + ID: Int64(79), + GUID: String("0b989ba4-242f-11e5-81e1-c7b6966d2516"), + State: String("pending"), + LockRepositories: Bool(true), + ExcludeAttachments: Bool(false), + URL: String("https://api.github.com/orgs/octo-org/migrations/79"), + CreatedAt: String("2015-07-06T15:33:38-07:00"), + UpdatedAt: String("2015-07-06T15:33:38-07:00"), + Repositories: []*Repository{ + { + ID: Int64(1296269), + Name: String("Hello-World"), + FullName: String("octocat/Hello-World"), + Description: String("This your first repo!"), + }, + }, +} diff --git a/vendor/github.com/google/go-github/github/misc.go b/vendor/github.com/google/go-github/github/misc.go new file mode 100644 index 00000000..5b8082d3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/misc.go @@ -0,0 +1,253 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "fmt" + "net/url" +) + +// MarkdownOptions specifies optional parameters to the Markdown method. +type MarkdownOptions struct { + // Mode identifies the rendering mode. Possible values are: + // markdown - render a document as plain Markdown, just like + // README files are rendered. + // + // gfm - to render a document as user-content, e.g. like user + // comments or issues are rendered. In GFM mode, hard line breaks are + // always taken into account, and issue and user mentions are linked + // accordingly. + // + // Default is "markdown". + Mode string + + // Context identifies the repository context. Only taken into account + // when rendering as "gfm". + Context string +} + +type markdownRequest struct { + Text *string `json:"text,omitempty"` + Mode *string `json:"mode,omitempty"` + Context *string `json:"context,omitempty"` +} + +// Markdown renders an arbitrary Markdown document. +// +// GitHub API docs: https://developer.github.com/v3/markdown/ +func (c *Client) Markdown(ctx context.Context, text string, opt *MarkdownOptions) (string, *Response, error) { + request := &markdownRequest{Text: String(text)} + if opt != nil { + if opt.Mode != "" { + request.Mode = String(opt.Mode) + } + if opt.Context != "" { + request.Context = String(opt.Context) + } + } + + req, err := c.NewRequest("POST", "markdown", request) + if err != nil { + return "", nil, err + } + + buf := new(bytes.Buffer) + resp, err := c.Do(ctx, req, buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// ListEmojis returns the emojis available to use on GitHub. +// +// GitHub API docs: https://developer.github.com/v3/emojis/ +func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) { + req, err := c.NewRequest("GET", "emojis", nil) + if err != nil { + return nil, nil, err + } + + var emoji map[string]string + resp, err := c.Do(ctx, req, &emoji) + if err != nil { + return nil, resp, err + } + + return emoji, resp, nil +} + +// CodeOfConduct represents a code of conduct. +type CodeOfConduct struct { + Name *string `json:"name,omitempty"` + Key *string `json:"key,omitempty"` + URL *string `json:"url,omitempty"` + Body *string `json:"body,omitempty"` +} + +func (c *CodeOfConduct) String() string { + return Stringify(c) +} + +// ListCodesOfConduct returns all codes of conduct. +// +// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#list-all-codes-of-conduct +func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error) { + req, err := c.NewRequest("GET", "codes_of_conduct", nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeCodesOfConductPreview) + + var cs []*CodeOfConduct + resp, err := c.Do(ctx, req, &cs) + if err != nil { + return nil, resp, err + } + + return cs, resp, nil +} + +// GetCodeOfConduct returns an individual code of conduct. +// +// https://developer.github.com/v3/codes_of_conduct/#get-an-individual-code-of-conduct +func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { + u := fmt.Sprintf("codes_of_conduct/%s", key) + req, err := c.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeCodesOfConductPreview) + + coc := new(CodeOfConduct) + resp, err := c.Do(ctx, req, coc) + if err != nil { + return nil, resp, err + } + + return coc, resp, nil +} + +// APIMeta represents metadata about the GitHub API. +type APIMeta struct { + // An Array of IP addresses in CIDR format specifying the addresses + // that incoming service hooks will originate from on GitHub.com. + Hooks []string `json:"hooks,omitempty"` + + // An Array of IP addresses in CIDR format specifying the Git servers + // for GitHub.com. + Git []string `json:"git,omitempty"` + + // Whether authentication with username and password is supported. + // (GitHub Enterprise instances using CAS or OAuth for authentication + // will return false. Features like Basic Authentication with a + // username and password, sudo mode, and two-factor authentication are + // not supported on these servers.) + VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub Pages websites. + Pages []string `json:"pages,omitempty"` +} + +// APIMeta returns information about GitHub.com, the service. Or, if you access +// this endpoint on your organization’s GitHub Enterprise installation, this +// endpoint provides information about that installation. +// +// GitHub API docs: https://developer.github.com/v3/meta/ +func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) { + req, err := c.NewRequest("GET", "meta", nil) + if err != nil { + return nil, nil, err + } + + meta := new(APIMeta) + resp, err := c.Do(ctx, req, meta) + if err != nil { + return nil, resp, err + } + + return meta, resp, nil +} + +// Octocat returns an ASCII art octocat with the specified message in a speech +// bubble. If message is empty, a random zen phrase is used. +func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) { + u := "octocat" + if message != "" { + u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) + } + + req, err := c.NewRequest("GET", u, nil) + if err != nil { + return "", nil, err + } + + buf := new(bytes.Buffer) + resp, err := c.Do(ctx, req, buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// Zen returns a random line from The Zen of GitHub. +// +// see also: http://warpspire.com/posts/taste/ +func (c *Client) Zen(ctx context.Context) (string, *Response, error) { + req, err := c.NewRequest("GET", "zen", nil) + if err != nil { + return "", nil, err + } + + buf := new(bytes.Buffer) + resp, err := c.Do(ctx, req, buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// ServiceHook represents a hook that has configuration settings, a list of +// available events, and default events. +type ServiceHook struct { + Name *string `json:"name,omitempty"` + Events []string `json:"events,omitempty"` + SupportedEvents []string `json:"supported_events,omitempty"` + Schema [][]string `json:"schema,omitempty"` +} + +func (s *ServiceHook) String() string { + return Stringify(s) +} + +// ListServiceHooks lists all of the available service hooks. +// +// GitHub API docs: https://developer.github.com/webhooks/#services +func (c *Client) ListServiceHooks(ctx context.Context) ([]*ServiceHook, *Response, error) { + u := "hooks" + req, err := c.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hooks []*ServiceHook + resp, err := c.Do(ctx, req, &hooks) + if err != nil { + return nil, resp, err + } + + return hooks, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/misc_test.go b/vendor/github.com/google/go-github/github/misc_test.go new file mode 100644 index 00000000..924e7b46 --- /dev/null +++ b/vendor/github.com/google/go-github/github/misc_test.go @@ -0,0 +1,232 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestMarkdown(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &markdownRequest{ + Text: String("# text #"), + Mode: String("gfm"), + Context: String("google/go-github"), + } + mux.HandleFunc("/markdown", func(w http.ResponseWriter, r *http.Request) { + v := new(markdownRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `

text

`) + }) + + md, _, err := client.Markdown(context.Background(), "# text #", &MarkdownOptions{ + Mode: "gfm", + Context: "google/go-github", + }) + if err != nil { + t.Errorf("Markdown returned error: %v", err) + } + + if want := "

text

"; want != md { + t.Errorf("Markdown returned %+v, want %+v", md, want) + } +} + +func TestListEmojis(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"+1": "+1.png"}`) + }) + + emoji, _, err := client.ListEmojis(context.Background()) + if err != nil { + t.Errorf("ListEmojis returned error: %v", err) + } + + want := map[string]string{"+1": "+1.png"} + if !reflect.DeepEqual(want, emoji) { + t.Errorf("ListEmojis returned %+v, want %+v", emoji, want) + } +} + +func TestListCodesOfConduct(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/codes_of_conduct", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) + fmt.Fprint(w, `[{ + "key": "key", + "name": "name", + "url": "url"} + ]`) + }) + + cs, _, err := client.ListCodesOfConduct(context.Background()) + if err != nil { + t.Errorf("ListCodesOfConduct returned error: %v", err) + } + + want := []*CodeOfConduct{ + { + Key: String("key"), + Name: String("name"), + URL: String("url"), + }} + if !reflect.DeepEqual(want, cs) { + t.Errorf("ListCodesOfConduct returned %+v, want %+v", cs, want) + } +} + +func TestGetCodeOfConduct(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/codes_of_conduct/k", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) + fmt.Fprint(w, `{ + "key": "key", + "name": "name", + "url": "url", + "body": "body"}`, + ) + }) + + coc, _, err := client.GetCodeOfConduct(context.Background(), "k") + if err != nil { + t.Errorf("ListCodesOfConduct returned error: %v", err) + } + + want := &CodeOfConduct{ + Key: String("key"), + Name: String("name"), + URL: String("url"), + Body: String("body"), + } + if !reflect.DeepEqual(want, coc) { + t.Errorf("GetCodeOfConductByKey returned %+v, want %+v", coc, want) + } +} + +func TestAPIMeta(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"hooks":["h"], "git":["g"], "pages":["p"], "verifiable_password_authentication": true}`) + }) + + meta, _, err := client.APIMeta(context.Background()) + if err != nil { + t.Errorf("APIMeta returned error: %v", err) + } + + want := &APIMeta{ + Hooks: []string{"h"}, + Git: []string{"g"}, + Pages: []string{"p"}, + VerifiablePasswordAuthentication: Bool(true), + } + if !reflect.DeepEqual(want, meta) { + t.Errorf("APIMeta returned %+v, want %+v", meta, want) + } +} + +func TestOctocat(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := "input" + output := "sample text" + + mux.HandleFunc("/octocat", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"s": input}) + w.Header().Set("Content-Type", "application/octocat-stream") + fmt.Fprint(w, output) + }) + + got, _, err := client.Octocat(context.Background(), input) + if err != nil { + t.Errorf("Octocat returned error: %v", err) + } + + if want := output; got != want { + t.Errorf("Octocat returned %+v, want %+v", got, want) + } +} + +func TestZen(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + output := "sample text" + + mux.HandleFunc("/zen", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.Header().Set("Content-Type", "text/plain;charset=utf-8") + fmt.Fprint(w, output) + }) + + got, _, err := client.Zen(context.Background()) + if err != nil { + t.Errorf("Zen returned error: %v", err) + } + + if want := output; got != want { + t.Errorf("Zen returned %+v, want %+v", got, want) + } +} + +func TestListServiceHooks(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/hooks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "name":"n", + "events":["e"], + "supported_events":["s"], + "schema":[ + ["a", "b"] + ] + }]`) + }) + + hooks, _, err := client.ListServiceHooks(context.Background()) + if err != nil { + t.Errorf("ListServiceHooks returned error: %v", err) + } + + want := []*ServiceHook{{ + Name: String("n"), + Events: []string{"e"}, + SupportedEvents: []string{"s"}, + Schema: [][]string{{"a", "b"}}, + }} + if !reflect.DeepEqual(hooks, want) { + t.Errorf("ListServiceHooks returned %+v, want %+v", hooks, want) + } +} diff --git a/vendor/github.com/google/go-github/github/orgs.go b/vendor/github.com/google/go-github/github/orgs.go new file mode 100644 index 00000000..976a5fca --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs.go @@ -0,0 +1,209 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// OrganizationsService provides access to the organization related functions +// in the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/orgs/ +type OrganizationsService service + +// Organization represents a GitHub organization account. +type Organization struct { + Login *string `json:"login,omitempty"` + ID *int64 `json:"id,omitempty"` + AvatarURL *string `json:"avatar_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Name *string `json:"name,omitempty"` + Company *string `json:"company,omitempty"` + Blog *string `json:"blog,omitempty"` + Location *string `json:"location,omitempty"` + Email *string `json:"email,omitempty"` + Description *string `json:"description,omitempty"` + PublicRepos *int `json:"public_repos,omitempty"` + PublicGists *int `json:"public_gists,omitempty"` + Followers *int `json:"followers,omitempty"` + Following *int `json:"following,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + TotalPrivateRepos *int `json:"total_private_repos,omitempty"` + OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` + PrivateGists *int `json:"private_gists,omitempty"` + DiskUsage *int `json:"disk_usage,omitempty"` + Collaborators *int `json:"collaborators,omitempty"` + BillingEmail *string `json:"billing_email,omitempty"` + Type *string `json:"type,omitempty"` + Plan *Plan `json:"plan,omitempty"` + NodeID *string `json:"node_id,omitempty"` + + // API URLs + URL *string `json:"url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + HooksURL *string `json:"hooks_url,omitempty"` + IssuesURL *string `json:"issues_url,omitempty"` + MembersURL *string `json:"members_url,omitempty"` + PublicMembersURL *string `json:"public_members_url,omitempty"` + ReposURL *string `json:"repos_url,omitempty"` +} + +func (o Organization) String() string { + return Stringify(o) +} + +// Plan represents the payment plan for an account. See plans at https://github.com/plans. +type Plan struct { + Name *string `json:"name,omitempty"` + Space *int `json:"space,omitempty"` + Collaborators *int `json:"collaborators,omitempty"` + PrivateRepos *int `json:"private_repos,omitempty"` +} + +func (p Plan) String() string { + return Stringify(p) +} + +// OrganizationsListOptions specifies the optional parameters to the +// OrganizationsService.ListAll method. +type OrganizationsListOptions struct { + // Since filters Organizations by ID. + Since int `url:"since,omitempty"` + + ListOptions +} + +// ListAll lists all organizations, in the order that they were created on GitHub. +// +// Note: Pagination is powered exclusively by the since parameter. To continue +// listing the next set of organizations, use the ID of the last-returned organization +// as the opts.Since parameter for the next call. +// +// GitHub API docs: https://developer.github.com/v3/orgs/#list-all-organizations +func (s *OrganizationsService) ListAll(ctx context.Context, opt *OrganizationsListOptions) ([]*Organization, *Response, error) { + u, err := addOptions("organizations", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + orgs := []*Organization{} + resp, err := s.client.Do(ctx, req, &orgs) + if err != nil { + return nil, resp, err + } + return orgs, resp, nil +} + +// List the organizations for a user. Passing the empty string will list +// organizations for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/orgs/#list-user-organizations +func (s *OrganizationsService) List(ctx context.Context, user string, opt *ListOptions) ([]*Organization, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/orgs", user) + } else { + u = "user/orgs" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var orgs []*Organization + resp, err := s.client.Do(ctx, req, &orgs) + if err != nil { + return nil, resp, err + } + + return orgs, resp, nil +} + +// Get fetches an organization by name. +// +// GitHub API docs: https://developer.github.com/v3/orgs/#get-an-organization +func (s *OrganizationsService) Get(ctx context.Context, org string) (*Organization, *Response, error) { + u := fmt.Sprintf("orgs/%v", org) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + organization := new(Organization) + resp, err := s.client.Do(ctx, req, organization) + if err != nil { + return nil, resp, err + } + + return organization, resp, nil +} + +// GetByID fetches an organization. +// +// Note: GetByID uses the undocumented GitHub API endpoint /organizations/:id. +func (s *OrganizationsService) GetByID(ctx context.Context, id int64) (*Organization, *Response, error) { + u := fmt.Sprintf("organizations/%d", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + organization := new(Organization) + resp, err := s.client.Do(ctx, req, organization) + if err != nil { + return nil, resp, err + } + + return organization, resp, nil +} + +// Edit an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/#edit-an-organization +func (s *OrganizationsService) Edit(ctx context.Context, name string, org *Organization) (*Organization, *Response, error) { + u := fmt.Sprintf("orgs/%v", name) + req, err := s.client.NewRequest("PATCH", u, org) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + o := new(Organization) + resp, err := s.client.Do(ctx, req, o) + if err != nil { + return nil, resp, err + } + + return o, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/orgs_hooks.go b/vendor/github.com/google/go-github/github/orgs_hooks.go new file mode 100644 index 00000000..4fc692e0 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_hooks.go @@ -0,0 +1,107 @@ +// Copyright 2015 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListHooks lists all Hooks for the specified organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#list-hooks +func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opt *ListOptions) ([]*Hook, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hooks []*Hook + resp, err := s.client.Do(ctx, req, &hooks) + if err != nil { + return nil, resp, err + } + + return hooks, resp, nil +} + +// GetHook returns a single specified Hook. +// +// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook +func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int) (*Hook, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + hook := new(Hook) + resp, err := s.client.Do(ctx, req, hook) + return hook, resp, err +} + +// CreateHook creates a Hook for the specified org. +// Name and Config are required fields. +// +// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook +func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook *Hook) (*Hook, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks", org) + req, err := s.client.NewRequest("POST", u, hook) + if err != nil { + return nil, nil, err + } + + h := new(Hook) + resp, err := s.client.Do(ctx, req, h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} + +// EditHook updates a specified Hook. +// +// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#edit-a-hook +func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int, hook *Hook) (*Hook, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) + req, err := s.client.NewRequest("PATCH", u, hook) + if err != nil { + return nil, nil, err + } + h := new(Hook) + resp, err := s.client.Do(ctx, req, h) + return h, resp, err +} + +// PingHook triggers a 'ping' event to be sent to the Hook. +// +// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook +func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int) (*Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%d/pings", org, id) + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// DeleteHook deletes a specified Hook. +// +// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#delete-a-hook +func (s *OrganizationsService) DeleteHook(ctx context.Context, org string, id int) (*Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/orgs_hooks_test.go b/vendor/github.com/google/go-github/github/orgs_hooks_test.go new file mode 100644 index 00000000..1adfef2b --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_hooks_test.go @@ -0,0 +1,147 @@ +// Copyright 2015 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestOrganizationsService_ListHooks(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/hooks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + + hooks, _, err := client.Organizations.ListHooks(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListHooks returned error: %v", err) + } + + want := []*Hook{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(hooks, want) { + t.Errorf("Organizations.ListHooks returned %+v, want %+v", hooks, want) + } +} + +func TestOrganizationsService_ListHooks_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.ListHooks(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_GetHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + hook, _, err := client.Organizations.GetHook(context.Background(), "o", 1) + if err != nil { + t.Errorf("Organizations.GetHook returned error: %v", err) + } + + want := &Hook{ID: Int64(1)} + if !reflect.DeepEqual(hook, want) { + t.Errorf("Organizations.GetHook returned %+v, want %+v", hook, want) + } +} + +func TestOrganizationsService_GetHook_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.GetHook(context.Background(), "%", 1) + testURLParseError(t, err) +} + +func TestOrganizationsService_EditHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Hook{Name: String("t")} + + mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { + v := new(Hook) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + hook, _, err := client.Organizations.EditHook(context.Background(), "o", 1, input) + if err != nil { + t.Errorf("Organizations.EditHook returned error: %v", err) + } + + want := &Hook{ID: Int64(1)} + if !reflect.DeepEqual(hook, want) { + t.Errorf("Organizations.EditHook returned %+v, want %+v", hook, want) + } +} + +func TestOrganizationsService_EditHook_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.EditHook(context.Background(), "%", 1, nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_PingHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + _, err := client.Organizations.PingHook(context.Background(), "o", 1) + if err != nil { + t.Errorf("Organizations.PingHook returned error: %v", err) + } +} + +func TestOrganizationsService_DeleteHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Organizations.DeleteHook(context.Background(), "o", 1) + if err != nil { + t.Errorf("Organizations.DeleteHook returned error: %v", err) + } +} + +func TestOrganizationsService_DeleteHook_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Organizations.DeleteHook(context.Background(), "%", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/orgs_members.go b/vendor/github.com/google/go-github/github/orgs_members.go new file mode 100644 index 00000000..d0ea6a98 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_members.go @@ -0,0 +1,299 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Membership represents the status of a user's membership in an organization or team. +type Membership struct { + URL *string `json:"url,omitempty"` + + // State is the user's status within the organization or team. + // Possible values are: "active", "pending" + State *string `json:"state,omitempty"` + + // Role identifies the user's role within the organization or team. + // Possible values for organization membership: + // member - non-owner organization member + // admin - organization owner + // + // Possible values for team membership are: + // member - a normal member of the team + // maintainer - a team maintainer. Able to add/remove other team + // members, promote other team members to team + // maintainer, and edit the team’s name and description + Role *string `json:"role,omitempty"` + + // For organization membership, the API URL of the organization. + OrganizationURL *string `json:"organization_url,omitempty"` + + // For organization membership, the organization the membership is for. + Organization *Organization `json:"organization,omitempty"` + + // For organization membership, the user the membership is for. + User *User `json:"user,omitempty"` +} + +func (m Membership) String() string { + return Stringify(m) +} + +// ListMembersOptions specifies optional parameters to the +// OrganizationsService.ListMembers method. +type ListMembersOptions struct { + // If true (or if the authenticated user is not an owner of the + // organization), list only publicly visible members. + PublicOnly bool `url:"-"` + + // Filter members returned in the list. Possible values are: + // 2fa_disabled, all. Default is "all". + Filter string `url:"filter,omitempty"` + + // Role filters members returned by their role in the organization. + // Possible values are: + // all - all members of the organization, regardless of role + // admin - organization owners + // member - non-organization members + // + // Default is "all". + Role string `url:"role,omitempty"` + + ListOptions +} + +// ListMembers lists the members for an organization. If the authenticated +// user is an owner of the organization, this will return both concealed and +// public members, otherwise it will only return public members. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#members-list +func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opt *ListMembersOptions) ([]*User, *Response, error) { + var u string + if opt != nil && opt.PublicOnly { + u = fmt.Sprintf("orgs/%v/public_members", org) + } else { + u = fmt.Sprintf("orgs/%v/members", org) + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var members []*User + resp, err := s.client.Do(ctx, req, &members) + if err != nil { + return nil, resp, err + } + + return members, resp, nil +} + +// IsMember checks if a user is a member of an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-membership +func (s *OrganizationsService) IsMember(ctx context.Context, org, user string) (bool, *Response, error) { + u := fmt.Sprintf("orgs/%v/members/%v", org, user) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + member, err := parseBoolResponse(err) + return member, resp, err +} + +// IsPublicMember checks if a user is a public member of an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-public-membership +func (s *OrganizationsService) IsPublicMember(ctx context.Context, org, user string) (bool, *Response, error) { + u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + member, err := parseBoolResponse(err) + return member, resp, err +} + +// RemoveMember removes a user from all teams of an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-a-member +func (s *OrganizationsService) RemoveMember(ctx context.Context, org, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/members/%v", org, user) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// PublicizeMembership publicizes a user's membership in an organization. (A +// user cannot publicize the membership for another user.) +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#publicize-a-users-membership +func (s *OrganizationsService) PublicizeMembership(ctx context.Context, org, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ConcealMembership conceals a user's membership in an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#conceal-a-users-membership +func (s *OrganizationsService) ConcealMembership(ctx context.Context, org, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ListOrgMembershipsOptions specifies optional parameters to the +// OrganizationsService.ListOrgMemberships method. +type ListOrgMembershipsOptions struct { + // Filter memberships to include only those with the specified state. + // Possible values are: "active", "pending". + State string `url:"state,omitempty"` + + ListOptions +} + +// ListOrgMemberships lists the organization memberships for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-your-organization-memberships +func (s *OrganizationsService) ListOrgMemberships(ctx context.Context, opt *ListOrgMembershipsOptions) ([]*Membership, *Response, error) { + u := "user/memberships/orgs" + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var memberships []*Membership + resp, err := s.client.Do(ctx, req, &memberships) + if err != nil { + return nil, resp, err + } + + return memberships, resp, nil +} + +// GetOrgMembership gets the membership for a user in a specified organization. +// Passing an empty string for user will get the membership for the +// authenticated user. +// +// GitHub API docs: +// https://developer.github.com/v3/orgs/members/#get-organization-membership +// https://developer.github.com/v3/orgs/members/#get-your-organization-membership +func (s *OrganizationsService) GetOrgMembership(ctx context.Context, user, org string) (*Membership, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) + } else { + u = fmt.Sprintf("user/memberships/orgs/%v", org) + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + membership := new(Membership) + resp, err := s.client.Do(ctx, req, membership) + if err != nil { + return nil, resp, err + } + + return membership, resp, nil +} + +// EditOrgMembership edits the membership for user in specified organization. +// Passing an empty string for user will edit the membership for the +// authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership +// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership +func (s *OrganizationsService) EditOrgMembership(ctx context.Context, user, org string, membership *Membership) (*Membership, *Response, error) { + var u, method string + if user != "" { + u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) + method = "PUT" + } else { + u = fmt.Sprintf("user/memberships/orgs/%v", org) + method = "PATCH" + } + + req, err := s.client.NewRequest(method, u, membership) + if err != nil { + return nil, nil, err + } + + m := new(Membership) + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// RemoveOrgMembership removes user from the specified organization. If the +// user has been invited to the organization, this will cancel their invitation. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-organization-membership +func (s *OrganizationsService) RemoveOrgMembership(ctx context.Context, user, org string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/memberships/%v", org, user) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ListPendingOrgInvitations returns a list of pending invitations. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-pending-organization-invitations +func (s *OrganizationsService) ListPendingOrgInvitations(ctx context.Context, org string, opt *ListOptions) ([]*Invitation, *Response, error) { + u := fmt.Sprintf("orgs/%v/invitations", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pendingInvitations []*Invitation + resp, err := s.client.Do(ctx, req, &pendingInvitations) + if err != nil { + return nil, resp, err + } + return pendingInvitations, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/orgs_members_test.go b/vendor/github.com/google/go-github/github/orgs_members_test.go new file mode 100644 index 00000000..7b5cba85 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_members_test.go @@ -0,0 +1,446 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestOrganizationsService_ListMembers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/members", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "filter": "2fa_disabled", + "role": "admin", + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListMembersOptions{ + PublicOnly: false, + Filter: "2fa_disabled", + Role: "admin", + ListOptions: ListOptions{Page: 2}, + } + members, _, err := client.Organizations.ListMembers(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListMembers returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(members, want) { + t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) + } +} + +func TestOrganizationsService_ListMembers_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.ListMembers(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_ListMembers_public(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/public_members", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListMembersOptions{PublicOnly: true} + members, _, err := client.Organizations.ListMembers(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListMembers returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(members, want) { + t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) + } +} + +func TestOrganizationsService_IsMember(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + member, _, err := client.Organizations.IsMember(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.IsMember returned error: %v", err) + } + if want := true; member != want { + t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) + } +} + +// ensure that a 404 response is interpreted as "false" and not an error +func TestOrganizationsService_IsMember_notMember(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + member, _, err := client.Organizations.IsMember(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.IsMember returned error: %+v", err) + } + if want := false; member != want { + t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) + } +} + +// ensure that a 400 response is interpreted as an actual error, and not simply +// as "false" like the above case of a 404 +func TestOrganizationsService_IsMember_error(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Error(w, "BadRequest", http.StatusBadRequest) + }) + + member, _, err := client.Organizations.IsMember(context.Background(), "o", "u") + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if want := false; member != want { + t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) + } +} + +func TestOrganizationsService_IsMember_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.IsMember(context.Background(), "%", "u") + testURLParseError(t, err) +} + +func TestOrganizationsService_IsPublicMember(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + member, _, err := client.Organizations.IsPublicMember(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.IsPublicMember returned error: %v", err) + } + if want := true; member != want { + t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) + } +} + +// ensure that a 404 response is interpreted as "false" and not an error +func TestOrganizationsService_IsPublicMember_notMember(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + member, _, err := client.Organizations.IsPublicMember(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.IsPublicMember returned error: %v", err) + } + if want := false; member != want { + t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) + } +} + +// ensure that a 400 response is interpreted as an actual error, and not simply +// as "false" like the above case of a 404 +func TestOrganizationsService_IsPublicMember_error(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Error(w, "BadRequest", http.StatusBadRequest) + }) + + member, _, err := client.Organizations.IsPublicMember(context.Background(), "o", "u") + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if want := false; member != want { + t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) + } +} + +func TestOrganizationsService_IsPublicMember_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.IsPublicMember(context.Background(), "%", "u") + testURLParseError(t, err) +} + +func TestOrganizationsService_RemoveMember(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Organizations.RemoveMember(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.RemoveMember returned error: %v", err) + } +} + +func TestOrganizationsService_RemoveMember_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Organizations.RemoveMember(context.Background(), "%", "u") + testURLParseError(t, err) +} + +func TestOrganizationsService_ListOrgMemberships(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/memberships/orgs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "active", + "page": "2", + }) + fmt.Fprint(w, `[{"url":"u"}]`) + }) + + opt := &ListOrgMembershipsOptions{ + State: "active", + ListOptions: ListOptions{Page: 2}, + } + memberships, _, err := client.Organizations.ListOrgMemberships(context.Background(), opt) + if err != nil { + t.Errorf("Organizations.ListOrgMemberships returned error: %v", err) + } + + want := []*Membership{{URL: String("u")}} + if !reflect.DeepEqual(memberships, want) { + t.Errorf("Organizations.ListOrgMemberships returned %+v, want %+v", memberships, want) + } +} + +func TestOrganizationsService_GetOrgMembership_AuthenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"url":"u"}`) + }) + + membership, _, err := client.Organizations.GetOrgMembership(context.Background(), "", "o") + if err != nil { + t.Errorf("Organizations.GetOrgMembership returned error: %v", err) + } + + want := &Membership{URL: String("u")} + if !reflect.DeepEqual(membership, want) { + t.Errorf("Organizations.GetOrgMembership returned %+v, want %+v", membership, want) + } +} + +func TestOrganizationsService_GetOrgMembership_SpecifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"url":"u"}`) + }) + + membership, _, err := client.Organizations.GetOrgMembership(context.Background(), "u", "o") + if err != nil { + t.Errorf("Organizations.GetOrgMembership returned error: %v", err) + } + + want := &Membership{URL: String("u")} + if !reflect.DeepEqual(membership, want) { + t.Errorf("Organizations.GetOrgMembership returned %+v, want %+v", membership, want) + } +} + +func TestOrganizationsService_EditOrgMembership_AuthenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Membership{State: String("active")} + + mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { + v := new(Membership) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"url":"u"}`) + }) + + membership, _, err := client.Organizations.EditOrgMembership(context.Background(), "", "o", input) + if err != nil { + t.Errorf("Organizations.EditOrgMembership returned error: %v", err) + } + + want := &Membership{URL: String("u")} + if !reflect.DeepEqual(membership, want) { + t.Errorf("Organizations.EditOrgMembership returned %+v, want %+v", membership, want) + } +} + +func TestOrganizationsService_EditOrgMembership_SpecifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Membership{State: String("active")} + + mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { + v := new(Membership) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"url":"u"}`) + }) + + membership, _, err := client.Organizations.EditOrgMembership(context.Background(), "u", "o", input) + if err != nil { + t.Errorf("Organizations.EditOrgMembership returned error: %v", err) + } + + want := &Membership{URL: String("u")} + if !reflect.DeepEqual(membership, want) { + t.Errorf("Organizations.EditOrgMembership returned %+v, want %+v", membership, want) + } +} + +func TestOrganizationsService_RemoveOrgMembership(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.RemoveOrgMembership(context.Background(), "u", "o") + if err != nil { + t.Errorf("Organizations.RemoveOrgMembership returned error: %v", err) + } +} + +func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1"}) + fmt.Fprint(w, `[ + { + "id": 1, + "login": "monalisa", + "email": "octocat@github.com", + "role": "direct_member", + "created_at": "2017-01-21T00:00:00Z", + "inviter": { + "login": "other_user", + "id": 1, + "avatar_url": "https://github.com/images/error/other_user_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/other_user", + "html_url": "https://github.com/other_user", + "followers_url": "https://api.github.com/users/other_user/followers", + "following_url": "https://api.github.com/users/other_user/following/other_user", + "gists_url": "https://api.github.com/users/other_user/gists/gist_id", + "starred_url": "https://api.github.com/users/other_user/starred/owner/repo", + "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", + "organizations_url": "https://api.github.com/users/other_user/orgs", + "repos_url": "https://api.github.com/users/other_user/repos", + "events_url": "https://api.github.com/users/other_user/events/privacy", + "received_events_url": "https://api.github.com/users/other_user/received_events/privacy", + "type": "User", + "site_admin": false + } + } + ]`) + }) + + opt := &ListOptions{Page: 1} + invitations, _, err := client.Organizations.ListPendingOrgInvitations(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListPendingOrgInvitations returned error: %v", err) + } + + createdAt := time.Date(2017, 01, 21, 0, 0, 0, 0, time.UTC) + want := []*Invitation{ + { + ID: Int64(1), + Login: String("monalisa"), + Email: String("octocat@github.com"), + Role: String("direct_member"), + CreatedAt: &createdAt, + Inviter: &User{ + Login: String("other_user"), + ID: Int64(1), + AvatarURL: String("https://github.com/images/error/other_user_happy.gif"), + GravatarID: String(""), + URL: String("https://api.github.com/users/other_user"), + HTMLURL: String("https://github.com/other_user"), + FollowersURL: String("https://api.github.com/users/other_user/followers"), + FollowingURL: String("https://api.github.com/users/other_user/following/other_user"), + GistsURL: String("https://api.github.com/users/other_user/gists/gist_id"), + StarredURL: String("https://api.github.com/users/other_user/starred/owner/repo"), + SubscriptionsURL: String("https://api.github.com/users/other_user/subscriptions"), + OrganizationsURL: String("https://api.github.com/users/other_user/orgs"), + ReposURL: String("https://api.github.com/users/other_user/repos"), + EventsURL: String("https://api.github.com/users/other_user/events/privacy"), + ReceivedEventsURL: String("https://api.github.com/users/other_user/received_events/privacy"), + Type: String("User"), + SiteAdmin: Bool(false), + }, + }} + + if !reflect.DeepEqual(invitations, want) { + t.Errorf("Organizations.ListPendingOrgInvitations returned %+v, want %+v", invitations, want) + } +} diff --git a/vendor/github.com/google/go-github/github/orgs_outside_collaborators.go b/vendor/github.com/google/go-github/github/orgs_outside_collaborators.go new file mode 100644 index 00000000..85ffd05f --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_outside_collaborators.go @@ -0,0 +1,81 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListOutsideCollaboratorsOptions specifies optional parameters to the +// OrganizationsService.ListOutsideCollaborators method. +type ListOutsideCollaboratorsOptions struct { + // Filter outside collaborators returned in the list. Possible values are: + // 2fa_disabled, all. Default is "all". + Filter string `url:"filter,omitempty"` + + ListOptions +} + +// ListOutsideCollaborators lists outside collaborators of organization's repositories. +// This will only work if the authenticated +// user is an owner of the organization. +// +// Warning: The API may change without advance notice during the preview period. +// Preview features are not supported for production use. +// +// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#list-outside-collaborators +func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org string, opt *ListOutsideCollaboratorsOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("orgs/%v/outside_collaborators", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var members []*User + resp, err := s.client.Do(ctx, req, &members) + if err != nil { + return nil, resp, err + } + + return members, resp, nil +} + +// RemoveOutsideCollaborator removes a user from the list of outside collaborators; +// consequently, removing them from all the organization's repositories. +// +// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#remove-outside-collaborator +func (s *OrganizationsService) RemoveOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ConvertMemberToOutsideCollaborator reduces the permission level of a member of the +// organization to that of an outside collaborator. Therefore, they will only +// have access to the repositories that their current team membership allows. +// Responses for converting a non-member or the last owner to an outside collaborator +// are listed in GitHub API docs. +// +// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#convert-member-to-outside-collaborator +func (s *OrganizationsService) ConvertMemberToOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/orgs_outside_collaborators_test.go b/vendor/github.com/google/go-github/github/orgs_outside_collaborators_test.go new file mode 100644 index 00000000..8b72feb2 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_outside_collaborators_test.go @@ -0,0 +1,134 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestOrganizationsService_ListOutsideCollaborators(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/outside_collaborators", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "filter": "2fa_disabled", + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOutsideCollaboratorsOptions{ + Filter: "2fa_disabled", + ListOptions: ListOptions{Page: 2}, + } + members, _, err := client.Organizations.ListOutsideCollaborators(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListOutsideCollaborators returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(members, want) { + t.Errorf("Organizations.ListOutsideCollaborators returned %+v, want %+v", members, want) + } +} + +func TestOrganizationsService_ListOutsideCollaborators_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.ListOutsideCollaborators(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_RemoveOutsideCollaborator(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + } + mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) + + _, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.RemoveOutsideCollaborator returned error: %v", err) + } +} + +func TestOrganizationsService_RemoveOutsideCollaborator_NonMember(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + } + mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) + + _, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u") + if err, ok := err.(*ErrorResponse); !ok { + t.Errorf("Organizations.RemoveOutsideCollaborator did not return an error") + } else if err.Response.StatusCode != http.StatusNotFound { + t.Errorf("Organizations.RemoveOutsideCollaborator did not return 404 status code") + } +} + +func TestOrganizationsService_RemoveOutsideCollaborator_Member(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusUnprocessableEntity) + } + mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) + + _, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u") + if err, ok := err.(*ErrorResponse); !ok { + t.Errorf("Organizations.RemoveOutsideCollaborator did not return an error") + } else if err.Response.StatusCode != http.StatusUnprocessableEntity { + t.Errorf("Organizations.RemoveOutsideCollaborator did not return 422 status code") + } +} + +func TestOrganizationsService_ConvertMemberToOutsideCollaborator(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + } + mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) + + _, err := client.Organizations.ConvertMemberToOutsideCollaborator(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.ConvertMemberToOutsideCollaborator returned error: %v", err) + } +} + +func TestOrganizationsService_ConvertMemberToOutsideCollaborator_NonMemberOrLastOwner(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusForbidden) + } + mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) + + _, err := client.Organizations.ConvertMemberToOutsideCollaborator(context.Background(), "o", "u") + if err, ok := err.(*ErrorResponse); !ok { + t.Errorf("Organizations.ConvertMemberToOutsideCollaborator did not return an error") + } else if err.Response.StatusCode != http.StatusForbidden { + t.Errorf("Organizations.ConvertMemberToOutsideCollaborator did not return 403 status code") + } +} diff --git a/vendor/github.com/google/go-github/github/orgs_projects.go b/vendor/github.com/google/go-github/github/orgs_projects.go new file mode 100644 index 00000000..e57cba97 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_projects.go @@ -0,0 +1,60 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListProjects lists the projects for an organization. +// +// GitHub API docs: https://developer.github.com/v3/projects/#list-organization-projects +func (s *OrganizationsService) ListProjects(ctx context.Context, org string, opt *ProjectListOptions) ([]*Project, *Response, error) { + u := fmt.Sprintf("orgs/%v/projects", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + var projects []*Project + resp, err := s.client.Do(ctx, req, &projects) + if err != nil { + return nil, resp, err + } + + return projects, resp, nil +} + +// CreateProject creates a GitHub Project for the specified organization. +// +// GitHub API docs: https://developer.github.com/v3/projects/#create-an-organization-project +func (s *OrganizationsService) CreateProject(ctx context.Context, org string, opt *ProjectOptions) (*Project, *Response, error) { + u := fmt.Sprintf("orgs/%v/projects", org) + req, err := s.client.NewRequest("POST", u, opt) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + project := &Project{} + resp, err := s.client.Do(ctx, req, project) + if err != nil { + return nil, resp, err + } + + return project, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/orgs_projects_test.go b/vendor/github.com/google/go-github/github/orgs_projects_test.go new file mode 100644 index 00000000..97a7002f --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_projects_test.go @@ -0,0 +1,68 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestOrganizationsService_ListProjects(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/projects", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testFormValues(t, r, values{"state": "open", "page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ProjectListOptions{State: "open", ListOptions: ListOptions{Page: 2}} + projects, _, err := client.Organizations.ListProjects(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListProjects returned error: %v", err) + } + + want := []*Project{{ID: Int64(1)}} + if !reflect.DeepEqual(projects, want) { + t.Errorf("Organizations.ListProjects returned %+v, want %+v", projects, want) + } +} + +func TestOrganizationsService_CreateProject(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectOptions{Name: "Project Name", Body: "Project body."} + + mux.HandleFunc("/orgs/o/projects", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + project, _, err := client.Organizations.CreateProject(context.Background(), "o", input) + if err != nil { + t.Errorf("Organizations.CreateProject returned error: %v", err) + } + + want := &Project{ID: Int64(1)} + if !reflect.DeepEqual(project, want) { + t.Errorf("Organizations.CreateProject returned %+v, want %+v", project, want) + } +} diff --git a/vendor/github.com/google/go-github/github/orgs_teams.go b/vendor/github.com/google/go-github/github/orgs_teams.go new file mode 100644 index 00000000..c1457108 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_teams.go @@ -0,0 +1,512 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "strings" + "time" +) + +// Team represents a team within a GitHub organization. Teams are used to +// manage access to an organization's repositories. +type Team struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + URL *string `json:"url,omitempty"` + Slug *string `json:"slug,omitempty"` + + // Permission specifies the default permission for repositories owned by the team. + Permission *string `json:"permission,omitempty"` + + // Privacy identifies the level of privacy this team should have. + // Possible values are: + // secret - only visible to organization owners and members of this team + // closed - visible to all members of this organization + // Default is "secret". + Privacy *string `json:"privacy,omitempty"` + + MembersCount *int `json:"members_count,omitempty"` + ReposCount *int `json:"repos_count,omitempty"` + Organization *Organization `json:"organization,omitempty"` + MembersURL *string `json:"members_url,omitempty"` + RepositoriesURL *string `json:"repositories_url,omitempty"` + Parent *Team `json:"parent,omitempty"` + + // LDAPDN is only available in GitHub Enterprise and when the team + // membership is synchronized with LDAP. + LDAPDN *string `json:"ldap_dn,omitempty"` +} + +func (t Team) String() string { + return Stringify(t) +} + +// Invitation represents a team member's invitation status. +type Invitation struct { + ID *int64 `json:"id,omitempty"` + Login *string `json:"login,omitempty"` + Email *string `json:"email,omitempty"` + // Role can be one of the values - 'direct_member', 'admin', 'billing_manager', 'hiring_manager', or 'reinstate'. + Role *string `json:"role,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + Inviter *User `json:"inviter,omitempty"` +} + +func (i Invitation) String() string { + return Stringify(i) +} + +// ListTeams lists all of the teams for an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-teams +func (s *OrganizationsService) ListTeams(ctx context.Context, org string, opt *ListOptions) ([]*Team, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + var teams []*Team + resp, err := s.client.Do(ctx, req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// GetTeam fetches a team by ID. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team +func (s *OrganizationsService) GetTeam(ctx context.Context, team int64) (*Team, *Response, error) { + u := fmt.Sprintf("teams/%v", team) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + t := new(Team) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// NewTeam represents a team to be created or modified. +type NewTeam struct { + Name string `json:"name"` // Name of the team. (Required.) + Description *string `json:"description,omitempty"` + Maintainers []string `json:"maintainers,omitempty"` + RepoNames []string `json:"repo_names,omitempty"` + ParentTeamID *int64 `json:"parent_team_id,omitempty"` + + // Deprecated: Permission is deprecated when creating or editing a team in an org + // using the new GitHub permission model. It no longer identifies the + // permission a team has on its repos, but only specifies the default + // permission a repo is initially added with. Avoid confusion by + // specifying a permission value when calling AddTeamRepo. + Permission *string `json:"permission,omitempty"` + + // Privacy identifies the level of privacy this team should have. + // Possible values are: + // secret - only visible to organization owners and members of this team + // closed - visible to all members of this organization + // Default is "secret". + Privacy *string `json:"privacy,omitempty"` + + // LDAPDN may be used in GitHub Enterprise when the team membership + // is synchronized with LDAP. + LDAPDN *string `json:"ldap_dn,omitempty"` +} + +func (s NewTeam) String() string { + return Stringify(s) +} + +// CreateTeam creates a new team within an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#create-team +func (s *OrganizationsService) CreateTeam(ctx context.Context, org string, team *NewTeam) (*Team, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams", org) + req, err := s.client.NewRequest("POST", u, team) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + t := new(Team) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// EditTeam edits a team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#edit-team +func (s *OrganizationsService) EditTeam(ctx context.Context, id int64, team *NewTeam) (*Team, *Response, error) { + u := fmt.Sprintf("teams/%v", id) + req, err := s.client.NewRequest("PATCH", u, team) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + t := new(Team) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// DeleteTeam deletes a team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#delete-team +func (s *OrganizationsService) DeleteTeam(ctx context.Context, team int64) (*Response, error) { + u := fmt.Sprintf("teams/%v", team) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + return s.client.Do(ctx, req, nil) +} + +// OrganizationListTeamMembersOptions specifies the optional parameters to the +// OrganizationsService.ListTeamMembers method. +type OrganizationListTeamMembersOptions struct { + // Role filters members returned by their role in the team. Possible + // values are "all", "member", "maintainer". Default is "all". + Role string `url:"role,omitempty"` + + ListOptions +} + +// ListChildTeams lists child teams for a team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-child-teams +func (s *OrganizationsService) ListChildTeams(ctx context.Context, teamID int64, opt *ListOptions) ([]*Team, *Response, error) { + u := fmt.Sprintf("teams/%v/teams", teamID) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + var teams []*Team + resp, err := s.client.Do(ctx, req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// ListTeamMembers lists all of the users who are members of the specified +// team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-members +func (s *OrganizationsService) ListTeamMembers(ctx context.Context, team int64, opt *OrganizationListTeamMembersOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("teams/%v/members", team) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + var members []*User + resp, err := s.client.Do(ctx, req, &members) + if err != nil { + return nil, resp, err + } + + return members, resp, nil +} + +// IsTeamMember checks if a user is a member of the specified team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-member +// +// Deprecated: This API has been marked as deprecated in the Github API docs, +// OrganizationsService.GetTeamMembership method should be used instead. +func (s *OrganizationsService) IsTeamMember(ctx context.Context, team int64, user string) (bool, *Response, error) { + u := fmt.Sprintf("teams/%v/members/%v", team, user) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + member, err := parseBoolResponse(err) + return member, resp, err +} + +// ListTeamRepos lists the repositories that the specified team has access to. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-repos +func (s *OrganizationsService) ListTeamRepos(ctx context.Context, team int64, opt *ListOptions) ([]*Repository, *Response, error) { + u := fmt.Sprintf("teams/%v/repos", team) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when topics API fully launches. + headers := []string{mediaTypeTopicsPreview, mediaTypeNestedTeamsPreview} + req.Header.Set("Accept", strings.Join(headers, ", ")) + + var repos []*Repository + resp, err := s.client.Do(ctx, req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// IsTeamRepo checks if a team manages the specified repository. If the +// repository is managed by team, a Repository is returned which includes the +// permissions team has for that repo. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#check-if-a-team-manages-a-repository +func (s *OrganizationsService) IsTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Repository, *Response, error) { + u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + headers := []string{mediaTypeOrgPermissionRepo, mediaTypeNestedTeamsPreview} + req.Header.Set("Accept", strings.Join(headers, ", ")) + + repository := new(Repository) + resp, err := s.client.Do(ctx, req, repository) + if err != nil { + return nil, resp, err + } + + return repository, resp, nil +} + +// OrganizationAddTeamRepoOptions specifies the optional parameters to the +// OrganizationsService.AddTeamRepo method. +type OrganizationAddTeamRepoOptions struct { + // Permission specifies the permission to grant the team on this repository. + // Possible values are: + // pull - team members can pull, but not push to or administer this repository + // push - team members can pull and push, but not administer this repository + // admin - team members can pull, push and administer this repository + // + // If not specified, the team's permission attribute will be used. + Permission string `json:"permission,omitempty"` +} + +// AddTeamRepo adds a repository to be managed by the specified team. The +// specified repository must be owned by the organization to which the team +// belongs, or a direct fork of a repository owned by the organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-repo +func (s *OrganizationsService) AddTeamRepo(ctx context.Context, team int64, owner string, repo string, opt *OrganizationAddTeamRepoOptions) (*Response, error) { + u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) + req, err := s.client.NewRequest("PUT", u, opt) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// RemoveTeamRepo removes a repository from being managed by the specified +// team. Note that this does not delete the repository, it just removes it +// from the team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-repo +func (s *OrganizationsService) RemoveTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Response, error) { + u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ListUserTeams lists a user's teams +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-user-teams +func (s *OrganizationsService) ListUserTeams(ctx context.Context, opt *ListOptions) ([]*Team, *Response, error) { + u := "user/teams" + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + var teams []*Team + resp, err := s.client.Do(ctx, req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// GetTeamMembership returns the membership status for a user in a team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership +func (s *OrganizationsService) GetTeamMembership(ctx context.Context, team int64, user string) (*Membership, *Response, error) { + u := fmt.Sprintf("teams/%v/memberships/%v", team, user) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + t := new(Membership) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// OrganizationAddTeamMembershipOptions does stuff specifies the optional +// parameters to the OrganizationsService.AddTeamMembership method. +type OrganizationAddTeamMembershipOptions struct { + // Role specifies the role the user should have in the team. Possible + // values are: + // member - a normal member of the team + // maintainer - a team maintainer. Able to add/remove other team + // members, promote other team members to team + // maintainer, and edit the team’s name and description + // + // Default value is "member". + Role string `json:"role,omitempty"` +} + +// AddTeamMembership adds or invites a user to a team. +// +// In order to add a membership between a user and a team, the authenticated +// user must have 'admin' permissions to the team or be an owner of the +// organization that the team is associated with. +// +// If the user is already a part of the team's organization (meaning they're on +// at least one other team in the organization), this endpoint will add the +// user to the team. +// +// If the user is completely unaffiliated with the team's organization (meaning +// they're on none of the organization's teams), this endpoint will send an +// invitation to the user via email. This newly-created membership will be in +// the "pending" state until the user accepts the invitation, at which point +// the membership will transition to the "active" state and the user will be +// added as a member of the team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-membership +func (s *OrganizationsService) AddTeamMembership(ctx context.Context, team int64, user string, opt *OrganizationAddTeamMembershipOptions) (*Membership, *Response, error) { + u := fmt.Sprintf("teams/%v/memberships/%v", team, user) + req, err := s.client.NewRequest("PUT", u, opt) + if err != nil { + return nil, nil, err + } + + t := new(Membership) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// RemoveTeamMembership removes a user from a team. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-membership +func (s *OrganizationsService) RemoveTeamMembership(ctx context.Context, team int64, user string) (*Response, error) { + u := fmt.Sprintf("teams/%v/memberships/%v", team, user) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ListPendingTeamInvitations get pending invitaion list in team. +// Warning: The API may change without advance notice during the preview period. +// Preview features are not supported for production use. +// +// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-pending-team-invitations +func (s *OrganizationsService) ListPendingTeamInvitations(ctx context.Context, team int64, opt *ListOptions) ([]*Invitation, *Response, error) { + u := fmt.Sprintf("teams/%v/invitations", team) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pendingInvitations []*Invitation + resp, err := s.client.Do(ctx, req, &pendingInvitations) + if err != nil { + return nil, resp, err + } + + return pendingInvitations, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/orgs_teams_test.go b/vendor/github.com/google/go-github/github/orgs_teams_test.go new file mode 100644 index 00000000..ead8024e --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_teams_test.go @@ -0,0 +1,663 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" + "testing" + "time" +) + +func TestOrganizationsService_ListTeams(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + teams, _, err := client.Organizations.ListTeams(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListTeams returned error: %v", err) + } + + want := []*Team{{ID: Int64(1)}} + if !reflect.DeepEqual(teams, want) { + t.Errorf("Organizations.ListTeams returned %+v, want %+v", teams, want) + } +} + +func TestOrganizationsService_ListTeams_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.ListTeams(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_GetTeam(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", "ldap_dn":"cn=n,ou=groups,dc=example,dc=com", "parent":null}`) + }) + + team, _, err := client.Organizations.GetTeam(context.Background(), 1) + if err != nil { + t.Errorf("Organizations.GetTeam returned error: %v", err) + } + + want := &Team{ID: Int64(1), Name: String("n"), Description: String("d"), URL: String("u"), Slug: String("s"), Permission: String("p"), LDAPDN: String("cn=n,ou=groups,dc=example,dc=com")} + if !reflect.DeepEqual(team, want) { + t.Errorf("Organizations.GetTeam returned %+v, want %+v", team, want) + } +} + +func TestOrganizationService_GetTeam_nestedTeams(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", + "parent": {"id":2, "name":"n", "description": "d", "parent": null}}`) + }) + + team, _, err := client.Organizations.GetTeam(context.Background(), 1) + if err != nil { + t.Errorf("Organizations.GetTeam returned error: %v", err) + } + + want := &Team{ID: Int64(1), Name: String("n"), Description: String("d"), URL: String("u"), Slug: String("s"), Permission: String("p"), + Parent: &Team{ID: Int64(2), Name: String("n"), Description: String("d")}, + } + if !reflect.DeepEqual(team, want) { + t.Errorf("Organizations.GetTeam returned %+v, want %+v", team, want) + } +} + +func TestOrganizationsService_CreateTeam(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &NewTeam{Name: "n", Privacy: String("closed"), RepoNames: []string{"r"}} + + mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) { + v := new(NewTeam) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + team, _, err := client.Organizations.CreateTeam(context.Background(), "o", input) + if err != nil { + t.Errorf("Organizations.CreateTeam returned error: %v", err) + } + + want := &Team{ID: Int64(1)} + if !reflect.DeepEqual(team, want) { + t.Errorf("Organizations.CreateTeam returned %+v, want %+v", team, want) + } +} + +func TestOrganizationsService_CreateTeam_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.CreateTeam(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_EditTeam(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &NewTeam{Name: "n", Privacy: String("closed")} + + mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { + v := new(NewTeam) + json.NewDecoder(r.Body).Decode(v) + + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + team, _, err := client.Organizations.EditTeam(context.Background(), 1, input) + if err != nil { + t.Errorf("Organizations.EditTeam returned error: %v", err) + } + + want := &Team{ID: Int64(1)} + if !reflect.DeepEqual(team, want) { + t.Errorf("Organizations.EditTeam returned %+v, want %+v", team, want) + } +} + +func TestOrganizationsService_DeleteTeam(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + }) + + _, err := client.Organizations.DeleteTeam(context.Background(), 1) + if err != nil { + t.Errorf("Organizations.DeleteTeam returned error: %v", err) + } +} + +func TestOrganizationsService_ListChildTeams(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + teams, _, err := client.Organizations.ListChildTeams(context.Background(), 1, opt) + if err != nil { + t.Errorf("Organizations.ListTeams returned error: %v", err) + } + + want := []*Team{{ID: Int64(2)}} + if !reflect.DeepEqual(teams, want) { + t.Errorf("Organizations.ListTeams returned %+v, want %+v", teams, want) + } +} + +func TestOrganizationsService_ListTeamMembers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/members", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + testFormValues(t, r, values{"role": "member", "page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &OrganizationListTeamMembersOptions{Role: "member", ListOptions: ListOptions{Page: 2}} + members, _, err := client.Organizations.ListTeamMembers(context.Background(), 1, opt) + if err != nil { + t.Errorf("Organizations.ListTeamMembers returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(members, want) { + t.Errorf("Organizations.ListTeamMembers returned %+v, want %+v", members, want) + } +} + +func TestOrganizationsService_IsTeamMember_true(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + + member, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "u") + if err != nil { + t.Errorf("Organizations.IsTeamMember returned error: %v", err) + } + if want := true; member != want { + t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want) + } +} + +// ensure that a 404 response is interpreted as "false" and not an error +func TestOrganizationsService_IsTeamMember_false(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + member, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "u") + if err != nil { + t.Errorf("Organizations.IsTeamMember returned error: %+v", err) + } + if want := false; member != want { + t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want) + } +} + +// ensure that a 400 response is interpreted as an actual error, and not simply +// as "false" like the above case of a 404 +func TestOrganizationsService_IsTeamMember_error(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Error(w, "BadRequest", http.StatusBadRequest) + }) + + member, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "u") + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if want := false; member != want { + t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want) + } +} + +func TestOrganizationsService_IsTeamMember_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "%") + testURLParseError(t, err) +} + +func TestOrganizationsService_PublicizeMembership(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.PublicizeMembership(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.PublicizeMembership returned error: %v", err) + } +} + +func TestOrganizationsService_PublicizeMembership_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Organizations.PublicizeMembership(context.Background(), "%", "u") + testURLParseError(t, err) +} + +func TestOrganizationsService_ConcealMembership(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.ConcealMembership(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.ConcealMembership returned error: %v", err) + } +} + +func TestOrganizationsService_ConcealMembership_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Organizations.ConcealMembership(context.Background(), "%", "u") + testURLParseError(t, err) +} + +func TestOrganizationsService_ListTeamRepos(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + acceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeNestedTeamsPreview} + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + members, _, err := client.Organizations.ListTeamRepos(context.Background(), 1, opt) + if err != nil { + t.Errorf("Organizations.ListTeamRepos returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(members, want) { + t.Errorf("Organizations.ListTeamRepos returned %+v, want %+v", members, want) + } +} + +func TestOrganizationsService_IsTeamRepo_true(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + acceptHeaders := []string{mediaTypeOrgPermissionRepo, mediaTypeNestedTeamsPreview} + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `{"id":1}`) + }) + + repo, _, err := client.Organizations.IsTeamRepo(context.Background(), 1, "o", "r") + if err != nil { + t.Errorf("Organizations.IsTeamRepo returned error: %v", err) + } + + want := &Repository{ID: Int64(1)} + if !reflect.DeepEqual(repo, want) { + t.Errorf("Organizations.IsTeamRepo returned %+v, want %+v", repo, want) + } +} + +func TestOrganizationsService_IsTeamRepo_false(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + repo, resp, err := client.Organizations.IsTeamRepo(context.Background(), 1, "o", "r") + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Organizations.IsTeamRepo returned status %d, want %d", got, want) + } + if repo != nil { + t.Errorf("Organizations.IsTeamRepo returned %+v, want nil", repo) + } +} + +func TestOrganizationsService_IsTeamRepo_error(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Error(w, "BadRequest", http.StatusBadRequest) + }) + + repo, resp, err := client.Organizations.IsTeamRepo(context.Background(), 1, "o", "r") + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { + t.Errorf("Organizations.IsTeamRepo returned status %d, want %d", got, want) + } + if repo != nil { + t.Errorf("Organizations.IsTeamRepo returned %+v, want nil", repo) + } +} + +func TestOrganizationsService_IsTeamRepo_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.IsTeamRepo(context.Background(), 1, "%", "r") + testURLParseError(t, err) +} + +func TestOrganizationsService_AddTeamRepo(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + opt := &OrganizationAddTeamRepoOptions{Permission: "admin"} + + mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + v := new(OrganizationAddTeamRepoOptions) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, opt) { + t.Errorf("Request body = %+v, want %+v", v, opt) + } + + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.AddTeamRepo(context.Background(), 1, "o", "r", opt) + if err != nil { + t.Errorf("Organizations.AddTeamRepo returned error: %v", err) + } +} + +func TestOrganizationsService_AddTeamRepo_noAccess(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusUnprocessableEntity) + }) + + _, err := client.Organizations.AddTeamRepo(context.Background(), 1, "o", "r", nil) + if err == nil { + t.Errorf("Expcted error to be returned") + } +} + +func TestOrganizationsService_AddTeamRepo_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Organizations.AddTeamRepo(context.Background(), 1, "%", "r", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_RemoveTeamRepo(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.RemoveTeamRepo(context.Background(), 1, "o", "r") + if err != nil { + t.Errorf("Organizations.RemoveTeamRepo returned error: %v", err) + } +} + +func TestOrganizationsService_RemoveTeamRepo_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Organizations.RemoveTeamRepo(context.Background(), 1, "%", "r") + testURLParseError(t, err) +} + +func TestOrganizationsService_GetTeamMembership(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + fmt.Fprint(w, `{"url":"u", "state":"active"}`) + }) + + membership, _, err := client.Organizations.GetTeamMembership(context.Background(), 1, "u") + if err != nil { + t.Errorf("Organizations.GetTeamMembership returned error: %v", err) + } + + want := &Membership{URL: String("u"), State: String("active")} + if !reflect.DeepEqual(membership, want) { + t.Errorf("Organizations.GetTeamMembership returned %+v, want %+v", membership, want) + } +} + +func TestOrganizationsService_AddTeamMembership(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + opt := &OrganizationAddTeamMembershipOptions{Role: "maintainer"} + + mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { + v := new(OrganizationAddTeamMembershipOptions) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, opt) { + t.Errorf("Request body = %+v, want %+v", v, opt) + } + + fmt.Fprint(w, `{"url":"u", "state":"pending"}`) + }) + + membership, _, err := client.Organizations.AddTeamMembership(context.Background(), 1, "u", opt) + if err != nil { + t.Errorf("Organizations.AddTeamMembership returned error: %v", err) + } + + want := &Membership{URL: String("u"), State: String("pending")} + if !reflect.DeepEqual(membership, want) { + t.Errorf("Organizations.AddTeamMembership returned %+v, want %+v", membership, want) + } +} + +func TestOrganizationsService_RemoveTeamMembership(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.RemoveTeamMembership(context.Background(), 1, "u") + if err != nil { + t.Errorf("Organizations.RemoveTeamMembership returned error: %v", err) + } +} + +func TestOrganizationsService_ListUserTeams(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + testFormValues(t, r, values{"page": "1"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1} + teams, _, err := client.Organizations.ListUserTeams(context.Background(), opt) + if err != nil { + t.Errorf("Organizations.ListUserTeams returned error: %v", err) + } + + want := []*Team{{ID: Int64(1)}} + if !reflect.DeepEqual(teams, want) { + t.Errorf("Organizations.ListUserTeams returned %+v, want %+v", teams, want) + } +} + +func TestOrganizationsService_ListPendingTeamInvitations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/teams/1/invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1"}) + fmt.Fprint(w, `[ + { + "id": 1, + "login": "monalisa", + "email": "octocat@github.com", + "role": "direct_member", + "created_at": "2017-01-21T00:00:00Z", + "inviter": { + "login": "other_user", + "id": 1, + "avatar_url": "https://github.com/images/error/other_user_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/other_user", + "html_url": "https://github.com/other_user", + "followers_url": "https://api.github.com/users/other_user/followers", + "following_url": "https://api.github.com/users/other_user/following/other_user", + "gists_url": "https://api.github.com/users/other_user/gists/gist_id", + "starred_url": "https://api.github.com/users/other_user/starred/owner/repo", + "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", + "organizations_url": "https://api.github.com/users/other_user/orgs", + "repos_url": "https://api.github.com/users/other_user/repos", + "events_url": "https://api.github.com/users/other_user/events/privacy", + "received_events_url": "https://api.github.com/users/other_user/received_events/privacy", + "type": "User", + "site_admin": false + } + } + ]`) + }) + + opt := &ListOptions{Page: 1} + invitations, _, err := client.Organizations.ListPendingTeamInvitations(context.Background(), 1, opt) + if err != nil { + t.Errorf("Organizations.ListPendingTeamInvitations returned error: %v", err) + } + + createdAt := time.Date(2017, 01, 21, 0, 0, 0, 0, time.UTC) + want := []*Invitation{ + { + ID: Int64(1), + Login: String("monalisa"), + Email: String("octocat@github.com"), + Role: String("direct_member"), + CreatedAt: &createdAt, + Inviter: &User{ + Login: String("other_user"), + ID: Int64(1), + AvatarURL: String("https://github.com/images/error/other_user_happy.gif"), + GravatarID: String(""), + URL: String("https://api.github.com/users/other_user"), + HTMLURL: String("https://github.com/other_user"), + FollowersURL: String("https://api.github.com/users/other_user/followers"), + FollowingURL: String("https://api.github.com/users/other_user/following/other_user"), + GistsURL: String("https://api.github.com/users/other_user/gists/gist_id"), + StarredURL: String("https://api.github.com/users/other_user/starred/owner/repo"), + SubscriptionsURL: String("https://api.github.com/users/other_user/subscriptions"), + OrganizationsURL: String("https://api.github.com/users/other_user/orgs"), + ReposURL: String("https://api.github.com/users/other_user/repos"), + EventsURL: String("https://api.github.com/users/other_user/events/privacy"), + ReceivedEventsURL: String("https://api.github.com/users/other_user/received_events/privacy"), + Type: String("User"), + SiteAdmin: Bool(false), + }, + }} + + if !reflect.DeepEqual(invitations, want) { + t.Errorf("Organizations.ListPendingTeamInvitations returned %+v, want %+v", invitations, want) + } +} diff --git a/vendor/github.com/google/go-github/github/orgs_test.go b/vendor/github.com/google/go-github/github/orgs_test.go new file mode 100644 index 00000000..ca4263e0 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_test.go @@ -0,0 +1,179 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestOrganizationsService_ListAll(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + since := 1342004 + mux.HandleFunc("/organizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"since": "1342004"}) + fmt.Fprint(w, `[{"id":4314092}]`) + }) + + opt := &OrganizationsListOptions{Since: since} + orgs, _, err := client.Organizations.ListAll(context.Background(), opt) + if err != nil { + t.Errorf("Organizations.ListAll returned error: %v", err) + } + + want := []*Organization{{ID: Int64(4314092)}} + if !reflect.DeepEqual(orgs, want) { + t.Errorf("Organizations.ListAll returned %+v, want %+v", orgs, want) + } +} + +func TestOrganizationsService_List_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/orgs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + orgs, _, err := client.Organizations.List(context.Background(), "", nil) + if err != nil { + t.Errorf("Organizations.List returned error: %v", err) + } + + want := []*Organization{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(orgs, want) { + t.Errorf("Organizations.List returned %+v, want %+v", orgs, want) + } +} + +func TestOrganizationsService_List_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/orgs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + orgs, _, err := client.Organizations.List(context.Background(), "u", opt) + if err != nil { + t.Errorf("Organizations.List returned error: %v", err) + } + + want := []*Organization{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(orgs, want) { + t.Errorf("Organizations.List returned %+v, want %+v", orgs, want) + } +} + +func TestOrganizationsService_List_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.List(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"id":1, "login":"l", "url":"u", "avatar_url": "a", "location":"l"}`) + }) + + org, _, err := client.Organizations.Get(context.Background(), "o") + if err != nil { + t.Errorf("Organizations.Get returned error: %v", err) + } + + want := &Organization{ID: Int64(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")} + if !reflect.DeepEqual(org, want) { + t.Errorf("Organizations.Get returned %+v, want %+v", org, want) + } +} + +func TestOrganizationsService_Get_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.Get(context.Background(), "%") + testURLParseError(t, err) +} + +func TestOrganizationsService_GetByID(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/organizations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"id":1, "login":"l", "url":"u", "avatar_url": "a", "location":"l"}`) + }) + + org, _, err := client.Organizations.GetByID(context.Background(), 1) + if err != nil { + t.Fatalf("Organizations.GetByID returned error: %v", err) + } + + want := &Organization{ID: Int64(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")} + if !reflect.DeepEqual(org, want) { + t.Errorf("Organizations.GetByID returned %+v, want %+v", org, want) + } +} + +func TestOrganizationsService_Edit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Organization{Login: String("l")} + + mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) { + v := new(Organization) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + org, _, err := client.Organizations.Edit(context.Background(), "o", input) + if err != nil { + t.Errorf("Organizations.Edit returned error: %v", err) + } + + want := &Organization{ID: Int64(1)} + if !reflect.DeepEqual(org, want) { + t.Errorf("Organizations.Edit returned %+v, want %+v", org, want) + } +} + +func TestOrganizationsService_Edit_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Organizations.Edit(context.Background(), "%", nil) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/orgs_users_blocking.go b/vendor/github.com/google/go-github/github/orgs_users_blocking.go new file mode 100644 index 00000000..b1aecf44 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_users_blocking.go @@ -0,0 +1,91 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListBlockedUsers lists all the users blocked by an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#list-blocked-users +func (s *OrganizationsService) ListBlockedUsers(ctx context.Context, org string, opt *ListOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("orgs/%v/blocks", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + var blockedUsers []*User + resp, err := s.client.Do(ctx, req, &blockedUsers) + if err != nil { + return nil, resp, err + } + + return blockedUsers, resp, nil +} + +// IsBlocked reports whether specified user is blocked from an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#check-whether-a-user-is-blocked-from-an-organization +func (s *OrganizationsService) IsBlocked(ctx context.Context, org string, user string) (bool, *Response, error) { + u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + resp, err := s.client.Do(ctx, req, nil) + isBlocked, err := parseBoolResponse(err) + return isBlocked, resp, err +} + +// BlockUser blocks specified user from an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#block-a-user +func (s *OrganizationsService) BlockUser(ctx context.Context, org string, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) + + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + return s.client.Do(ctx, req, nil) +} + +// UnblockUser unblocks specified user from an organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#unblock-a-user +func (s *OrganizationsService) UnblockUser(ctx context.Context, org string, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/orgs_users_blocking_test.go b/vendor/github.com/google/go-github/github/orgs_users_blocking_test.go new file mode 100644 index 00000000..5d38cc44 --- /dev/null +++ b/vendor/github.com/google/go-github/github/orgs_users_blocking_test.go @@ -0,0 +1,90 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestOrganizationsService_ListBlockedUsers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/blocks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{ + "login": "octocat" + }]`) + }) + + opt := &ListOptions{Page: 2} + blockedUsers, _, err := client.Organizations.ListBlockedUsers(context.Background(), "o", opt) + if err != nil { + t.Errorf("Organizations.ListBlockedUsers returned error: %v", err) + } + + want := []*User{{Login: String("octocat")}} + if !reflect.DeepEqual(blockedUsers, want) { + t.Errorf("Organizations.ListBlockedUsers returned %+v, want %+v", blockedUsers, want) + } +} + +func TestOrganizationsService_IsBlocked(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + w.WriteHeader(http.StatusNoContent) + }) + + isBlocked, _, err := client.Organizations.IsBlocked(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.IsBlocked returned error: %v", err) + } + if want := true; isBlocked != want { + t.Errorf("Organizations.IsBlocked returned %+v, want %+v", isBlocked, want) + } +} + +func TestOrganizationsService_BlockUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.BlockUser(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.BlockUser returned error: %v", err) + } +} + +func TestOrganizationsService_UnblockUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Organizations.UnblockUser(context.Background(), "o", "u") + if err != nil { + t.Errorf("Organizations.UnblockUser returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/projects.go b/vendor/github.com/google/go-github/github/projects.go new file mode 100644 index 00000000..22061363 --- /dev/null +++ b/vendor/github.com/google/go-github/github/projects.go @@ -0,0 +1,431 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ProjectsService provides access to the projects functions in the +// GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/projects/ +type ProjectsService service + +// Project represents a GitHub Project. +type Project struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + OwnerURL *string `json:"owner_url,omitempty"` + Name *string `json:"name,omitempty"` + Body *string `json:"body,omitempty"` + Number *int `json:"number,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + + // The User object that generated the project. + Creator *User `json:"creator,omitempty"` +} + +func (p Project) String() string { + return Stringify(p) +} + +// GetProject gets a GitHub Project for a repo. +// +// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project +func (s *ProjectsService) GetProject(ctx context.Context, id int64) (*Project, *Response, error) { + u := fmt.Sprintf("projects/%v", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + project := &Project{} + resp, err := s.client.Do(ctx, req, project) + if err != nil { + return nil, resp, err + } + + return project, resp, nil +} + +// ProjectOptions specifies the parameters to the +// RepositoriesService.CreateProject and +// ProjectsService.UpdateProject methods. +type ProjectOptions struct { + // The name of the project. (Required for creation; optional for update.) + Name string `json:"name,omitempty"` + // The body of the project. (Optional.) + Body string `json:"body,omitempty"` + + // The following field(s) are only applicable for update. + // They should be left with zero values for creation. + + // State of the project. Either "open" or "closed". (Optional.) + State string `json:"state,omitempty"` +} + +// UpdateProject updates a repository project. +// +// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project +func (s *ProjectsService) UpdateProject(ctx context.Context, id int64, opt *ProjectOptions) (*Project, *Response, error) { + u := fmt.Sprintf("projects/%v", id) + req, err := s.client.NewRequest("PATCH", u, opt) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + project := &Project{} + resp, err := s.client.Do(ctx, req, project) + if err != nil { + return nil, resp, err + } + + return project, resp, nil +} + +// DeleteProject deletes a GitHub Project from a repository. +// +// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project +func (s *ProjectsService) DeleteProject(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("projects/%v", id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + return s.client.Do(ctx, req, nil) +} + +// ProjectColumn represents a column of a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/repos/projects/ +type ProjectColumn struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + ProjectURL *string `json:"project_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// ListProjectColumns lists the columns of a GitHub Project for a repo. +// +// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns +func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int64, opt *ListOptions) ([]*ProjectColumn, *Response, error) { + u := fmt.Sprintf("projects/%v/columns", projectID) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + columns := []*ProjectColumn{} + resp, err := s.client.Do(ctx, req, &columns) + if err != nil { + return nil, resp, err + } + + return columns, resp, nil +} + +// GetProjectColumn gets a column of a GitHub Project for a repo. +// +// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column +func (s *ProjectsService) GetProjectColumn(ctx context.Context, id int64) (*ProjectColumn, *Response, error) { + u := fmt.Sprintf("projects/columns/%v", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + column := &ProjectColumn{} + resp, err := s.client.Do(ctx, req, column) + if err != nil { + return nil, resp, err + } + + return column, resp, nil +} + +// ProjectColumnOptions specifies the parameters to the +// ProjectsService.CreateProjectColumn and +// ProjectsService.UpdateProjectColumn methods. +type ProjectColumnOptions struct { + // The name of the project column. (Required for creation and update.) + Name string `json:"name"` +} + +// CreateProjectColumn creates a column for the specified (by number) project. +// +// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column +func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { + u := fmt.Sprintf("projects/%v/columns", projectID) + req, err := s.client.NewRequest("POST", u, opt) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + column := &ProjectColumn{} + resp, err := s.client.Do(ctx, req, column) + if err != nil { + return nil, resp, err + } + + return column, resp, nil +} + +// UpdateProjectColumn updates a column of a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column +func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { + u := fmt.Sprintf("projects/columns/%v", columnID) + req, err := s.client.NewRequest("PATCH", u, opt) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + column := &ProjectColumn{} + resp, err := s.client.Do(ctx, req, column) + if err != nil { + return nil, resp, err + } + + return column, resp, nil +} + +// DeleteProjectColumn deletes a column from a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column +func (s *ProjectsService) DeleteProjectColumn(ctx context.Context, columnID int64) (*Response, error) { + u := fmt.Sprintf("projects/columns/%v", columnID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + return s.client.Do(ctx, req, nil) +} + +// ProjectColumnMoveOptions specifies the parameters to the +// ProjectsService.MoveProjectColumn method. +type ProjectColumnMoveOptions struct { + // Position can be one of "first", "last", or "after:", where + // is the ID of a column in the same project. (Required.) + Position string `json:"position"` +} + +// MoveProjectColumn moves a column within a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column +func (s *ProjectsService) MoveProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnMoveOptions) (*Response, error) { + u := fmt.Sprintf("projects/columns/%v/moves", columnID) + req, err := s.client.NewRequest("POST", u, opt) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + return s.client.Do(ctx, req, nil) +} + +// ProjectCard represents a card in a column of a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card +type ProjectCard struct { + URL *string `json:"url,omitempty"` + ColumnURL *string `json:"column_url,omitempty"` + ContentURL *string `json:"content_url,omitempty"` + ID *int64 `json:"id,omitempty"` + Note *string `json:"note,omitempty"` + Creator *User `json:"creator,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + + // The following fields are only populated by Webhook events. + ColumnID *int64 `json:"column_id,omitempty"` +} + +// ListProjectCards lists the cards in a column of a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards +func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int64, opt *ListOptions) ([]*ProjectCard, *Response, error) { + u := fmt.Sprintf("projects/columns/%v/cards", columnID) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + cards := []*ProjectCard{} + resp, err := s.client.Do(ctx, req, &cards) + if err != nil { + return nil, resp, err + } + + return cards, resp, nil +} + +// GetProjectCard gets a card in a column of a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card +func (s *ProjectsService) GetProjectCard(ctx context.Context, columnID int64) (*ProjectCard, *Response, error) { + u := fmt.Sprintf("projects/columns/cards/%v", columnID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + card := &ProjectCard{} + resp, err := s.client.Do(ctx, req, card) + if err != nil { + return nil, resp, err + } + + return card, resp, nil +} + +// ProjectCardOptions specifies the parameters to the +// ProjectsService.CreateProjectCard and +// ProjectsService.UpdateProjectCard methods. +type ProjectCardOptions struct { + // The note of the card. Note and ContentID are mutually exclusive. + Note string `json:"note,omitempty"` + // The ID (not Number) of the Issue to associate with this card. + // Note and ContentID are mutually exclusive. + ContentID int64 `json:"content_id,omitempty"` + // The type of content to associate with this card. Possible values are: "Issue". + ContentType string `json:"content_type,omitempty"` +} + +// CreateProjectCard creates a card in the specified column of a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card +func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { + u := fmt.Sprintf("projects/columns/%v/cards", columnID) + req, err := s.client.NewRequest("POST", u, opt) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + card := &ProjectCard{} + resp, err := s.client.Do(ctx, req, card) + if err != nil { + return nil, resp, err + } + + return card, resp, nil +} + +// UpdateProjectCard updates a card of a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card +func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { + u := fmt.Sprintf("projects/columns/cards/%v", cardID) + req, err := s.client.NewRequest("PATCH", u, opt) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + card := &ProjectCard{} + resp, err := s.client.Do(ctx, req, card) + if err != nil { + return nil, resp, err + } + + return card, resp, nil +} + +// DeleteProjectCard deletes a card from a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card +func (s *ProjectsService) DeleteProjectCard(ctx context.Context, cardID int64) (*Response, error) { + u := fmt.Sprintf("projects/columns/cards/%v", cardID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + return s.client.Do(ctx, req, nil) +} + +// ProjectCardMoveOptions specifies the parameters to the +// ProjectsService.MoveProjectCard method. +type ProjectCardMoveOptions struct { + // Position can be one of "top", "bottom", or "after:", where + // is the ID of a card in the same project. + Position string `json:"position"` + // ColumnID is the ID of a column in the same project. Note that ColumnID + // is required when using Position "after:" when that card is in + // another column; otherwise it is optional. + ColumnID int64 `json:"column_id,omitempty"` +} + +// MoveProjectCard moves a card within a GitHub Project. +// +// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card +func (s *ProjectsService) MoveProjectCard(ctx context.Context, cardID int64, opt *ProjectCardMoveOptions) (*Response, error) { + u := fmt.Sprintf("projects/columns/cards/%v/moves", cardID) + req, err := s.client.NewRequest("POST", u, opt) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/projects_test.go b/vendor/github.com/google/go-github/github/projects_test.go new file mode 100644 index 00000000..9cb12aaa --- /dev/null +++ b/vendor/github.com/google/go-github/github/projects_test.go @@ -0,0 +1,371 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestProjectsService_UpdateProject(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectOptions{Name: "Project Name", Body: "Project body.", State: "open"} + + mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + project, _, err := client.Projects.UpdateProject(context.Background(), 1, input) + if err != nil { + t.Errorf("Projects.UpdateProject returned error: %v", err) + } + + want := &Project{ID: Int64(1)} + if !reflect.DeepEqual(project, want) { + t.Errorf("Projects.UpdateProject returned %+v, want %+v", project, want) + } +} + +func TestProjectsService_GetProject(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + project, _, err := client.Projects.GetProject(context.Background(), 1) + if err != nil { + t.Errorf("Projects.GetProject returned error: %v", err) + } + + want := &Project{ID: Int64(1)} + if !reflect.DeepEqual(project, want) { + t.Errorf("Projects.GetProject returned %+v, want %+v", project, want) + } +} + +func TestProjectsService_DeleteProject(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + }) + + _, err := client.Projects.DeleteProject(context.Background(), 1) + if err != nil { + t.Errorf("Projects.DeleteProject returned error: %v", err) + } +} + +func TestProjectsService_ListProjectColumns(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/1/columns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + columns, _, err := client.Projects.ListProjectColumns(context.Background(), 1, opt) + if err != nil { + t.Errorf("Projects.ListProjectColumns returned error: %v", err) + } + + want := []*ProjectColumn{{ID: Int64(1)}} + if !reflect.DeepEqual(columns, want) { + t.Errorf("Projects.ListProjectColumns returned %+v, want %+v", columns, want) + } +} + +func TestProjectsService_GetProjectColumn(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + column, _, err := client.Projects.GetProjectColumn(context.Background(), 1) + if err != nil { + t.Errorf("Projects.GetProjectColumn returned error: %v", err) + } + + want := &ProjectColumn{ID: Int64(1)} + if !reflect.DeepEqual(column, want) { + t.Errorf("Projects.GetProjectColumn returned %+v, want %+v", column, want) + } +} + +func TestProjectsService_CreateProjectColumn(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectColumnOptions{Name: "Column Name"} + + mux.HandleFunc("/projects/1/columns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectColumnOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + column, _, err := client.Projects.CreateProjectColumn(context.Background(), 1, input) + if err != nil { + t.Errorf("Projects.CreateProjectColumn returned error: %v", err) + } + + want := &ProjectColumn{ID: Int64(1)} + if !reflect.DeepEqual(column, want) { + t.Errorf("Projects.CreateProjectColumn returned %+v, want %+v", column, want) + } +} + +func TestProjectsService_UpdateProjectColumn(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectColumnOptions{Name: "Column Name"} + + mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectColumnOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + column, _, err := client.Projects.UpdateProjectColumn(context.Background(), 1, input) + if err != nil { + t.Errorf("Projects.UpdateProjectColumn returned error: %v", err) + } + + want := &ProjectColumn{ID: Int64(1)} + if !reflect.DeepEqual(column, want) { + t.Errorf("Projects.UpdateProjectColumn returned %+v, want %+v", column, want) + } +} + +func TestProjectsService_DeleteProjectColumn(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + }) + + _, err := client.Projects.DeleteProjectColumn(context.Background(), 1) + if err != nil { + t.Errorf("Projects.DeleteProjectColumn returned error: %v", err) + } +} + +func TestProjectsService_MoveProjectColumn(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectColumnMoveOptions{Position: "after:12345"} + + mux.HandleFunc("/projects/columns/1/moves", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectColumnMoveOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + }) + + _, err := client.Projects.MoveProjectColumn(context.Background(), 1, input) + if err != nil { + t.Errorf("Projects.MoveProjectColumn returned error: %v", err) + } +} + +func TestProjectsService_ListProjectCards(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/columns/1/cards", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + cards, _, err := client.Projects.ListProjectCards(context.Background(), 1, opt) + if err != nil { + t.Errorf("Projects.ListProjectCards returned error: %v", err) + } + + want := []*ProjectCard{{ID: Int64(1)}} + if !reflect.DeepEqual(cards, want) { + t.Errorf("Projects.ListProjectCards returned %+v, want %+v", cards, want) + } +} + +func TestProjectsService_GetProjectCard(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + card, _, err := client.Projects.GetProjectCard(context.Background(), 1) + if err != nil { + t.Errorf("Projects.GetProjectCard returned error: %v", err) + } + + want := &ProjectCard{ID: Int64(1)} + if !reflect.DeepEqual(card, want) { + t.Errorf("Projects.GetProjectCard returned %+v, want %+v", card, want) + } +} + +func TestProjectsService_CreateProjectCard(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectCardOptions{ + ContentID: 12345, + ContentType: "Issue", + } + + mux.HandleFunc("/projects/columns/1/cards", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectCardOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + card, _, err := client.Projects.CreateProjectCard(context.Background(), 1, input) + if err != nil { + t.Errorf("Projects.CreateProjectCard returned error: %v", err) + } + + want := &ProjectCard{ID: Int64(1)} + if !reflect.DeepEqual(card, want) { + t.Errorf("Projects.CreateProjectCard returned %+v, want %+v", card, want) + } +} + +func TestProjectsService_UpdateProjectCard(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectCardOptions{ + ContentID: 12345, + ContentType: "Issue", + } + + mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectCardOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + card, _, err := client.Projects.UpdateProjectCard(context.Background(), 1, input) + if err != nil { + t.Errorf("Projects.UpdateProjectCard returned error: %v", err) + } + + want := &ProjectCard{ID: Int64(1)} + if !reflect.DeepEqual(card, want) { + t.Errorf("Projects.UpdateProjectCard returned %+v, want %+v", card, want) + } +} + +func TestProjectsService_DeleteProjectCard(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + }) + + _, err := client.Projects.DeleteProjectCard(context.Background(), 1) + if err != nil { + t.Errorf("Projects.DeleteProjectCard returned error: %v", err) + } +} + +func TestProjectsService_MoveProjectCard(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectCardMoveOptions{Position: "after:12345"} + + mux.HandleFunc("/projects/columns/cards/1/moves", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectCardMoveOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + }) + + _, err := client.Projects.MoveProjectCard(context.Background(), 1, input) + if err != nil { + t.Errorf("Projects.MoveProjectCard returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/pulls.go b/vendor/github.com/google/go-github/github/pulls.go new file mode 100644 index 00000000..31d492ee --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls.go @@ -0,0 +1,371 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "fmt" + "time" +) + +// PullRequestsService handles communication with the pull request related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/pulls/ +type PullRequestsService service + +// PullRequest represents a GitHub pull request on a repository. +type PullRequest struct { + ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` + State *string `json:"state,omitempty"` + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + ClosedAt *time.Time `json:"closed_at,omitempty"` + MergedAt *time.Time `json:"merged_at,omitempty"` + User *User `json:"user,omitempty"` + Merged *bool `json:"merged,omitempty"` + Mergeable *bool `json:"mergeable,omitempty"` + MergeableState *string `json:"mergeable_state,omitempty"` + MergedBy *User `json:"merged_by,omitempty"` + MergeCommitSHA *string `json:"merge_commit_sha,omitempty"` + Comments *int `json:"comments,omitempty"` + Commits *int `json:"commits,omitempty"` + Additions *int `json:"additions,omitempty"` + Deletions *int `json:"deletions,omitempty"` + ChangedFiles *int `json:"changed_files,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + IssueURL *string `json:"issue_url,omitempty"` + StatusesURL *string `json:"statuses_url,omitempty"` + DiffURL *string `json:"diff_url,omitempty"` + PatchURL *string `json:"patch_url,omitempty"` + ReviewCommentsURL *string `json:"review_comments_url,omitempty"` + ReviewCommentURL *string `json:"review_comment_url,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Assignees []*User `json:"assignees,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` + AuthorAssociation *string `json:"author_association,omitempty"` + NodeID *string `json:"node_id,omitempty"` + + Head *PullRequestBranch `json:"head,omitempty"` + Base *PullRequestBranch `json:"base,omitempty"` +} + +func (p PullRequest) String() string { + return Stringify(p) +} + +// PullRequestBranch represents a base or head branch in a GitHub pull request. +type PullRequestBranch struct { + Label *string `json:"label,omitempty"` + Ref *string `json:"ref,omitempty"` + SHA *string `json:"sha,omitempty"` + Repo *Repository `json:"repo,omitempty"` + User *User `json:"user,omitempty"` +} + +// PullRequestListOptions specifies the optional parameters to the +// PullRequestsService.List method. +type PullRequestListOptions struct { + // State filters pull requests based on their state. Possible values are: + // open, closed. Default is "open". + State string `url:"state,omitempty"` + + // Head filters pull requests by head user and branch name in the format of: + // "user:ref-name". + Head string `url:"head,omitempty"` + + // Base filters pull requests by base branch name. + Base string `url:"base,omitempty"` + + // Sort specifies how to sort pull requests. Possible values are: created, + // updated, popularity, long-running. Default is "created". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort pull requests. Possible values are: asc, desc. + // If Sort is "created" or not specified, Default is "desc", otherwise Default + // is "asc" + Direction string `url:"direction,omitempty"` + + ListOptions +} + +// List the pull requests for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests +func (s *PullRequestsService) List(ctx context.Context, owner string, repo string, opt *PullRequestListOptions) ([]*PullRequest, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var pulls []*PullRequest + resp, err := s.client.Do(ctx, req, &pulls) + if err != nil { + return nil, resp, err + } + + return pulls, resp, nil +} + +// Get a single pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request +func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string, number int) (*PullRequest, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + pull := new(PullRequest) + resp, err := s.client.Do(ctx, req, pull) + if err != nil { + return nil, resp, err + } + + return pull, resp, nil +} + +// GetRaw gets a single pull request in raw (diff or patch) format. +func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo string, number int, opt RawOptions) (string, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return "", nil, err + } + + switch opt.Type { + case Diff: + req.Header.Set("Accept", mediaTypeV3Diff) + case Patch: + req.Header.Set("Accept", mediaTypeV3Patch) + default: + return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) + } + + var buf bytes.Buffer + resp, err := s.client.Do(ctx, req, &buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// NewPullRequest represents a new pull request to be created. +type NewPullRequest struct { + Title *string `json:"title,omitempty"` + Head *string `json:"head,omitempty"` + Base *string `json:"base,omitempty"` + Body *string `json:"body,omitempty"` + Issue *int `json:"issue,omitempty"` + MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` +} + +// Create a new pull request on the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#create-a-pull-request +func (s *PullRequestsService) Create(ctx context.Context, owner string, repo string, pull *NewPullRequest) (*PullRequest, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) + req, err := s.client.NewRequest("POST", u, pull) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + p := new(PullRequest) + resp, err := s.client.Do(ctx, req, p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +type pullRequestUpdate struct { + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + State *string `json:"state,omitempty"` + Base *string `json:"base,omitempty"` + MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` +} + +// Edit a pull request. +// pull must not be nil. +// +// The following fields are editable: Title, Body, State, Base.Ref and MaintainerCanModify. +// Base.Ref updates the base branch of the pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request +func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo string, number int, pull *PullRequest) (*PullRequest, *Response, error) { + if pull == nil { + return nil, nil, fmt.Errorf("pull must be provided") + } + + u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) + + update := &pullRequestUpdate{ + Title: pull.Title, + Body: pull.Body, + State: pull.State, + MaintainerCanModify: pull.MaintainerCanModify, + } + if pull.Base != nil { + update.Base = pull.Base.Ref + } + + req, err := s.client.NewRequest("PATCH", u, update) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + p := new(PullRequest) + resp, err := s.client.Do(ctx, req, p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// ListCommits lists the commits in a pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request +func (s *PullRequestsService) ListCommits(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*RepositoryCommit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/commits", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGitSigningPreview) + + var commits []*RepositoryCommit + resp, err := s.client.Do(ctx, req, &commits) + if err != nil { + return nil, resp, err + } + + return commits, resp, nil +} + +// ListFiles lists the files in a pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests-files +func (s *PullRequestsService) ListFiles(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*CommitFile, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/files", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var commitFiles []*CommitFile + resp, err := s.client.Do(ctx, req, &commitFiles) + if err != nil { + return nil, resp, err + } + + return commitFiles, resp, nil +} + +// IsMerged checks if a pull request has been merged. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged +func (s *PullRequestsService) IsMerged(ctx context.Context, owner string, repo string, number int) (bool, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + merged, err := parseBoolResponse(err) + return merged, resp, err +} + +// PullRequestMergeResult represents the result of merging a pull request. +type PullRequestMergeResult struct { + SHA *string `json:"sha,omitempty"` + Merged *bool `json:"merged,omitempty"` + Message *string `json:"message,omitempty"` +} + +// PullRequestOptions lets you define how a pull request will be merged. +type PullRequestOptions struct { + CommitTitle string // Extra detail to append to automatic commit message. (Optional.) + SHA string // SHA that pull request head must match to allow merge. (Optional.) + + // The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge. (Optional.) + MergeMethod string +} + +type pullRequestMergeRequest struct { + CommitMessage string `json:"commit_message"` + CommitTitle string `json:"commit_title,omitempty"` + MergeMethod string `json:"merge_method,omitempty"` + SHA string `json:"sha,omitempty"` +} + +// Merge a pull request (Merge Buttonâ„¢). +// commitMessage is the title for the automatic commit message. +// +// GitHub API docs: https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-buttontrade +func (s *PullRequestsService) Merge(ctx context.Context, owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) + + pullRequestBody := &pullRequestMergeRequest{CommitMessage: commitMessage} + if options != nil { + pullRequestBody.CommitTitle = options.CommitTitle + pullRequestBody.MergeMethod = options.MergeMethod + pullRequestBody.SHA = options.SHA + } + req, err := s.client.NewRequest("PUT", u, pullRequestBody) + if err != nil { + return nil, nil, err + } + + mergeResult := new(PullRequestMergeResult) + resp, err := s.client.Do(ctx, req, mergeResult) + if err != nil { + return nil, resp, err + } + + return mergeResult, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/pulls_comments.go b/vendor/github.com/google/go-github/github/pulls_comments.go new file mode 100644 index 00000000..ff892279 --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls_comments.go @@ -0,0 +1,157 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// PullRequestComment represents a comment left on a pull request. +type PullRequestComment struct { + ID *int64 `json:"id,omitempty"` + InReplyTo *int64 `json:"in_reply_to,omitempty"` + Body *string `json:"body,omitempty"` + Path *string `json:"path,omitempty"` + DiffHunk *string `json:"diff_hunk,omitempty"` + Position *int `json:"position,omitempty"` + OriginalPosition *int `json:"original_position,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + OriginalCommitID *string `json:"original_commit_id,omitempty"` + User *User `json:"user,omitempty"` + Reactions *Reactions `json:"reactions,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + PullRequestURL *string `json:"pull_request_url,omitempty"` +} + +func (p PullRequestComment) String() string { + return Stringify(p) +} + +// PullRequestListCommentsOptions specifies the optional parameters to the +// PullRequestsService.ListComments method. +type PullRequestListCommentsOptions struct { + // Sort specifies how to sort comments. Possible values are: created, updated. + Sort string `url:"sort,omitempty"` + + // Direction in which to sort comments. Possible values are: asc, desc. + Direction string `url:"direction,omitempty"` + + // Since filters comments by time. + Since time.Time `url:"since,omitempty"` + + ListOptions +} + +// ListComments lists all comments on the specified pull request. Specifying a +// pull request number of 0 will return all comments on all pull requests for +// the repository. +// +// GitHub API docs: https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request +func (s *PullRequestsService) ListComments(ctx context.Context, owner string, repo string, number int, opt *PullRequestListCommentsOptions) ([]*PullRequestComment, *Response, error) { + var u string + if number == 0 { + u = fmt.Sprintf("repos/%v/%v/pulls/comments", owner, repo) + } else { + u = fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var comments []*PullRequestComment + resp, err := s.client.Do(ctx, req, &comments) + if err != nil { + return nil, resp, err + } + + return comments, resp, nil +} + +// GetComment fetches the specified pull request comment. +// +// GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment +func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo string, number int) (*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + comment := new(PullRequestComment) + resp, err := s.client.Do(ctx, req, comment) + if err != nil { + return nil, resp, err + } + + return comment, resp, nil +} + +// CreateComment creates a new comment on the specified pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment +func (s *PullRequestsService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) + req, err := s.client.NewRequest("POST", u, comment) + if err != nil { + return nil, nil, err + } + + c := new(PullRequestComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// EditComment updates a pull request comment. +// +// GitHub API docs: https://developer.github.com/v3/pulls/comments/#edit-a-comment +func (s *PullRequestsService) EditComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) + req, err := s.client.NewRequest("PATCH", u, comment) + if err != nil { + return nil, nil, err + } + + c := new(PullRequestComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// DeleteComment deletes a pull request comment. +// +// GitHub API docs: https://developer.github.com/v3/pulls/comments/#delete-a-comment +func (s *PullRequestsService) DeleteComment(ctx context.Context, owner string, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/pulls_comments_test.go b/vendor/github.com/google/go-github/github/pulls_comments_test.go new file mode 100644 index 00000000..becb30e1 --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls_comments_test.go @@ -0,0 +1,203 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestPullRequestsService_ListComments_allPulls(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{ + "sort": "updated", + "direction": "desc", + "since": "2002-02-10T15:30:00Z", + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &PullRequestListCommentsOptions{ + Sort: "updated", + Direction: "desc", + Since: time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), + ListOptions: ListOptions{Page: 2}, + } + pulls, _, err := client.PullRequests.ListComments(context.Background(), "o", "r", 0, opt) + if err != nil { + t.Errorf("PullRequests.ListComments returned error: %v", err) + } + + want := []*PullRequestComment{{ID: Int64(1)}} + if !reflect.DeepEqual(pulls, want) { + t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want) + } +} + +func TestPullRequestsService_ListComments_specificPull(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + pulls, _, err := client.PullRequests.ListComments(context.Background(), "o", "r", 1, nil) + if err != nil { + t.Errorf("PullRequests.ListComments returned error: %v", err) + } + + want := []*PullRequestComment{{ID: Int64(1)}} + if !reflect.DeepEqual(pulls, want) { + t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want) + } +} + +func TestPullRequestsService_ListComments_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.ListComments(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestPullRequestsService_GetComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.PullRequests.GetComment(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("PullRequests.GetComment returned error: %v", err) + } + + want := &PullRequestComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("PullRequests.GetComment returned %+v, want %+v", comment, want) + } +} + +func TestPullRequestsService_GetComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.GetComment(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} + +func TestPullRequestsService_CreateComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &PullRequestComment{Body: String("b")} + + mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) { + v := new(PullRequestComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.PullRequests.CreateComment(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("PullRequests.CreateComment returned error: %v", err) + } + + want := &PullRequestComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("PullRequests.CreateComment returned %+v, want %+v", comment, want) + } +} + +func TestPullRequestsService_CreateComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.CreateComment(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestPullRequestsService_EditComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &PullRequestComment{Body: String("b")} + + mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { + v := new(PullRequestComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.PullRequests.EditComment(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("PullRequests.EditComment returned error: %v", err) + } + + want := &PullRequestComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("PullRequests.EditComment returned %+v, want %+v", comment, want) + } +} + +func TestPullRequestsService_EditComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.EditComment(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestPullRequestsService_DeleteComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.PullRequests.DeleteComment(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("PullRequests.DeleteComment returned error: %v", err) + } +} + +func TestPullRequestsService_DeleteComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.PullRequests.DeleteComment(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/pulls_reviewers.go b/vendor/github.com/google/go-github/github/pulls_reviewers.go new file mode 100644 index 00000000..15b47be3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls_reviewers.go @@ -0,0 +1,88 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ReviewersRequest specifies users and teams for a pull request review request. +type ReviewersRequest struct { + Reviewers []string `json:"reviewers,omitempty"` + TeamReviewers []string `json:"team_reviewers,omitempty"` +} + +// Reviewers represents reviewers of a pull request. +type Reviewers struct { + Users []*User `json:"users,omitempty"` + Teams []*Team `json:"teams,omitempty"` +} + +// RequestReviewers creates a review request for the provided reviewers for the specified pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#create-a-review-request +func (s *PullRequestsService) RequestReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*PullRequest, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) + req, err := s.client.NewRequest("POST", u, &reviewers) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeTeamReviewPreview) + + r := new(PullRequest) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// ListReviewers lists reviewers whose reviews have been requested on the specified pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#list-review-requests +func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo string, number int, opt *ListOptions) (*Reviewers, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/requested_reviewers", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeTeamReviewPreview) + + reviewers := new(Reviewers) + resp, err := s.client.Do(ctx, req, reviewers) + if err != nil { + return nil, resp, err + } + + return reviewers, resp, nil +} + +// RemoveReviewers removes the review request for the provided reviewers for the specified pull request. +// +// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request +func (s *PullRequestsService) RemoveReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*Response, error) { + u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) + req, err := s.client.NewRequest("DELETE", u, &reviewers) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeTeamReviewPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/pulls_reviewers_test.go b/vendor/github.com/google/go-github/github/pulls_reviewers_test.go new file mode 100644 index 00000000..48a3bf0c --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls_reviewers_test.go @@ -0,0 +1,104 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRequestReviewers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testBody(t, r, `{"reviewers":["octocat","googlebot"],"team_reviewers":["justice-league","injustice-league"]}`+"\n") + testHeader(t, r, "Accept", mediaTypeTeamReviewPreview) + fmt.Fprint(w, `{"number":1}`) + }) + + // This returns a PR, unmarshalling of which is tested elsewhere + pull, _, err := client.PullRequests.RequestReviewers(context.Background(), "o", "r", 1, ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league", "injustice-league"}}) + if err != nil { + t.Errorf("PullRequests.RequestReviewers returned error: %v", err) + } + want := &PullRequest{Number: Int(1)} + if !reflect.DeepEqual(pull, want) { + t.Errorf("PullRequests.RequestReviewers returned %+v, want %+v", pull, want) + } +} + +func TestRemoveReviewers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeTeamReviewPreview) + testBody(t, r, `{"reviewers":["octocat","googlebot"],"team_reviewers":["justice-league"]}`+"\n") + }) + + _, err := client.PullRequests.RemoveReviewers(context.Background(), "o", "r", 1, ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league"}}) + if err != nil { + t.Errorf("PullRequests.RemoveReviewers returned error: %v", err) + } +} + +func TestListReviewers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeTeamReviewPreview) + fmt.Fprint(w, `{"users":[{"login":"octocat","id":1}],"teams":[{"id":1,"name":"Justice League"}]}`) + }) + + reviewers, _, err := client.PullRequests.ListReviewers(context.Background(), "o", "r", 1, nil) + if err != nil { + t.Errorf("PullRequests.ListReviewers returned error: %v", err) + } + + want := &Reviewers{ + Users: []*User{ + { + Login: String("octocat"), + ID: Int64(1), + }, + }, + Teams: []*Team{ + { + ID: Int64(1), + Name: String("Justice League"), + }, + }, + } + if !reflect.DeepEqual(reviewers, want) { + t.Errorf("PullRequests.ListReviewers returned %+v, want %+v", reviewers, want) + } +} + +func TestListReviewers_withOptions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `{}`) + }) + + _, _, err := client.PullRequests.ListReviewers(context.Background(), "o", "r", 1, &ListOptions{Page: 2}) + if err != nil { + t.Errorf("PullRequests.ListReviewers returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/pulls_reviews.go b/vendor/github.com/google/go-github/github/pulls_reviews.go new file mode 100644 index 00000000..1aceb0d4 --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls_reviews.go @@ -0,0 +1,236 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// PullRequestReview represents a review of a pull request. +type PullRequestReview struct { + ID *int64 `json:"id,omitempty"` + User *User `json:"user,omitempty"` + Body *string `json:"body,omitempty"` + SubmittedAt *time.Time `json:"submitted_at,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + PullRequestURL *string `json:"pull_request_url,omitempty"` + State *string `json:"state,omitempty"` +} + +func (p PullRequestReview) String() string { + return Stringify(p) +} + +// DraftReviewComment represents a comment part of the review. +type DraftReviewComment struct { + Path *string `json:"path,omitempty"` + Position *int `json:"position,omitempty"` + Body *string `json:"body,omitempty"` +} + +func (c DraftReviewComment) String() string { + return Stringify(c) +} + +// PullRequestReviewRequest represents a request to create a review. +type PullRequestReviewRequest struct { + CommitID *string `json:"commit_id,omitempty"` + Body *string `json:"body,omitempty"` + Event *string `json:"event,omitempty"` + Comments []*DraftReviewComment `json:"comments,omitempty"` +} + +func (r PullRequestReviewRequest) String() string { + return Stringify(r) +} + +// PullRequestReviewDismissalRequest represents a request to dismiss a review. +type PullRequestReviewDismissalRequest struct { + Message *string `json:"message,omitempty"` +} + +func (r PullRequestReviewDismissalRequest) String() string { + return Stringify(r) +} + +// ListReviews lists all reviews on the specified pull request. +// +// TODO: Follow up with GitHub support about an issue with this method's +// returned error format and remove this comment once it's fixed. +// Read more about it here - https://github.com/google/go-github/issues/540 +// +// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request +func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var reviews []*PullRequestReview + resp, err := s.client.Do(ctx, req, &reviews) + if err != nil { + return nil, resp, err + } + + return reviews, resp, nil +} + +// GetReview fetches the specified pull request review. +// +// TODO: Follow up with GitHub support about an issue with this method's +// returned error format and remove this comment once it's fixed. +// Read more about it here - https://github.com/google/go-github/issues/540 +// +// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-a-single-review +func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number, reviewID int64) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + review := new(PullRequestReview) + resp, err := s.client.Do(ctx, req, review) + if err != nil { + return nil, resp, err + } + + return review, resp, nil +} + +// DeletePendingReview deletes the specified pull request pending review. +// +// TODO: Follow up with GitHub support about an issue with this method's +// returned error format and remove this comment once it's fixed. +// Read more about it here - https://github.com/google/go-github/issues/540 +// +// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review +func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number, reviewID int64) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + review := new(PullRequestReview) + resp, err := s.client.Do(ctx, req, review) + if err != nil { + return nil, resp, err + } + + return review, resp, nil +} + +// ListReviewComments lists all the comments for the specified review. +// +// TODO: Follow up with GitHub support about an issue with this method's +// returned error format and remove this comment once it's fixed. +// Read more about it here - https://github.com/google/go-github/issues/540 +// +// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review +func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number, reviewID int64, opt *ListOptions) ([]*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var comments []*PullRequestComment + resp, err := s.client.Do(ctx, req, &comments) + if err != nil { + return nil, resp, err + } + + return comments, resp, nil +} + +// CreateReview creates a new review on the specified pull request. +// +// TODO: Follow up with GitHub support about an issue with this method's +// returned error format and remove this comment once it's fixed. +// Read more about it here - https://github.com/google/go-github/issues/540 +// +// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review +func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) + + req, err := s.client.NewRequest("POST", u, review) + if err != nil { + return nil, nil, err + } + + r := new(PullRequestReview) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// SubmitReview submits a specified review on the specified pull request. +// +// TODO: Follow up with GitHub support about an issue with this method's +// returned error format and remove this comment once it's fixed. +// Read more about it here - https://github.com/google/go-github/issues/540 +// +// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review +func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number, reviewID int64, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID) + + req, err := s.client.NewRequest("POST", u, review) + if err != nil { + return nil, nil, err + } + + r := new(PullRequestReview) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// DismissReview dismisses a specified review on the specified pull request. +// +// TODO: Follow up with GitHub support about an issue with this method's +// returned error format and remove this comment once it's fixed. +// Read more about it here - https://github.com/google/go-github/issues/540 +// +// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review +func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number, reviewID int64, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID) + + req, err := s.client.NewRequest("PUT", u, review) + if err != nil { + return nil, nil, err + } + + r := new(PullRequestReview) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/pulls_reviews_test.go b/vendor/github.com/google/go-github/github/pulls_reviews_test.go new file mode 100644 index 00000000..58e301bd --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls_reviews_test.go @@ -0,0 +1,273 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestPullRequestsService_ListReviews(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + reviews, _, err := client.PullRequests.ListReviews(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("PullRequests.ListReviews returned error: %v", err) + } + + want := []*PullRequestReview{ + {ID: Int64(1)}, + {ID: Int64(2)}, + } + if !reflect.DeepEqual(reviews, want) { + t.Errorf("PullRequests.ListReviews returned %+v, want %+v", reviews, want) + } +} + +func TestPullRequestsService_ListReviews_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.ListReviews(context.Background(), "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestPullRequestsService_GetReview(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + review, _, err := client.PullRequests.GetReview(context.Background(), "o", "r", 1, 1) + if err != nil { + t.Errorf("PullRequests.GetReview returned error: %v", err) + } + + want := &PullRequestReview{ID: Int64(1)} + if !reflect.DeepEqual(review, want) { + t.Errorf("PullRequests.GetReview returned %+v, want %+v", review, want) + } +} + +func TestPullRequestsService_GetReview_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.GetReview(context.Background(), "%", "r", 1, 1) + testURLParseError(t, err) +} + +func TestPullRequestsService_DeletePendingReview(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{"id":1}`) + }) + + review, _, err := client.PullRequests.DeletePendingReview(context.Background(), "o", "r", 1, 1) + if err != nil { + t.Errorf("PullRequests.DeletePendingReview returned error: %v", err) + } + + want := &PullRequestReview{ID: Int64(1)} + if !reflect.DeepEqual(review, want) { + t.Errorf("PullRequests.DeletePendingReview returned %+v, want %+v", review, want) + } +} + +func TestPullRequestsService_DeletePendingReview_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.DeletePendingReview(context.Background(), "%", "r", 1, 1) + testURLParseError(t, err) +} + +func TestPullRequestsService_ListReviewComments(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + comments, _, err := client.PullRequests.ListReviewComments(context.Background(), "o", "r", 1, 1, nil) + if err != nil { + t.Errorf("PullRequests.ListReviewComments returned error: %v", err) + } + + want := []*PullRequestComment{ + {ID: Int64(1)}, + {ID: Int64(2)}, + } + if !reflect.DeepEqual(comments, want) { + t.Errorf("PullRequests.ListReviewComments returned %+v, want %+v", comments, want) + } +} + +func TestPullRequestsService_ListReviewComments_withOptions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[]`) + }) + + _, _, err := client.PullRequests.ListReviewComments(context.Background(), "o", "r", 1, 1, &ListOptions{Page: 2}) + if err != nil { + t.Errorf("PullRequests.ListReviewComments returned error: %v", err) + } +} + +func TestPullRequestsService_ListReviewComments_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.ListReviewComments(context.Background(), "%", "r", 1, 1, nil) + testURLParseError(t, err) +} + +func TestPullRequestsService_CreateReview(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &PullRequestReviewRequest{ + CommitID: String("commit_id"), + Body: String("b"), + Event: String("APPROVE"), + } + + mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) { + v := new(PullRequestReviewRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + review, _, err := client.PullRequests.CreateReview(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("PullRequests.CreateReview returned error: %v", err) + } + + want := &PullRequestReview{ID: Int64(1)} + if !reflect.DeepEqual(review, want) { + t.Errorf("PullRequests.CreateReview returned %+v, want %+v", review, want) + } +} + +func TestPullRequestsService_CreateReview_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.CreateReview(context.Background(), "%", "r", 1, &PullRequestReviewRequest{}) + testURLParseError(t, err) +} + +func TestPullRequestsService_SubmitReview(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &PullRequestReviewRequest{ + Body: String("b"), + Event: String("APPROVE"), + } + + mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/events", func(w http.ResponseWriter, r *http.Request) { + v := new(PullRequestReviewRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + review, _, err := client.PullRequests.SubmitReview(context.Background(), "o", "r", 1, 1, input) + if err != nil { + t.Errorf("PullRequests.SubmitReview returned error: %v", err) + } + + want := &PullRequestReview{ID: Int64(1)} + if !reflect.DeepEqual(review, want) { + t.Errorf("PullRequests.SubmitReview returned %+v, want %+v", review, want) + } +} + +func TestPullRequestsService_SubmitReview_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.SubmitReview(context.Background(), "%", "r", 1, 1, &PullRequestReviewRequest{}) + testURLParseError(t, err) +} + +func TestPullRequestsService_DismissReview(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &PullRequestReviewDismissalRequest{Message: String("m")} + + mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/dismissals", func(w http.ResponseWriter, r *http.Request) { + v := new(PullRequestReviewDismissalRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + review, _, err := client.PullRequests.DismissReview(context.Background(), "o", "r", 1, 1, input) + if err != nil { + t.Errorf("PullRequests.DismissReview returned error: %v", err) + } + + want := &PullRequestReview{ID: Int64(1)} + if !reflect.DeepEqual(review, want) { + t.Errorf("PullRequests.DismissReview returned %+v, want %+v", review, want) + } +} + +func TestPullRequestsService_DismissReview_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.DismissReview(context.Background(), "%", "r", 1, 1, &PullRequestReviewDismissalRequest{}) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/pulls_test.go b/vendor/github.com/google/go-github/github/pulls_test.go new file mode 100644 index 00000000..7d02c1a6 --- /dev/null +++ b/vendor/github.com/google/go-github/github/pulls_test.go @@ -0,0 +1,529 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestPullRequestsService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{ + "state": "closed", + "head": "h", + "base": "b", + "sort": "created", + "direction": "desc", + "page": "2", + }) + fmt.Fprint(w, `[{"number":1}]`) + }) + + opt := &PullRequestListOptions{"closed", "h", "b", "created", "desc", ListOptions{Page: 2}} + pulls, _, err := client.PullRequests.List(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("PullRequests.List returned error: %v", err) + } + + want := []*PullRequest{{Number: Int(1)}} + if !reflect.DeepEqual(pulls, want) { + t.Errorf("PullRequests.List returned %+v, want %+v", pulls, want) + } +} + +func TestPullRequestsService_List_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.List(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestPullRequestsService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"number":1}`) + }) + + pull, _, err := client.PullRequests.Get(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("PullRequests.Get returned error: %v", err) + } + + want := &PullRequest{Number: Int(1)} + if !reflect.DeepEqual(pull, want) { + t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) + } +} + +func TestPullRequestsService_GetRaw_diff(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + const rawStr = "@@diff content" + + mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3Diff) + fmt.Fprint(w, rawStr) + }) + + got, _, err := client.PullRequests.GetRaw(context.Background(), "o", "r", 1, RawOptions{Diff}) + if err != nil { + t.Fatalf("PullRequests.GetRaw returned error: %v", err) + } + want := rawStr + if got != want { + t.Errorf("PullRequests.GetRaw returned %s want %s", got, want) + } +} + +func TestPullRequestsService_GetRaw_patch(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + const rawStr = "@@patch content" + + mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3Patch) + fmt.Fprint(w, rawStr) + }) + + got, _, err := client.PullRequests.GetRaw(context.Background(), "o", "r", 1, RawOptions{Patch}) + if err != nil { + t.Fatalf("PullRequests.GetRaw returned error: %v", err) + } + want := rawStr + if got != want { + t.Errorf("PullRequests.GetRaw returned %s want %s", got, want) + } +} + +func TestPullRequestsService_GetRaw_invalid(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.GetRaw(context.Background(), "o", "r", 1, RawOptions{100}) + if err == nil { + t.Fatal("PullRequests.GetRaw should return error") + } + if !strings.Contains(err.Error(), "unsupported raw type") { + t.Error("PullRequests.GetRaw should return unsupported raw type error") + } +} + +func TestPullRequestsService_Get_headAndBase(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"number":1,"head":{"ref":"r2","repo":{"id":2}},"base":{"ref":"r1","repo":{"id":1}}}`) + }) + + pull, _, err := client.PullRequests.Get(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("PullRequests.Get returned error: %v", err) + } + + want := &PullRequest{ + Number: Int(1), + Head: &PullRequestBranch{ + Ref: String("r2"), + Repo: &Repository{ID: Int64(2)}, + }, + Base: &PullRequestBranch{ + Ref: String("r1"), + Repo: &Repository{ID: Int64(1)}, + }, + } + if !reflect.DeepEqual(pull, want) { + t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) + } +} + +func TestPullRequestsService_Get_urlFields(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"number":1, + "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347", + "html_url": "https://github.com/octocat/Hello-World/pull/1347", + "issue_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e", + "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff", + "patch_url": "https://github.com/octocat/Hello-World/pull/1347.patch", + "review_comments_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments", + "review_comment_url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"}`) + }) + + pull, _, err := client.PullRequests.Get(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("PullRequests.Get returned error: %v", err) + } + + want := &PullRequest{ + Number: Int(1), + URL: String("https://api.github.com/repos/octocat/Hello-World/pulls/1347"), + HTMLURL: String("https://github.com/octocat/Hello-World/pull/1347"), + IssueURL: String("https://api.github.com/repos/octocat/Hello-World/issues/1347"), + StatusesURL: String("https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"), + DiffURL: String("https://github.com/octocat/Hello-World/pull/1347.diff"), + PatchURL: String("https://github.com/octocat/Hello-World/pull/1347.patch"), + ReviewCommentsURL: String("https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments"), + ReviewCommentURL: String("https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"), + } + + if !reflect.DeepEqual(pull, want) { + t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) + } +} + +func TestPullRequestsService_Get_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.Get(context.Background(), "%", "r", 1) + testURLParseError(t, err) +} + +func TestPullRequestsService_Create(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &NewPullRequest{Title: String("t")} + + mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { + v := new(NewPullRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"number":1}`) + }) + + pull, _, err := client.PullRequests.Create(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("PullRequests.Create returned error: %v", err) + } + + want := &PullRequest{Number: Int(1)} + if !reflect.DeepEqual(pull, want) { + t.Errorf("PullRequests.Create returned %+v, want %+v", pull, want) + } +} + +func TestPullRequestsService_Create_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.Create(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestPullRequestsService_Edit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + tests := []struct { + input *PullRequest + sendResponse string + + wantUpdate string + want *PullRequest + }{ + { + input: &PullRequest{Title: String("t")}, + sendResponse: `{"number":1}`, + wantUpdate: `{"title":"t"}`, + want: &PullRequest{Number: Int(1)}, + }, + { + // base update + input: &PullRequest{Base: &PullRequestBranch{Ref: String("master")}}, + sendResponse: `{"number":1,"base":{"ref":"master"}}`, + wantUpdate: `{"base":"master"}`, + want: &PullRequest{ + Number: Int(1), + Base: &PullRequestBranch{Ref: String("master")}, + }, + }, + } + + for i, tt := range tests { + madeRequest := false + mux.HandleFunc(fmt.Sprintf("/repos/o/r/pulls/%v", i), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testBody(t, r, tt.wantUpdate+"\n") + io.WriteString(w, tt.sendResponse) + madeRequest = true + }) + + pull, _, err := client.PullRequests.Edit(context.Background(), "o", "r", i, tt.input) + if err != nil { + t.Errorf("%d: PullRequests.Edit returned error: %v", i, err) + } + + if !reflect.DeepEqual(pull, tt.want) { + t.Errorf("%d: PullRequests.Edit returned %+v, want %+v", i, pull, tt.want) + } + + if !madeRequest { + t.Errorf("%d: PullRequest.Edit did not make the expected request", i) + } + } +} + +func TestPullRequestsService_Edit_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.PullRequests.Edit(context.Background(), "%", "r", 1, &PullRequest{}) + testURLParseError(t, err) +} + +func TestPullRequestsService_ListCommits(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/commits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, ` + [ + { + "sha": "3", + "parents": [ + { + "sha": "2" + } + ] + }, + { + "sha": "2", + "parents": [ + { + "sha": "1" + } + ] + } + ]`) + }) + + opt := &ListOptions{Page: 2} + commits, _, err := client.PullRequests.ListCommits(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("PullRequests.ListCommits returned error: %v", err) + } + + want := []*RepositoryCommit{ + { + SHA: String("3"), + Parents: []Commit{ + { + SHA: String("2"), + }, + }, + }, + { + SHA: String("2"), + Parents: []Commit{ + { + SHA: String("1"), + }, + }, + }, + } + if !reflect.DeepEqual(commits, want) { + t.Errorf("PullRequests.ListCommits returned %+v, want %+v", commits, want) + } +} + +func TestPullRequestsService_ListFiles(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/files", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, ` + [ + { + "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", + "filename": "file1.txt", + "status": "added", + "additions": 103, + "deletions": 21, + "changes": 124, + "patch": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test" + }, + { + "sha": "f61aebed695e2e4193db5e6dcb09b5b57875f334", + "filename": "file2.txt", + "status": "modified", + "additions": 5, + "deletions": 3, + "changes": 103, + "patch": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test" + } + ]`) + }) + + opt := &ListOptions{Page: 2} + commitFiles, _, err := client.PullRequests.ListFiles(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("PullRequests.ListFiles returned error: %v", err) + } + + want := []*CommitFile{ + { + SHA: String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), + Filename: String("file1.txt"), + Additions: Int(103), + Deletions: Int(21), + Changes: Int(124), + Status: String("added"), + Patch: String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), + }, + { + SHA: String("f61aebed695e2e4193db5e6dcb09b5b57875f334"), + Filename: String("file2.txt"), + Additions: Int(5), + Deletions: Int(3), + Changes: Int(103), + Status: String("modified"), + Patch: String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), + }, + } + + if !reflect.DeepEqual(commitFiles, want) { + t.Errorf("PullRequests.ListFiles returned %+v, want %+v", commitFiles, want) + } +} + +func TestPullRequestsService_IsMerged(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + isMerged, _, err := client.PullRequests.IsMerged(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("PullRequests.IsMerged returned error: %v", err) + } + + want := true + if !reflect.DeepEqual(isMerged, want) { + t.Errorf("PullRequests.IsMerged returned %+v, want %+v", isMerged, want) + } +} + +func TestPullRequestsService_Merge(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, ` + { + "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", + "merged": true, + "message": "Pull Request successfully merged" + }`) + }) + + options := &PullRequestOptions{MergeMethod: "rebase"} + merge, _, err := client.PullRequests.Merge(context.Background(), "o", "r", 1, "merging pull request", options) + if err != nil { + t.Errorf("PullRequests.Merge returned error: %v", err) + } + + want := &PullRequestMergeResult{ + SHA: String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), + Merged: Bool(true), + Message: String("Pull Request successfully merged"), + } + if !reflect.DeepEqual(merge, want) { + t.Errorf("PullRequests.Merge returned %+v, want %+v", merge, want) + } +} + +// Test that different merge options produce expected PUT requests. See issue https://github.com/google/go-github/issues/500. +func TestPullRequestsService_Merge_options(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + tests := []struct { + options *PullRequestOptions + wantBody string + }{ + { + options: nil, + wantBody: `{"commit_message":"merging pull request"}`, + }, + { + options: &PullRequestOptions{}, + wantBody: `{"commit_message":"merging pull request"}`, + }, + { + options: &PullRequestOptions{MergeMethod: "rebase"}, + wantBody: `{"commit_message":"merging pull request","merge_method":"rebase"}`, + }, + { + options: &PullRequestOptions{SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e"}, + wantBody: `{"commit_message":"merging pull request","sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}`, + }, + { + options: &PullRequestOptions{ + CommitTitle: "Extra detail", + SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e", + MergeMethod: "squash", + }, + wantBody: `{"commit_message":"merging pull request","commit_title":"Extra detail","merge_method":"squash","sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}`, + }, + } + + for i, test := range tests { + madeRequest := false + mux.HandleFunc(fmt.Sprintf("/repos/o/r/pulls/%d/merge", i), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testBody(t, r, test.wantBody+"\n") + madeRequest = true + }) + _, _, _ = client.PullRequests.Merge(context.Background(), "o", "r", i, "merging pull request", test.options) + if !madeRequest { + t.Errorf("%d: PullRequests.Merge(%#v): expected request was not made", i, test.options) + } + } +} diff --git a/vendor/github.com/google/go-github/github/reactions.go b/vendor/github.com/google/go-github/github/reactions.go new file mode 100644 index 00000000..b276ff3e --- /dev/null +++ b/vendor/github.com/google/go-github/github/reactions.go @@ -0,0 +1,273 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ReactionsService provides access to the reactions-related functions in the +// GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/reactions/ +type ReactionsService service + +// Reaction represents a GitHub reaction. +type Reaction struct { + // ID is the Reaction ID. + ID *int64 `json:"id,omitempty"` + User *User `json:"user,omitempty"` + // Content is the type of reaction. + // Possible values are: + // "+1", "-1", "laugh", "confused", "heart", "hooray". + Content *string `json:"content,omitempty"` +} + +// Reactions represents a summary of GitHub reactions. +type Reactions struct { + TotalCount *int `json:"total_count,omitempty"` + PlusOne *int `json:"+1,omitempty"` + MinusOne *int `json:"-1,omitempty"` + Laugh *int `json:"laugh,omitempty"` + Confused *int `json:"confused,omitempty"` + Heart *int `json:"heart,omitempty"` + Hooray *int `json:"hooray,omitempty"` + URL *string `json:"url,omitempty"` +} + +func (r Reaction) String() string { + return Stringify(r) +} + +// ListCommentReactions lists the reactions for a commit comment. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment +func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var m []*Reaction + resp, err := s.client.Do(ctx, req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// CreateCommentReaction creates a reaction for a commit comment. +// Note that if you have already created a reaction of type content, the +// previously created reaction will be returned with Status: 200 OK. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment +func (s ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) + + body := &Reaction{Content: String(content)} + req, err := s.client.NewRequest("POST", u, body) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + m := &Reaction{} + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// ListIssueReactions lists the reactions for an issue. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue +func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var m []*Reaction + resp, err := s.client.Do(ctx, req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// CreateIssueReaction creates a reaction for an issue. +// Note that if you have already created a reaction of type content, the +// previously created reaction will be returned with Status: 200 OK. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue +func (s ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo string, number int, content string) (*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) + + body := &Reaction{Content: String(content)} + req, err := s.client.NewRequest("POST", u, body) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + m := &Reaction{} + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// ListIssueCommentReactions lists the reactions for an issue comment. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment +func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var m []*Reaction + resp, err := s.client.Do(ctx, req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// CreateIssueCommentReaction creates a reaction for an issue comment. +// Note that if you have already created a reaction of type content, the +// previously created reaction will be returned with Status: 200 OK. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment +func (s ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) + + body := &Reaction{Content: String(content)} + req, err := s.client.NewRequest("POST", u, body) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + m := &Reaction{} + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// ListPullRequestCommentReactions lists the reactions for a pull request review comment. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment +func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var m []*Reaction + resp, err := s.client.Do(ctx, req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// CreatePullRequestCommentReaction creates a reaction for a pull request review comment. +// Note that if you have already created a reaction of type content, the +// previously created reaction will be returned with Status: 200 OK. +// +// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment +func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) + + body := &Reaction{Content: String(content)} + req, err := s.client.NewRequest("POST", u, body) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + m := &Reaction{} + resp, err := s.client.Do(ctx, req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// DeleteReaction deletes a reaction. +// +// GitHub API docs: https://developer.github.com/v3/reaction/reactions/#delete-a-reaction-archive +func (s *ReactionsService) DeleteReaction(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("reactions/%v", id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/reactions_test.go b/vendor/github.com/google/go-github/github/reactions_test.go new file mode 100644 index 00000000..2a43a8b8 --- /dev/null +++ b/vendor/github.com/google/go-github/github/reactions_test.go @@ -0,0 +1,201 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "net/http" + "reflect" + "testing" +) + +func TestReactionsService_ListCommentReactions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + }) + + got, _, err := client.Reactions.ListCommentReactions(context.Background(), "o", "r", 1, nil) + if err != nil { + t.Errorf("ListCommentReactions returned error: %v", err) + } + if want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { + t.Errorf("ListCommentReactions = %+v, want %+v", got, want) + } +} + +func TestReactionsService_CreateCommentReaction(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + }) + + got, _, err := client.Reactions.CreateCommentReaction(context.Background(), "o", "r", 1, "+1") + if err != nil { + t.Errorf("CreateCommentReaction returned error: %v", err) + } + want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} + if !reflect.DeepEqual(got, want) { + t.Errorf("CreateCommentReaction = %+v, want %+v", got, want) + } +} + +func TestReactionsService_ListIssueReactions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + }) + + got, _, err := client.Reactions.ListIssueReactions(context.Background(), "o", "r", 1, nil) + if err != nil { + t.Errorf("ListIssueReactions returned error: %v", err) + } + if want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { + t.Errorf("ListIssueReactions = %+v, want %+v", got, want) + } +} + +func TestReactionsService_CreateIssueReaction(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + }) + + got, _, err := client.Reactions.CreateIssueReaction(context.Background(), "o", "r", 1, "+1") + if err != nil { + t.Errorf("CreateIssueReaction returned error: %v", err) + } + want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} + if !reflect.DeepEqual(got, want) { + t.Errorf("CreateIssueReaction = %+v, want %+v", got, want) + } +} + +func TestReactionsService_ListIssueCommentReactions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + }) + + got, _, err := client.Reactions.ListIssueCommentReactions(context.Background(), "o", "r", 1, nil) + if err != nil { + t.Errorf("ListIssueCommentReactions returned error: %v", err) + } + if want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { + t.Errorf("ListIssueCommentReactions = %+v, want %+v", got, want) + } +} + +func TestReactionsService_CreateIssueCommentReaction(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + }) + + got, _, err := client.Reactions.CreateIssueCommentReaction(context.Background(), "o", "r", 1, "+1") + if err != nil { + t.Errorf("CreateIssueCommentReaction returned error: %v", err) + } + want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} + if !reflect.DeepEqual(got, want) { + t.Errorf("CreateIssueCommentReaction = %+v, want %+v", got, want) + } +} + +func TestReactionsService_ListPullRequestCommentReactions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + }) + + got, _, err := client.Reactions.ListPullRequestCommentReactions(context.Background(), "o", "r", 1, nil) + if err != nil { + t.Errorf("ListPullRequestCommentReactions returned error: %v", err) + } + if want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { + t.Errorf("ListPullRequestCommentReactions = %+v, want %+v", got, want) + } +} + +func TestReactionsService_CreatePullRequestCommentReaction(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + }) + + got, _, err := client.Reactions.CreatePullRequestCommentReaction(context.Background(), "o", "r", 1, "+1") + if err != nil { + t.Errorf("CreatePullRequestCommentReaction returned error: %v", err) + } + want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} + if !reflect.DeepEqual(got, want) { + t.Errorf("CreatePullRequestCommentReaction = %+v, want %+v", got, want) + } +} + +func TestReactionsService_DeleteReaction(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/reactions/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + if _, err := client.Reactions.DeleteReaction(context.Background(), 1); err != nil { + t.Errorf("DeleteReaction returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/repos.go b/vendor/github.com/google/go-github/github/repos.go new file mode 100644 index 00000000..68accf7f --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos.go @@ -0,0 +1,1076 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "strings" +) + +// RepositoriesService handles communication with the repository related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/repos/ +type RepositoriesService service + +// Repository represents a GitHub repository. +type Repository struct { + ID *int64 `json:"id,omitempty"` + Owner *User `json:"owner,omitempty"` + Name *string `json:"name,omitempty"` + FullName *string `json:"full_name,omitempty"` + Description *string `json:"description,omitempty"` + Homepage *string `json:"homepage,omitempty"` + CodeOfConduct *CodeOfConduct `json:"code_of_conduct,omitempty"` + DefaultBranch *string `json:"default_branch,omitempty"` + MasterBranch *string `json:"master_branch,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + PushedAt *Timestamp `json:"pushed_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CloneURL *string `json:"clone_url,omitempty"` + GitURL *string `json:"git_url,omitempty"` + MirrorURL *string `json:"mirror_url,omitempty"` + SSHURL *string `json:"ssh_url,omitempty"` + SVNURL *string `json:"svn_url,omitempty"` + Language *string `json:"language,omitempty"` + Fork *bool `json:"fork,omitempty"` + ForksCount *int `json:"forks_count,omitempty"` + NetworkCount *int `json:"network_count,omitempty"` + OpenIssuesCount *int `json:"open_issues_count,omitempty"` + StargazersCount *int `json:"stargazers_count,omitempty"` + SubscribersCount *int `json:"subscribers_count,omitempty"` + WatchersCount *int `json:"watchers_count,omitempty"` + Size *int `json:"size,omitempty"` + AutoInit *bool `json:"auto_init,omitempty"` + Parent *Repository `json:"parent,omitempty"` + Source *Repository `json:"source,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Permissions *map[string]bool `json:"permissions,omitempty"` + AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` + AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` + AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` + Topics []string `json:"topics,omitempty"` + + // Only provided when using RepositoriesService.Get while in preview + License *License `json:"license,omitempty"` + + // Additional mutable fields when creating and editing a repository + Private *bool `json:"private,omitempty"` + HasIssues *bool `json:"has_issues,omitempty"` + HasWiki *bool `json:"has_wiki,omitempty"` + HasPages *bool `json:"has_pages,omitempty"` + HasProjects *bool `json:"has_projects,omitempty"` + HasDownloads *bool `json:"has_downloads,omitempty"` + LicenseTemplate *string `json:"license_template,omitempty"` + GitignoreTemplate *string `json:"gitignore_template,omitempty"` + Archived *bool `json:"archived,omitempty"` + + // Creating an organization repository. Required for non-owners. + TeamID *int64 `json:"team_id,omitempty"` + + // API URLs + URL *string `json:"url,omitempty"` + ArchiveURL *string `json:"archive_url,omitempty"` + AssigneesURL *string `json:"assignees_url,omitempty"` + BlobsURL *string `json:"blobs_url,omitempty"` + BranchesURL *string `json:"branches_url,omitempty"` + CollaboratorsURL *string `json:"collaborators_url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` + CommitsURL *string `json:"commits_url,omitempty"` + CompareURL *string `json:"compare_url,omitempty"` + ContentsURL *string `json:"contents_url,omitempty"` + ContributorsURL *string `json:"contributors_url,omitempty"` + DeploymentsURL *string `json:"deployments_url,omitempty"` + DownloadsURL *string `json:"downloads_url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + ForksURL *string `json:"forks_url,omitempty"` + GitCommitsURL *string `json:"git_commits_url,omitempty"` + GitRefsURL *string `json:"git_refs_url,omitempty"` + GitTagsURL *string `json:"git_tags_url,omitempty"` + HooksURL *string `json:"hooks_url,omitempty"` + IssueCommentURL *string `json:"issue_comment_url,omitempty"` + IssueEventsURL *string `json:"issue_events_url,omitempty"` + IssuesURL *string `json:"issues_url,omitempty"` + KeysURL *string `json:"keys_url,omitempty"` + LabelsURL *string `json:"labels_url,omitempty"` + LanguagesURL *string `json:"languages_url,omitempty"` + MergesURL *string `json:"merges_url,omitempty"` + MilestonesURL *string `json:"milestones_url,omitempty"` + NotificationsURL *string `json:"notifications_url,omitempty"` + PullsURL *string `json:"pulls_url,omitempty"` + ReleasesURL *string `json:"releases_url,omitempty"` + StargazersURL *string `json:"stargazers_url,omitempty"` + StatusesURL *string `json:"statuses_url,omitempty"` + SubscribersURL *string `json:"subscribers_url,omitempty"` + SubscriptionURL *string `json:"subscription_url,omitempty"` + TagsURL *string `json:"tags_url,omitempty"` + TreesURL *string `json:"trees_url,omitempty"` + TeamsURL *string `json:"teams_url,omitempty"` + + // TextMatches is only populated from search results that request text matches + // See: search.go and https://developer.github.com/v3/search/#text-match-metadata + TextMatches []TextMatch `json:"text_matches,omitempty"` +} + +func (r Repository) String() string { + return Stringify(r) +} + +// RepositoryListOptions specifies the optional parameters to the +// RepositoriesService.List method. +type RepositoryListOptions struct { + // Visibility of repositories to list. Can be one of all, public, or private. + // Default: all + Visibility string `url:"visibility,omitempty"` + + // List repos of given affiliation[s]. + // Comma-separated list of values. Can include: + // * owner: Repositories that are owned by the authenticated user. + // * collaborator: Repositories that the user has been added to as a + // collaborator. + // * organization_member: Repositories that the user has access to through + // being a member of an organization. This includes every repository on + // every team that the user is on. + // Default: owner,collaborator,organization_member + Affiliation string `url:"affiliation,omitempty"` + + // Type of repositories to list. + // Can be one of all, owner, public, private, member. Default: all + // Will cause a 422 error if used in the same request as visibility or + // affiliation. + Type string `url:"type,omitempty"` + + // How to sort the repository list. Can be one of created, updated, pushed, + // full_name. Default: full_name + Sort string `url:"sort,omitempty"` + + // Direction in which to sort repositories. Can be one of asc or desc. + // Default: when using full_name: asc; otherwise desc + Direction string `url:"direction,omitempty"` + + ListOptions +} + +// List the repositories for a user. Passing the empty string will list +// repositories for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-user-repositories +func (s *RepositoriesService) List(ctx context.Context, user string, opt *RepositoryListOptions) ([]*Repository, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/repos", user) + } else { + u = "user/repos" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + var repos []*Repository + resp, err := s.client.Do(ctx, req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// RepositoryListByOrgOptions specifies the optional parameters to the +// RepositoriesService.ListByOrg method. +type RepositoryListByOrgOptions struct { + // Type of repositories to list. Possible values are: all, public, private, + // forks, sources, member. Default is "all". + Type string `url:"type,omitempty"` + + ListOptions +} + +// ListByOrg lists the repositories for an organization. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-organization-repositories +func (s *RepositoriesService) ListByOrg(ctx context.Context, org string, opt *RepositoryListByOrgOptions) ([]*Repository, *Response, error) { + u := fmt.Sprintf("orgs/%v/repos", org) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + var repos []*Repository + resp, err := s.client.Do(ctx, req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// RepositoryListAllOptions specifies the optional parameters to the +// RepositoriesService.ListAll method. +type RepositoryListAllOptions struct { + // ID of the last repository seen + Since int64 `url:"since,omitempty"` +} + +// ListAll lists all GitHub repositories in the order that they were created. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-all-public-repositories +func (s *RepositoriesService) ListAll(ctx context.Context, opt *RepositoryListAllOptions) ([]*Repository, *Response, error) { + u, err := addOptions("repositories", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var repos []*Repository + resp, err := s.client.Do(ctx, req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// Create a new repository. If an organization is specified, the new +// repository will be created under that org. If the empty string is +// specified, it will be created for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/repos/#create +func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repository) (*Repository, *Response, error) { + var u string + if org != "" { + u = fmt.Sprintf("orgs/%v/repos", org) + } else { + u = "user/repos" + } + + req, err := s.client.NewRequest("POST", u, repo) + if err != nil { + return nil, nil, err + } + + r := new(Repository) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// Get fetches a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#get +func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when the license support fully launches + // https://developer.github.com/v3/licenses/#get-a-repositorys-license + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + repository := new(Repository) + resp, err := s.client.Do(ctx, req, repository) + if err != nil { + return nil, resp, err + } + + return repository, resp, nil +} + +// GetCodeOfConduct gets the contents of a repository's code of conduct. +// +// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#get-the-contents-of-a-repositorys-code-of-conduct +func (s *RepositoriesService) GetCodeOfConduct(ctx context.Context, owner, repo string) (*CodeOfConduct, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/community/code_of_conduct", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeCodesOfConductPreview) + + coc := new(CodeOfConduct) + resp, err := s.client.Do(ctx, req, coc) + if err != nil { + return nil, resp, err + } + + return coc, resp, nil +} + +// GetByID fetches a repository. +// +// Note: GetByID uses the undocumented GitHub API endpoint /repositories/:id. +func (s *RepositoriesService) GetByID(ctx context.Context, id int64) (*Repository, *Response, error) { + u := fmt.Sprintf("repositories/%d", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when the license support fully launches + // https://developer.github.com/v3/licenses/#get-a-repositorys-license + req.Header.Set("Accept", mediaTypeLicensesPreview) + + repository := new(Repository) + resp, err := s.client.Do(ctx, req, repository) + if err != nil { + return nil, resp, err + } + + return repository, resp, nil +} + +// Edit updates a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#edit +func (s *RepositoriesService) Edit(ctx context.Context, owner, repo string, repository *Repository) (*Repository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v", owner, repo) + req, err := s.client.NewRequest("PATCH", u, repository) + if err != nil { + return nil, nil, err + } + + r := new(Repository) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// Delete a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#delete-a-repository +func (s *RepositoriesService) Delete(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v", owner, repo) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Contributor represents a repository contributor +type Contributor struct { + Login *string `json:"login,omitempty"` + ID *int64 `json:"id,omitempty"` + AvatarURL *string `json:"avatar_url,omitempty"` + GravatarID *string `json:"gravatar_id,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + FollowersURL *string `json:"followers_url,omitempty"` + FollowingURL *string `json:"following_url,omitempty"` + GistsURL *string `json:"gists_url,omitempty"` + StarredURL *string `json:"starred_url,omitempty"` + SubscriptionsURL *string `json:"subscriptions_url,omitempty"` + OrganizationsURL *string `json:"organizations_url,omitempty"` + ReposURL *string `json:"repos_url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + ReceivedEventsURL *string `json:"received_events_url,omitempty"` + Type *string `json:"type,omitempty"` + SiteAdmin *bool `json:"site_admin,omitempty"` + Contributions *int `json:"contributions,omitempty"` +} + +// ListContributorsOptions specifies the optional parameters to the +// RepositoriesService.ListContributors method. +type ListContributorsOptions struct { + // Include anonymous contributors in results or not + Anon string `url:"anon,omitempty"` + + ListOptions +} + +// ListContributors lists contributors for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-contributors +func (s *RepositoriesService) ListContributors(ctx context.Context, owner string, repository string, opt *ListContributorsOptions) ([]*Contributor, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/contributors", owner, repository) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var contributor []*Contributor + resp, err := s.client.Do(ctx, req, &contributor) + if err != nil { + return nil, nil, err + } + + return contributor, resp, nil +} + +// ListLanguages lists languages for the specified repository. The returned map +// specifies the languages and the number of bytes of code written in that +// language. For example: +// +// { +// "C": 78769, +// "Python": 7769 +// } +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-languages +func (s *RepositoriesService) ListLanguages(ctx context.Context, owner string, repo string) (map[string]int, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/languages", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + languages := make(map[string]int) + resp, err := s.client.Do(ctx, req, &languages) + if err != nil { + return nil, resp, err + } + + return languages, resp, nil +} + +// ListTeams lists the teams for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-teams +func (s *RepositoriesService) ListTeams(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/teams", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + var teams []*Team + resp, err := s.client.Do(ctx, req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// RepositoryTag represents a repository tag. +type RepositoryTag struct { + Name *string `json:"name,omitempty"` + Commit *Commit `json:"commit,omitempty"` + ZipballURL *string `json:"zipball_url,omitempty"` + TarballURL *string `json:"tarball_url,omitempty"` +} + +// ListTags lists tags for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-tags +func (s *RepositoriesService) ListTags(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*RepositoryTag, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var tags []*RepositoryTag + resp, err := s.client.Do(ctx, req, &tags) + if err != nil { + return nil, resp, err + } + + return tags, resp, nil +} + +// Branch represents a repository branch +type Branch struct { + Name *string `json:"name,omitempty"` + Commit *RepositoryCommit `json:"commit,omitempty"` + Protected *bool `json:"protected,omitempty"` +} + +// Protection represents a repository branch's protection. +type Protection struct { + RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"` + RequiredPullRequestReviews *PullRequestReviewsEnforcement `json:"required_pull_request_reviews"` + EnforceAdmins *AdminEnforcement `json:"enforce_admins"` + Restrictions *BranchRestrictions `json:"restrictions"` +} + +// ProtectionRequest represents a request to create/edit a branch's protection. +type ProtectionRequest struct { + RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"` + RequiredPullRequestReviews *PullRequestReviewsEnforcementRequest `json:"required_pull_request_reviews"` + EnforceAdmins bool `json:"enforce_admins"` + Restrictions *BranchRestrictionsRequest `json:"restrictions"` +} + +// RequiredStatusChecks represents the protection status of a individual branch. +type RequiredStatusChecks struct { + // Require branches to be up to date before merging. (Required.) + Strict bool `json:"strict"` + // The list of status checks to require in order to merge into this + // branch. (Required; use []string{} instead of nil for empty list.) + Contexts []string `json:"contexts"` +} + +// PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch. +type PullRequestReviewsEnforcement struct { + // Specifies which users and teams can dismiss pull request reviews. + DismissalRestrictions DismissalRestrictions `json:"dismissal_restrictions"` + // Specifies if approved reviews are dismissed automatically, when a new commit is pushed. + DismissStaleReviews bool `json:"dismiss_stale_reviews"` + // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. + RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"` +} + +// PullRequestReviewsEnforcementRequest represents request to set the pull request review +// enforcement of a protected branch. It is separate from PullRequestReviewsEnforcement above +// because the request structure is different from the response structure. +type PullRequestReviewsEnforcementRequest struct { + // Specifies which users and teams should be allowed to dismiss pull request reviews. Can be nil to disable the restrictions. + DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions"` + // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. (Required) + DismissStaleReviews bool `json:"dismiss_stale_reviews"` + // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. + RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"` +} + +// MarshalJSON implements the json.Marshaler interface. +// Converts nil value of PullRequestReviewsEnforcementRequest.DismissalRestrictionsRequest to empty array +func (req PullRequestReviewsEnforcementRequest) MarshalJSON() ([]byte, error) { + if req.DismissalRestrictionsRequest == nil { + newReq := struct { + R []interface{} `json:"dismissal_restrictions"` + D bool `json:"dismiss_stale_reviews"` + O bool `json:"require_code_owner_reviews"` + }{ + R: []interface{}{}, + D: req.DismissStaleReviews, + O: req.RequireCodeOwnerReviews, + } + return json.Marshal(newReq) + } + newReq := struct { + R *DismissalRestrictionsRequest `json:"dismissal_restrictions"` + D bool `json:"dismiss_stale_reviews"` + O bool `json:"require_code_owner_reviews"` + }{ + R: req.DismissalRestrictionsRequest, + D: req.DismissStaleReviews, + O: req.RequireCodeOwnerReviews, + } + return json.Marshal(newReq) +} + +// PullRequestReviewsEnforcementUpdate represents request to patch the pull request review +// enforcement of a protected branch. It is separate from PullRequestReviewsEnforcementRequest above +// because the patch request does not require all fields to be initialized. +type PullRequestReviewsEnforcementUpdate struct { + // Specifies which users and teams can dismiss pull request reviews. Can be omitted. + DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"` + // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. Can be omitted. + DismissStaleReviews *bool `json:"dismiss_stale_reviews,omitempty"` + // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. + RequireCodeOwnerReviews bool `json:"require_code_owner_reviews,omitempty"` +} + +// AdminEnforcement represents the configuration to enforce required status checks for repository administrators. +type AdminEnforcement struct { + URL *string `json:"url,omitempty"` + Enabled bool `json:"enabled"` +} + +// BranchRestrictions represents the restriction that only certain users or +// teams may push to a branch. +type BranchRestrictions struct { + // The list of user logins with push access. + Users []*User `json:"users"` + // The list of team slugs with push access. + Teams []*Team `json:"teams"` +} + +// BranchRestrictionsRequest represents the request to create/edit the +// restriction that only certain users or teams may push to a branch. It is +// separate from BranchRestrictions above because the request structure is +// different from the response structure. +type BranchRestrictionsRequest struct { + // The list of user logins with push access. (Required; use []string{} instead of nil for empty list.) + Users []string `json:"users"` + // The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.) + Teams []string `json:"teams"` +} + +// DismissalRestrictions specifies which users and teams can dismiss pull request reviews. +type DismissalRestrictions struct { + // The list of users who can dimiss pull request reviews. + Users []*User `json:"users"` + // The list of teams which can dismiss pull request reviews. + Teams []*Team `json:"teams"` +} + +// DismissalRestrictionsRequest represents the request to create/edit the +// restriction to allows only specific users or teams to dimiss pull request reviews. It is +// separate from DismissalRestrictions above because the request structure is +// different from the response structure. +type DismissalRestrictionsRequest struct { + // The list of user logins who can dismiss pull request reviews. (Required; use []string{} instead of nil for empty list.) + Users []string `json:"users"` + // The list of team slugs which can dismiss pull request reviews. (Required; use []string{} instead of nil for empty list.) + Teams []string `json:"teams"` +} + +// ListBranches lists branches for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-branches +func (s *RepositoriesService) ListBranches(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Branch, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + var branches []*Branch + resp, err := s.client.Do(ctx, req, &branches) + if err != nil { + return nil, resp, err + } + + return branches, resp, nil +} + +// GetBranch gets the specified branch for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#get-branch +func (s *RepositoriesService) GetBranch(ctx context.Context, owner, repo, branch string) (*Branch, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + b := new(Branch) + resp, err := s.client.Do(ctx, req, b) + if err != nil { + return nil, resp, err + } + + return b, resp, nil +} + +// GetBranchProtection gets the protection of a given branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-branch-protection +func (s *RepositoriesService) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*Protection, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + p := new(Protection) + resp, err := s.client.Do(ctx, req, p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// GetRequiredStatusChecks gets the required status checks for a given protected branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-required-status-checks-of-protected-branch +func (s *RepositoriesService) GetRequiredStatusChecks(ctx context.Context, owner, repo, branch string) (*RequiredStatusChecks, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + p := new(RequiredStatusChecks) + resp, err := s.client.Do(ctx, req, p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// ListRequiredStatusChecksContexts lists the required status checks contexts for a given protected branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#list-required-status-checks-contexts-of-protected-branch +func (s *RepositoriesService) ListRequiredStatusChecksContexts(ctx context.Context, owner, repo, branch string) (contexts []string, resp *Response, err error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks/contexts", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + resp, err = s.client.Do(ctx, req, &contexts) + if err != nil { + return nil, resp, err + } + + return contexts, resp, nil +} + +// UpdateBranchProtection updates the protection of a given branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-branch-protection +func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *ProtectionRequest) (*Protection, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) + req, err := s.client.NewRequest("PUT", u, preq) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + p := new(Protection) + resp, err := s.client.Do(ctx, req, p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// RemoveBranchProtection removes the protection of a given branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-branch-protection +func (s *RepositoriesService) RemoveBranchProtection(ctx context.Context, owner, repo, branch string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + return s.client.Do(ctx, req, nil) +} + +// License gets the contents of a repository's license if one is detected. +// +// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license +func (s *RepositoriesService) License(ctx context.Context, owner, repo string) (*RepositoryLicense, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/license", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + r := &RepositoryLicense{} + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// GetPullRequestReviewEnforcement gets pull request review enforcement of a protected branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-pull-request-review-enforcement-of-protected-branch +func (s *RepositoriesService) GetPullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + r := new(PullRequestReviewsEnforcement) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// UpdatePullRequestReviewEnforcement patches pull request review enforcement of a protected branch. +// It requires admin access and branch protection to be enabled. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch +func (s *RepositoriesService) UpdatePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string, patch *PullRequestReviewsEnforcementUpdate) (*PullRequestReviewsEnforcement, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) + req, err := s.client.NewRequest("PATCH", u, patch) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + r := new(PullRequestReviewsEnforcement) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, err +} + +// DisableDismissalRestrictions disables dismissal restrictions of a protected branch. +// It requires admin access and branch protection to be enabled. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch +func (s *RepositoriesService) DisableDismissalRestrictions(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) + + data := struct { + R []interface{} `json:"dismissal_restrictions"` + }{[]interface{}{}} + + req, err := s.client.NewRequest("PATCH", u, data) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + r := new(PullRequestReviewsEnforcement) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, err +} + +// RemovePullRequestReviewEnforcement removes pull request enforcement of a protected branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-pull-request-review-enforcement-of-protected-branch +func (s *RepositoriesService) RemovePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + return s.client.Do(ctx, req, nil) +} + +// GetAdminEnforcement gets admin enforcement information of a protected branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-admin-enforcement-of-protected-branch +func (s *RepositoriesService) GetAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + r := new(AdminEnforcement) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// AddAdminEnforcement adds admin enforcement to a protected branch. +// It requires admin access and branch protection to be enabled. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-admin-enforcement-of-protected-branch +func (s *RepositoriesService) AddAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + r := new(AdminEnforcement) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, err +} + +// RemoveAdminEnforcement removes admin enforcement from a protected branch. +// +// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-admin-enforcement-of-protected-branch +func (s *RepositoriesService) RemoveAdminEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + return s.client.Do(ctx, req, nil) +} + +// repositoryTopics represents a collection of repository topics. +type repositoryTopics struct { + Names []string `json:"names"` +} + +// ListAllTopics lists topics for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#list-all-topics-for-a-repository +func (s *RepositoriesService) ListAllTopics(ctx context.Context, owner, repo string) ([]string, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/topics", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeTopicsPreview) + + topics := new(repositoryTopics) + resp, err := s.client.Do(ctx, req, topics) + if err != nil { + return nil, resp, err + } + + return topics.Names, resp, nil +} + +// ReplaceAllTopics replaces topics for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/#replace-all-topics-for-a-repository +func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo string, topics []string) ([]string, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/topics", owner, repo) + t := &repositoryTopics{ + Names: topics, + } + if t.Names == nil { + t.Names = []string{} + } + req, err := s.client.NewRequest("PUT", u, t) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeTopicsPreview) + + t = new(repositoryTopics) + resp, err := s.client.Do(ctx, req, t) + if err != nil { + return nil, resp, err + } + + return t.Names, resp, nil +} + +// TransferRequest represents a request to transfer a repository. +type TransferRequest struct { + NewOwner string `json:"new_owner"` + TeamID []int64 `json:"team_id,omitempty"` +} + +// Transfer transfers a repository from one account or organization to another. +// +// This method might return an *AcceptedError and a status code of +// 202. This is because this is the status that GitHub returns to signify that +// it has now scheduled the transfer of the repository in a background task. +// A follow up request, after a delay of a second or so, should result +// in a successful request. +// +// GitHub API docs: https://developer.github.com/v3/repos/#transfer-a-repository +func (s *RepositoriesService) Transfer(ctx context.Context, owner, repo string, transfer TransferRequest) (*Repository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/transfer", owner, repo) + + req, err := s.client.NewRequest("POST", u, &transfer) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryTransferPreview) + + r := new(Repository) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_collaborators.go b/vendor/github.com/google/go-github/github/repos_collaborators.go new file mode 100644 index 00000000..61ee9d39 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_collaborators.go @@ -0,0 +1,140 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListCollaboratorsOptions specifies the optional parameters to the +// RepositoriesService.ListCollaborators method. +type ListCollaboratorsOptions struct { + // Affiliation specifies how collaborators should be filtered by their affiliation. + // Possible values are: + // outside - All outside collaborators of an organization-owned repository + // direct - All collaborators with permissions to an organization-owned repository, + // regardless of organization membership status + // all - All collaborators the authenticated user can see + // + // Default value is "all". + Affiliation string `url:"affiliation,omitempty"` + + ListOptions +} + +// ListCollaborators lists the GitHub users that have access to the repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#list-collaborators +func (s *RepositoriesService) ListCollaborators(ctx context.Context, owner, repo string, opt *ListCollaboratorsOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/collaborators", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + + var users []*User + resp, err := s.client.Do(ctx, req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// IsCollaborator checks whether the specified GitHub user has collaborator +// access to the given repo. +// Note: This will return false if the user is not a collaborator OR the user +// is not a GitHub user. +// +// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#get +func (s *RepositoriesService) IsCollaborator(ctx context.Context, owner, repo, user string) (bool, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + isCollab, err := parseBoolResponse(err) + return isCollab, resp, err +} + +// RepositoryPermissionLevel represents the permission level an organization +// member has for a given repository. +type RepositoryPermissionLevel struct { + // Possible values: "admin", "write", "read", "none" + Permission *string `json:"permission,omitempty"` + + User *User `json:"user,omitempty"` +} + +// GetPermissionLevel retrieves the specific permission level a collaborator has for a given repository. +// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level +func (s *RepositoriesService) GetPermissionLevel(ctx context.Context, owner, repo, user string) (*RepositoryPermissionLevel, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/collaborators/%v/permission", owner, repo, user) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + rpl := new(RepositoryPermissionLevel) + resp, err := s.client.Do(ctx, req, rpl) + if err != nil { + return nil, resp, err + } + return rpl, resp, nil +} + +// RepositoryAddCollaboratorOptions specifies the optional parameters to the +// RepositoriesService.AddCollaborator method. +type RepositoryAddCollaboratorOptions struct { + // Permission specifies the permission to grant the user on this repository. + // Possible values are: + // pull - team members can pull, but not push to or administer this repository + // push - team members can pull and push, but not administer this repository + // admin - team members can pull, push and administer this repository + // + // Default value is "push". This option is only valid for organization-owned repositories. + Permission string `json:"permission,omitempty"` +} + +// AddCollaborator sends an invitation to the specified GitHub user +// to become a collaborator to the given repo. +// +// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator +func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) + req, err := s.client.NewRequest("PUT", u, opt) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) + + return s.client.Do(ctx, req, nil) +} + +// RemoveCollaborator removes the specified GitHub user as collaborator from the given repo. +// Note: Does not return error if a valid user that is not a collaborator is removed. +// +// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#remove-collaborator +func (s *RepositoriesService) RemoveCollaborator(ctx context.Context, owner, repo, user string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/repos_collaborators_test.go b/vendor/github.com/google/go-github/github/repos_collaborators_test.go new file mode 100644 index 00000000..c814a5f9 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_collaborators_test.go @@ -0,0 +1,201 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListCollaborators(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/collaborators", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListCollaboratorsOptions{ + ListOptions: ListOptions{Page: 2}, + } + users, _, err := client.Repositories.ListCollaborators(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListCollaborators returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(users, want) { + t.Errorf("Repositori es.ListCollaborators returned %+v, want %+v", users, want) + } +} + +func TestRepositoriesService_ListCollaborators_withAffiliation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/collaborators", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"affiliation": "all", "page": "2"}) + fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListCollaboratorsOptions{ + ListOptions: ListOptions{Page: 2}, + Affiliation: "all", + } + users, _, err := client.Repositories.ListCollaborators(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListCollaborators returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(users, want) { + t.Errorf("Repositories.ListCollaborators returned %+v, want %+v", users, want) + } +} + +func TestRepositoriesService_ListCollaborators_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListCollaborators(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_IsCollaborator_True(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + isCollab, _, err := client.Repositories.IsCollaborator(context.Background(), "o", "r", "u") + if err != nil { + t.Errorf("Repositories.IsCollaborator returned error: %v", err) + } + + if !isCollab { + t.Errorf("Repositories.IsCollaborator returned false, want true") + } +} + +func TestRepositoriesService_IsCollaborator_False(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + isCollab, _, err := client.Repositories.IsCollaborator(context.Background(), "o", "r", "u") + if err != nil { + t.Errorf("Repositories.IsCollaborator returned error: %v", err) + } + + if isCollab { + t.Errorf("Repositories.IsCollaborator returned true, want false") + } +} + +func TestRepositoriesService_IsCollaborator_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.IsCollaborator(context.Background(), "%", "%", "%") + testURLParseError(t, err) +} + +func TestRepositoryService_GetPermissionLevel(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/collaborators/u/permission", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{"permission":"admin","user":{"login":"u"}}`) + }) + + rpl, _, err := client.Repositories.GetPermissionLevel(context.Background(), "o", "r", "u") + if err != nil { + t.Errorf("Repositories.GetPermissionLevel returned error: %v", err) + } + + want := &RepositoryPermissionLevel{ + Permission: String("admin"), + User: &User{ + Login: String("u"), + }, + } + + if !reflect.DeepEqual(rpl, want) { + t.Errorf("Repositories.GetPermissionLevel returned %+v, want %+v", rpl, want) + } +} + +func TestRepositoriesService_AddCollaborator(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + opt := &RepositoryAddCollaboratorOptions{Permission: "admin"} + + mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { + v := new(RepositoryAddCollaboratorOptions) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + if !reflect.DeepEqual(v, opt) { + t.Errorf("Request body = %+v, want %+v", v, opt) + } + + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Repositories.AddCollaborator(context.Background(), "o", "r", "u", opt) + if err != nil { + t.Errorf("Repositories.AddCollaborator returned error: %v", err) + } +} + +func TestRepositoriesService_AddCollaborator_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Repositories.AddCollaborator(context.Background(), "%", "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_RemoveCollaborator(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Repositories.RemoveCollaborator(context.Background(), "o", "r", "u") + if err != nil { + t.Errorf("Repositories.RemoveCollaborator returned error: %v", err) + } +} + +func TestRepositoriesService_RemoveCollaborator_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Repositories.RemoveCollaborator(context.Background(), "%", "%", "%") + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/repos_comments.go b/vendor/github.com/google/go-github/github/repos_comments.go new file mode 100644 index 00000000..fa2377d4 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_comments.go @@ -0,0 +1,161 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// RepositoryComment represents a comment for a commit, file, or line in a repository. +type RepositoryComment struct { + HTMLURL *string `json:"html_url,omitempty"` + URL *string `json:"url,omitempty"` + ID *int64 `json:"id,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + User *User `json:"user,omitempty"` + Reactions *Reactions `json:"reactions,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + + // User-mutable fields + Body *string `json:"body"` + // User-initialized fields + Path *string `json:"path,omitempty"` + Position *int `json:"position,omitempty"` +} + +func (r RepositoryComment) String() string { + return Stringify(r) +} + +// ListComments lists all the comments for the repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository +func (s *RepositoriesService) ListComments(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/comments", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var comments []*RepositoryComment + resp, err := s.client.Do(ctx, req, &comments) + if err != nil { + return nil, resp, err + } + + return comments, resp, nil +} + +// ListCommitComments lists all the comments for a given commit SHA. +// +// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit +func (s *RepositoriesService) ListCommitComments(ctx context.Context, owner, repo, sha string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var comments []*RepositoryComment + resp, err := s.client.Do(ctx, req, &comments) + if err != nil { + return nil, resp, err + } + + return comments, resp, nil +} + +// CreateComment creates a comment for the given commit. +// Note: GitHub allows for comments to be created for non-existing files and positions. +// +// GitHub API docs: https://developer.github.com/v3/repos/comments/#create-a-commit-comment +func (s *RepositoriesService) CreateComment(ctx context.Context, owner, repo, sha string, comment *RepositoryComment) (*RepositoryComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) + req, err := s.client.NewRequest("POST", u, comment) + if err != nil { + return nil, nil, err + } + + c := new(RepositoryComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// GetComment gets a single comment from a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment +func (s *RepositoriesService) GetComment(ctx context.Context, owner, repo string, id int64) (*RepositoryComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeReactionsPreview) + + c := new(RepositoryComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// UpdateComment updates the body of a single comment. +// +// GitHub API docs: https://developer.github.com/v3/repos/comments/#update-a-commit-comment +func (s *RepositoriesService) UpdateComment(ctx context.Context, owner, repo string, id int64, comment *RepositoryComment) (*RepositoryComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) + req, err := s.client.NewRequest("PATCH", u, comment) + if err != nil { + return nil, nil, err + } + + c := new(RepositoryComment) + resp, err := s.client.Do(ctx, req, c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} + +// DeleteComment deletes a single comment from a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/comments/#delete-a-commit-comment +func (s *RepositoriesService) DeleteComment(ctx context.Context, owner, repo string, id int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/repos_comments_test.go b/vendor/github.com/google/go-github/github/repos_comments_test.go new file mode 100644 index 00000000..0eb54eba --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_comments_test.go @@ -0,0 +1,202 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListComments(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + comments, _, err := client.Repositories.ListComments(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListComments returned error: %v", err) + } + + want := []*RepositoryComment{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(comments, want) { + t.Errorf("Repositories.ListComments returned %+v, want %+v", comments, want) + } +} + +func TestRepositoriesService_ListComments_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListComments(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_ListCommitComments(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/commits/s/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + comments, _, err := client.Repositories.ListCommitComments(context.Background(), "o", "r", "s", opt) + if err != nil { + t.Errorf("Repositories.ListCommitComments returned error: %v", err) + } + + want := []*RepositoryComment{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(comments, want) { + t.Errorf("Repositories.ListCommitComments returned %+v, want %+v", comments, want) + } +} + +func TestRepositoriesService_ListCommitComments_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListCommitComments(context.Background(), "%", "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_CreateComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &RepositoryComment{Body: String("b")} + + mux.HandleFunc("/repos/o/r/commits/s/comments", func(w http.ResponseWriter, r *http.Request) { + v := new(RepositoryComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Repositories.CreateComment(context.Background(), "o", "r", "s", input) + if err != nil { + t.Errorf("Repositories.CreateComment returned error: %v", err) + } + + want := &RepositoryComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Repositories.CreateComment returned %+v, want %+v", comment, want) + } +} + +func TestRepositoriesService_CreateComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.CreateComment(context.Background(), "%", "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_GetComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Repositories.GetComment(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetComment returned error: %v", err) + } + + want := &RepositoryComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Repositories.GetComment returned %+v, want %+v", comment, want) + } +} + +func TestRepositoriesService_GetComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.GetComment(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} + +func TestRepositoriesService_UpdateComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &RepositoryComment{Body: String("b")} + + mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { + v := new(RepositoryComment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + comment, _, err := client.Repositories.UpdateComment(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Repositories.UpdateComment returned error: %v", err) + } + + want := &RepositoryComment{ID: Int64(1)} + if !reflect.DeepEqual(comment, want) { + t.Errorf("Repositories.UpdateComment returned %+v, want %+v", comment, want) + } +} + +func TestRepositoriesService_UpdateComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.UpdateComment(context.Background(), "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_DeleteComment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Repositories.DeleteComment(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteComment returned error: %v", err) + } +} + +func TestRepositoriesService_DeleteComment_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Repositories.DeleteComment(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/repos_commits.go b/vendor/github.com/google/go-github/github/repos_commits.go new file mode 100644 index 00000000..04847373 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_commits.go @@ -0,0 +1,237 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "fmt" + "time" +) + +// RepositoryCommit represents a commit in a repo. +// Note that it's wrapping a Commit, so author/committer information is in two places, +// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details". +type RepositoryCommit struct { + SHA *string `json:"sha,omitempty"` + Commit *Commit `json:"commit,omitempty"` + Author *User `json:"author,omitempty"` + Committer *User `json:"committer,omitempty"` + Parents []Commit `json:"parents,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + URL *string `json:"url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` + + // Details about how many changes were made in this commit. Only filled in during GetCommit! + Stats *CommitStats `json:"stats,omitempty"` + // Details about which files, and how this commit touched. Only filled in during GetCommit! + Files []CommitFile `json:"files,omitempty"` +} + +func (r RepositoryCommit) String() string { + return Stringify(r) +} + +// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit. +type CommitStats struct { + Additions *int `json:"additions,omitempty"` + Deletions *int `json:"deletions,omitempty"` + Total *int `json:"total,omitempty"` +} + +func (c CommitStats) String() string { + return Stringify(c) +} + +// CommitFile represents a file modified in a commit. +type CommitFile struct { + SHA *string `json:"sha,omitempty"` + Filename *string `json:"filename,omitempty"` + Additions *int `json:"additions,omitempty"` + Deletions *int `json:"deletions,omitempty"` + Changes *int `json:"changes,omitempty"` + Status *string `json:"status,omitempty"` + Patch *string `json:"patch,omitempty"` + BlobURL *string `json:"blob_url,omitempty"` + RawURL *string `json:"raw_url,omitempty"` + ContentsURL *string `json:"contents_url,omitempty"` +} + +func (c CommitFile) String() string { + return Stringify(c) +} + +// CommitsComparison is the result of comparing two commits. +// See CompareCommits() for details. +type CommitsComparison struct { + BaseCommit *RepositoryCommit `json:"base_commit,omitempty"` + MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"` + + // Head can be 'behind' or 'ahead' + Status *string `json:"status,omitempty"` + AheadBy *int `json:"ahead_by,omitempty"` + BehindBy *int `json:"behind_by,omitempty"` + TotalCommits *int `json:"total_commits,omitempty"` + + Commits []RepositoryCommit `json:"commits,omitempty"` + + Files []CommitFile `json:"files,omitempty"` + + HTMLURL *string `json:"html_url,omitempty"` + PermalinkURL *string `json:"permalink_url,omitempty"` + DiffURL *string `json:"diff_url,omitempty"` + PatchURL *string `json:"patch_url,omitempty"` + URL *string `json:"url,omitempty"` // API URL. +} + +func (c CommitsComparison) String() string { + return Stringify(c) +} + +// CommitsListOptions specifies the optional parameters to the +// RepositoriesService.ListCommits method. +type CommitsListOptions struct { + // SHA or branch to start listing Commits from. + SHA string `url:"sha,omitempty"` + + // Path that should be touched by the returned Commits. + Path string `url:"path,omitempty"` + + // Author of by which to filter Commits. + Author string `url:"author,omitempty"` + + // Since when should Commits be included in the response. + Since time.Time `url:"since,omitempty"` + + // Until when should Commits be included in the response. + Until time.Time `url:"until,omitempty"` + + ListOptions +} + +// ListCommits lists the commits of a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/commits/#list +func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGitSigningPreview) + + var commits []*RepositoryCommit + resp, err := s.client.Do(ctx, req, &commits) + if err != nil { + return nil, resp, err + } + + return commits, resp, nil +} + +// GetCommit fetches the specified commit, including all details about it. +// +// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-a-single-commit +// See also: https://developer.github.com/v3/git/commits/#get-a-single-commit provides the same functionality +func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGitSigningPreview) + + commit := new(RepositoryCommit) + resp, err := s.client.Do(ctx, req, commit) + if err != nil { + return nil, resp, err + } + + return commit, resp, nil +} + +// GetCommitRaw fetches the specified commit in raw (diff or patch) format. +func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, repo string, sha string, opt RawOptions) (string, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return "", nil, err + } + + switch opt.Type { + case Diff: + req.Header.Set("Accept", mediaTypeV3Diff) + case Patch: + req.Header.Set("Accept", mediaTypeV3Patch) + default: + return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) + } + + var buf bytes.Buffer + resp, err := s.client.Do(ctx, req, &buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is +// supplied and no new commits have occurred, a 304 Unmodified response is returned. +// +// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference +func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, ref) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return "", nil, err + } + if lastSHA != "" { + req.Header.Set("If-None-Match", `"`+lastSHA+`"`) + } + + req.Header.Set("Accept", mediaTypeV3SHA) + + var buf bytes.Buffer + resp, err := s.client.Do(ctx, req, &buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// CompareCommits compares a range of commits with each other. +// todo: support media formats - https://github.com/google/go-github/issues/6 +// +// GitHub API docs: https://developer.github.com/v3/repos/commits/index.html#compare-two-commits +func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + comp := new(CommitsComparison) + resp, err := s.client.Do(ctx, req, comp) + if err != nil { + return nil, resp, err + } + + return comp, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_commits_test.go b/vendor/github.com/google/go-github/github/repos_commits_test.go new file mode 100644 index 00000000..3f7039da --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_commits_test.go @@ -0,0 +1,325 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "strings" + "testing" + "time" +) + +func TestRepositoriesService_ListCommits(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + // given + mux.HandleFunc("/repos/o/r/commits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + testFormValues(t, r, + values{ + "sha": "s", + "path": "p", + "author": "a", + "since": "2013-08-01T00:00:00Z", + "until": "2013-09-03T00:00:00Z", + }) + fmt.Fprintf(w, `[{"sha": "s"}]`) + }) + + opt := &CommitsListOptions{ + SHA: "s", + Path: "p", + Author: "a", + Since: time.Date(2013, time.August, 1, 0, 0, 0, 0, time.UTC), + Until: time.Date(2013, time.September, 3, 0, 0, 0, 0, time.UTC), + } + commits, _, err := client.Repositories.ListCommits(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListCommits returned error: %v", err) + } + + want := []*RepositoryCommit{{SHA: String("s")}} + if !reflect.DeepEqual(commits, want) { + t.Errorf("Repositories.ListCommits returned %+v, want %+v", commits, want) + } +} + +func TestRepositoriesService_GetCommit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + fmt.Fprintf(w, `{ + "sha": "s", + "commit": { "message": "m" }, + "author": { "login": "l" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ], + "stats": { "additions": 104, "deletions": 4, "total": 108 }, + "files": [ + { + "filename": "f", + "additions": 10, + "deletions": 2, + "changes": 12, + "status": "s", + "patch": "p", + "blob_url": "b", + "raw_url": "r", + "contents_url": "c" + } + ] + }`) + }) + + commit, _, err := client.Repositories.GetCommit(context.Background(), "o", "r", "s") + if err != nil { + t.Errorf("Repositories.GetCommit returned error: %v", err) + } + + want := &RepositoryCommit{ + SHA: String("s"), + Commit: &Commit{ + Message: String("m"), + }, + Author: &User{ + Login: String("l"), + }, + Committer: &User{ + Login: String("l"), + }, + Parents: []Commit{ + { + SHA: String("s"), + }, + }, + Stats: &CommitStats{ + Additions: Int(104), + Deletions: Int(4), + Total: Int(108), + }, + Files: []CommitFile{ + { + Filename: String("f"), + Additions: Int(10), + Deletions: Int(2), + Changes: Int(12), + Status: String("s"), + Patch: String("p"), + BlobURL: String("b"), + RawURL: String("r"), + ContentsURL: String("c"), + }, + }, + } + if !reflect.DeepEqual(commit, want) { + t.Errorf("Repositories.GetCommit returned \n%+v, want \n%+v", commit, want) + } +} + +func TestRepositoriesService_GetCommitRaw_diff(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + const rawStr = "@@diff content" + + mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3Diff) + fmt.Fprint(w, rawStr) + }) + + got, _, err := client.Repositories.GetCommitRaw(context.Background(), "o", "r", "s", RawOptions{Type: Diff}) + if err != nil { + t.Fatalf("Repositories.GetCommitRaw returned error: %v", err) + } + want := rawStr + if got != want { + t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want) + } +} + +func TestRepositoriesService_GetCommitRaw_patch(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + const rawStr = "@@patch content" + + mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3Patch) + fmt.Fprint(w, rawStr) + }) + + got, _, err := client.Repositories.GetCommitRaw(context.Background(), "o", "r", "s", RawOptions{Type: Patch}) + if err != nil { + t.Fatalf("Repositories.GetCommitRaw returned error: %v", err) + } + want := rawStr + if got != want { + t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want) + } +} + +func TestRepositoriesService_GetCommitRaw_invalid(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.GetCommitRaw(context.Background(), "o", "r", "s", RawOptions{100}) + if err == nil { + t.Fatal("Repositories.GetCommitRaw should return error") + } + if !strings.Contains(err.Error(), "unsupported raw type") { + t.Error("Repositories.GetCommitRaw should return unsupported raw type error") + } +} + +func TestRepositoriesService_GetCommitSHA1(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + const sha1 = "01234abcde" + + mux.HandleFunc("/repos/o/r/commits/master", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3SHA) + + fmt.Fprintf(w, sha1) + }) + + got, _, err := client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "master", "") + if err != nil { + t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err) + } + + want := sha1 + if got != want { + t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want) + } + + mux.HandleFunc("/repos/o/r/commits/tag", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3SHA) + testHeader(t, r, "If-None-Match", `"`+sha1+`"`) + + w.WriteHeader(http.StatusNotModified) + }) + + got, _, err = client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "tag", sha1) + if err == nil { + t.Errorf("Expected HTTP 304 response") + } + + want = "" + if got != want { + t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want) + } +} + +func TestRepositoriesService_CompareCommits(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/compare/b...h", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "base_commit": { + "sha": "s", + "commit": { + "author": { "name": "n" }, + "committer": { "name": "n" }, + "message": "m", + "tree": { "sha": "t" } + }, + "author": { "login": "l" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ] + }, + "status": "s", + "ahead_by": 1, + "behind_by": 2, + "total_commits": 1, + "commits": [ + { + "sha": "s", + "commit": { "author": { "name": "n" } }, + "author": { "login": "l" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ] + } + ], + "files": [ { "filename": "f" } ], + "html_url": "https://github.com/o/r/compare/b...h", + "permalink_url": "https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17", + "diff_url": "https://github.com/o/r/compare/b...h.diff", + "patch_url": "https://github.com/o/r/compare/b...h.patch", + "url": "https://api.github.com/repos/o/r/compare/b...h" + }`) + }) + + got, _, err := client.Repositories.CompareCommits(context.Background(), "o", "r", "b", "h") + if err != nil { + t.Errorf("Repositories.CompareCommits returned error: %v", err) + } + + want := &CommitsComparison{ + BaseCommit: &RepositoryCommit{ + SHA: String("s"), + Commit: &Commit{ + Author: &CommitAuthor{Name: String("n")}, + Committer: &CommitAuthor{Name: String("n")}, + Message: String("m"), + Tree: &Tree{SHA: String("t")}, + }, + Author: &User{Login: String("l")}, + Committer: &User{Login: String("l")}, + Parents: []Commit{ + { + SHA: String("s"), + }, + }, + }, + Status: String("s"), + AheadBy: Int(1), + BehindBy: Int(2), + TotalCommits: Int(1), + Commits: []RepositoryCommit{ + { + SHA: String("s"), + Commit: &Commit{ + Author: &CommitAuthor{Name: String("n")}, + }, + Author: &User{Login: String("l")}, + Committer: &User{Login: String("l")}, + Parents: []Commit{ + { + SHA: String("s"), + }, + }, + }, + }, + Files: []CommitFile{ + { + Filename: String("f"), + }, + }, + HTMLURL: String("https://github.com/o/r/compare/b...h"), + PermalinkURL: String("https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17"), + DiffURL: String("https://github.com/o/r/compare/b...h.diff"), + PatchURL: String("https://github.com/o/r/compare/b...h.patch"), + URL: String("https://api.github.com/repos/o/r/compare/b...h"), + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_community_health.go b/vendor/github.com/google/go-github/github/repos_community_health.go new file mode 100644 index 00000000..b5c75d6f --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_community_health.go @@ -0,0 +1,57 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// Metric represents the different fields for one file in community health files. +type Metric struct { + Name *string `json:"name"` + Key *string `json:"key"` + URL *string `json:"url"` + HTMLURL *string `json:"html_url"` +} + +// CommunityHealthFiles represents the different files in the community health metrics response. +type CommunityHealthFiles struct { + CodeOfConduct *Metric `json:"code_of_conduct"` + Contributing *Metric `json:"contributing"` + License *Metric `json:"license"` + Readme *Metric `json:"readme"` +} + +// CommunityHealthMetrics represents a response containing the community metrics of a repository. +type CommunityHealthMetrics struct { + HealthPercentage *int `json:"health_percentage"` + Files *CommunityHealthFiles `json:"files"` + UpdatedAt *time.Time `json:"updated_at"` +} + +// GetCommunityHealthMetrics retrieves all the community health metrics for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/community/#retrieve-community-health-metrics +func (s *RepositoriesService) GetCommunityHealthMetrics(ctx context.Context, owner, repo string) (*CommunityHealthMetrics, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/community/profile", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryCommunityHealthMetricsPreview) + + metrics := &CommunityHealthMetrics{} + resp, err := s.client.Do(ctx, req, metrics) + if err != nil { + return nil, resp, err + } + + return metrics, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_community_health_test.go b/vendor/github.com/google/go-github/github/repos_community_health_test.go new file mode 100644 index 00000000..c9499aa1 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_community_health_test.go @@ -0,0 +1,86 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestRepositoriesService_GetCommunityHealthMetrics(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/community/profile", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeRepositoryCommunityHealthMetricsPreview) + fmt.Fprintf(w, `{ + "health_percentage": 100, + "files": { + "code_of_conduct": { + "name": "Contributor Covenant", + "key": "contributor_covenant", + "url": null, + "html_url": "https://github.com/octocat/Hello-World/blob/master/CODE_OF_CONDUCT.md" + }, + "contributing": { + "url": "https://api.github.com/repos/octocat/Hello-World/contents/CONTRIBUTING", + "html_url": "https://github.com/octocat/Hello-World/blob/master/CONTRIBUTING" + }, + "license": { + "name": "MIT License", + "key": "mit", + "url": "https://api.github.com/licenses/mit", + "html_url": "https://github.com/octocat/Hello-World/blob/master/LICENSE" + }, + "readme": { + "url": "https://api.github.com/repos/octocat/Hello-World/contents/README.md", + "html_url": "https://github.com/octocat/Hello-World/blob/master/README.md" + } + }, + "updated_at": "2017-02-28T00:00:00Z" + }`) + }) + + got, _, err := client.Repositories.GetCommunityHealthMetrics(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.GetCommunityHealthMetrics returned error: %v", err) + } + + updatedAt := time.Date(2017, 02, 28, 0, 0, 0, 0, time.UTC) + want := &CommunityHealthMetrics{ + HealthPercentage: Int(100), + UpdatedAt: &updatedAt, + Files: &CommunityHealthFiles{ + CodeOfConduct: &Metric{ + Name: String("Contributor Covenant"), + Key: String("contributor_covenant"), + HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/CODE_OF_CONDUCT.md"), + }, + Contributing: &Metric{ + URL: String("https://api.github.com/repos/octocat/Hello-World/contents/CONTRIBUTING"), + HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/CONTRIBUTING"), + }, + License: &Metric{ + Name: String("MIT License"), + Key: String("mit"), + URL: String("https://api.github.com/licenses/mit"), + HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/LICENSE"), + }, + Readme: &Metric{ + URL: String("https://api.github.com/repos/octocat/Hello-World/contents/README.md"), + HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/README.md"), + }, + }, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.GetCommunityHealthMetrics:\ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want)) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_contents.go b/vendor/github.com/google/go-github/github/repos_contents.go new file mode 100644 index 00000000..ffb56b90 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_contents.go @@ -0,0 +1,266 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Repository contents API methods. +// GitHub API docs: https://developer.github.com/v3/repos/contents/ + +package github + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "path" +) + +// RepositoryContent represents a file or directory in a github repository. +type RepositoryContent struct { + Type *string `json:"type,omitempty"` + Encoding *string `json:"encoding,omitempty"` + Size *int `json:"size,omitempty"` + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` + // Content contains the actual file content, which may be encoded. + // Callers should call GetContent which will decode the content if + // necessary. + Content *string `json:"content,omitempty"` + SHA *string `json:"sha,omitempty"` + URL *string `json:"url,omitempty"` + GitURL *string `json:"git_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + DownloadURL *string `json:"download_url,omitempty"` +} + +// RepositoryContentResponse holds the parsed response from CreateFile, UpdateFile, and DeleteFile. +type RepositoryContentResponse struct { + Content *RepositoryContent `json:"content,omitempty"` + Commit `json:"commit,omitempty"` +} + +// RepositoryContentFileOptions specifies optional parameters for CreateFile, UpdateFile, and DeleteFile. +type RepositoryContentFileOptions struct { + Message *string `json:"message,omitempty"` + Content []byte `json:"content,omitempty"` // unencoded + SHA *string `json:"sha,omitempty"` + Branch *string `json:"branch,omitempty"` + Author *CommitAuthor `json:"author,omitempty"` + Committer *CommitAuthor `json:"committer,omitempty"` +} + +// RepositoryContentGetOptions represents an optional ref parameter, which can be a SHA, +// branch, or tag +type RepositoryContentGetOptions struct { + Ref string `url:"ref,omitempty"` +} + +// String converts RepositoryContent to a string. It's primarily for testing. +func (r RepositoryContent) String() string { + return Stringify(r) +} + +// GetContent returns the content of r, decoding it if necessary. +func (r *RepositoryContent) GetContent() (string, error) { + var encoding string + if r.Encoding != nil { + encoding = *r.Encoding + } + + switch encoding { + case "base64": + c, err := base64.StdEncoding.DecodeString(*r.Content) + return string(c), err + case "": + if r.Content == nil { + return "", nil + } + return *r.Content, nil + default: + return "", fmt.Errorf("unsupported content encoding: %v", encoding) + } +} + +// GetReadme gets the Readme file for the repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-the-readme +func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string, opt *RepositoryContentGetOptions) (*RepositoryContent, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/readme", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + readme := new(RepositoryContent) + resp, err := s.client.Do(ctx, req, readme) + if err != nil { + return nil, resp, err + } + return readme, resp, nil +} + +// DownloadContents returns an io.ReadCloser that reads the contents of the +// specified file. This function will work with files of any size, as opposed +// to GetContents which is limited to 1 Mb files. It is the caller's +// responsibility to close the ReadCloser. +func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, filepath string, opt *RepositoryContentGetOptions) (io.ReadCloser, error) { + dir := path.Dir(filepath) + filename := path.Base(filepath) + _, dirContents, _, err := s.GetContents(ctx, owner, repo, dir, opt) + if err != nil { + return nil, err + } + for _, contents := range dirContents { + if *contents.Name == filename { + if contents.DownloadURL == nil || *contents.DownloadURL == "" { + return nil, fmt.Errorf("No download link found for %s", filepath) + } + resp, err := s.client.client.Get(*contents.DownloadURL) + if err != nil { + return nil, err + } + return resp.Body, nil + } + } + return nil, fmt.Errorf("No file named %s found in %s", filename, dir) +} + +// GetContents can return either the metadata and content of a single file +// (when path references a file) or the metadata of all the files and/or +// subdirectories of a directory (when path references a directory). To make it +// easy to distinguish between both result types and to mimic the API as much +// as possible, both result types will be returned but only one will contain a +// value and the other will be nil. +// +// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-contents +func (s *RepositoriesService) GetContents(ctx context.Context, owner, repo, path string, opt *RepositoryContentGetOptions) (fileContent *RepositoryContent, directoryContent []*RepositoryContent, resp *Response, err error) { + escapedPath := (&url.URL{Path: path}).String() + u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, escapedPath) + u, err = addOptions(u, opt) + if err != nil { + return nil, nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, nil, err + } + var rawJSON json.RawMessage + resp, err = s.client.Do(ctx, req, &rawJSON) + if err != nil { + return nil, nil, resp, err + } + fileUnmarshalError := json.Unmarshal(rawJSON, &fileContent) + if fileUnmarshalError == nil { + return fileContent, nil, resp, nil + } + directoryUnmarshalError := json.Unmarshal(rawJSON, &directoryContent) + if directoryUnmarshalError == nil { + return nil, directoryContent, resp, nil + } + return nil, nil, resp, fmt.Errorf("unmarshalling failed for both file and directory content: %s and %s", fileUnmarshalError, directoryUnmarshalError) +} + +// CreateFile creates a new file in a repository at the given path and returns +// the commit and file metadata. +// +// GitHub API docs: https://developer.github.com/v3/repos/contents/#create-a-file +func (s *RepositoriesService) CreateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) + req, err := s.client.NewRequest("PUT", u, opt) + if err != nil { + return nil, nil, err + } + createResponse := new(RepositoryContentResponse) + resp, err := s.client.Do(ctx, req, createResponse) + if err != nil { + return nil, resp, err + } + return createResponse, resp, nil +} + +// UpdateFile updates a file in a repository at the given path and returns the +// commit and file metadata. Requires the blob SHA of the file being updated. +// +// GitHub API docs: https://developer.github.com/v3/repos/contents/#update-a-file +func (s *RepositoriesService) UpdateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) + req, err := s.client.NewRequest("PUT", u, opt) + if err != nil { + return nil, nil, err + } + updateResponse := new(RepositoryContentResponse) + resp, err := s.client.Do(ctx, req, updateResponse) + if err != nil { + return nil, resp, err + } + return updateResponse, resp, nil +} + +// DeleteFile deletes a file from a repository and returns the commit. +// Requires the blob SHA of the file to be deleted. +// +// GitHub API docs: https://developer.github.com/v3/repos/contents/#delete-a-file +func (s *RepositoriesService) DeleteFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) + req, err := s.client.NewRequest("DELETE", u, opt) + if err != nil { + return nil, nil, err + } + deleteResponse := new(RepositoryContentResponse) + resp, err := s.client.Do(ctx, req, deleteResponse) + if err != nil { + return nil, resp, err + } + return deleteResponse, resp, nil +} + +// archiveFormat is used to define the archive type when calling GetArchiveLink. +type archiveFormat string + +const ( + // Tarball specifies an archive in gzipped tar format. + Tarball archiveFormat = "tarball" + + // Zipball specifies an archive in zip format. + Zipball archiveFormat = "zipball" +) + +// GetArchiveLink returns an URL to download a tarball or zipball archive for a +// repository. The archiveFormat can be specified by either the github.Tarball +// or github.Zipball constant. +// +// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-archive-link +func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions) (*url.URL, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/%s", owner, repo, archiveformat) + if opt != nil && opt.Ref != "" { + u += fmt.Sprintf("/%s", opt.Ref) + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + var resp *http.Response + // Use http.DefaultTransport if no custom Transport is configured + req = withContext(ctx, req) + if s.client.client.Transport == nil { + resp, err = http.DefaultTransport.RoundTrip(req) + } else { + resp, err = s.client.client.Transport.RoundTrip(req) + } + if err != nil { + return nil, nil, err + } + resp.Body.Close() + if resp.StatusCode != http.StatusFound { + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) + } + parsedURL, err := url.Parse(resp.Header.Get("Location")) + return parsedURL, newResponse(resp), err +} diff --git a/vendor/github.com/google/go-github/github/repos_contents_test.go b/vendor/github.com/google/go-github/github/repos_contents_test.go new file mode 100644 index 00000000..0a08e8c7 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_contents_test.go @@ -0,0 +1,378 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "reflect" + "testing" +) + +func TestRepositoryContent_GetContent(t *testing.T) { + tests := []struct { + encoding, content *string // input encoding and content + want string // desired output + wantErr bool // whether an error is expected + }{ + { + encoding: String(""), + content: String("hello"), + want: "hello", + wantErr: false, + }, + { + encoding: nil, + content: String("hello"), + want: "hello", + wantErr: false, + }, + { + encoding: nil, + content: nil, + want: "", + wantErr: false, + }, + { + encoding: String("base64"), + content: String("aGVsbG8="), + want: "hello", + wantErr: false, + }, + { + encoding: String("bad"), + content: String("aGVsbG8="), + want: "", + wantErr: true, + }, + } + + for _, tt := range tests { + r := RepositoryContent{Encoding: tt.encoding, Content: tt.content} + got, err := r.GetContent() + if err != nil && !tt.wantErr { + t.Errorf("RepositoryContent(%q, %q) returned unexpected error: %v", tt.encoding, tt.content, err) + } + if err == nil && tt.wantErr { + t.Errorf("RepositoryContent(%q, %q) did not return unexpected error", tt.encoding, tt.content) + } + if want := tt.want; got != want { + t.Errorf("RepositoryContent.GetContent returned %+v, want %+v", got, want) + } + } +} + +func TestRepositoriesService_GetReadme(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/readme", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "type": "file", + "encoding": "base64", + "size": 5362, + "name": "README.md", + "path": "README.md" + }`) + }) + readme, _, err := client.Repositories.GetReadme(context.Background(), "o", "r", &RepositoryContentGetOptions{}) + if err != nil { + t.Errorf("Repositories.GetReadme returned error: %v", err) + } + want := &RepositoryContent{Type: String("file"), Name: String("README.md"), Size: Int(5362), Encoding: String("base64"), Path: String("README.md")} + if !reflect.DeepEqual(readme, want) { + t.Errorf("Repositories.GetReadme returned %+v, want %+v", readme, want) + } +} + +func TestRepositoriesService_DownloadContents_Success(t *testing.T) { + client, mux, serverURL, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "type": "file", + "name": "f", + "download_url": "`+serverURL+baseURLPath+`/download/f" + }]`) + }) + mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, "foo") + }) + + r, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil) + if err != nil { + t.Errorf("Repositories.DownloadContents returned error: %v", err) + } + + bytes, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("Error reading response body: %v", err) + } + r.Close() + + if got, want := string(bytes), "foo"; got != want { + t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) + } +} + +func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "type": "file", + "name": "f", + }]`) + }) + + _, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil) + if err == nil { + t.Errorf("Repositories.DownloadContents did not return expected error") + } +} + +func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[]`) + }) + + _, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil) + if err == nil { + t.Errorf("Repositories.DownloadContents did not return expected error") + } +} + +func TestRepositoriesService_GetContents_File(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "type": "file", + "encoding": "base64", + "size": 20678, + "name": "LICENSE", + "path": "LICENSE" + }`) + }) + fileContents, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{}) + if err != nil { + t.Errorf("Repositories.GetContents returned error: %v", err) + } + want := &RepositoryContent{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Encoding: String("base64"), Path: String("LICENSE")} + if !reflect.DeepEqual(fileContents, want) { + t.Errorf("Repositories.GetContents returned %+v, want %+v", fileContents, want) + } +} + +func TestRepositoriesService_GetContents_FilenameNeedsEscape(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/p#?%/中.go", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{}`) + }) + _, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p#?%/中.go", &RepositoryContentGetOptions{}) + if err != nil { + t.Fatalf("Repositories.GetContents returned error: %v", err) + } +} + +func TestRepositoriesService_GetContents_DirectoryWithSpaces(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/some directory/file.go", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{}`) + }) + _, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory/file.go", &RepositoryContentGetOptions{}) + if err != nil { + t.Fatalf("Repositories.GetContents returned error: %v", err) + } +} + +func TestRepositoriesService_GetContents_DirectoryWithPlusChars(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/some directory+name/file.go", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{}`) + }) + _, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{}) + if err != nil { + t.Fatalf("Repositories.GetContents returned error: %v", err) + } +} + +func TestRepositoriesService_GetContents_Directory(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "type": "dir", + "name": "lib", + "path": "lib" + }, + { + "type": "file", + "size": 20678, + "name": "LICENSE", + "path": "LICENSE" + }]`) + }) + _, directoryContents, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{}) + if err != nil { + t.Errorf("Repositories.GetContents returned error: %v", err) + } + want := []*RepositoryContent{{Type: String("dir"), Name: String("lib"), Path: String("lib")}, + {Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Path: String("LICENSE")}} + if !reflect.DeepEqual(directoryContents, want) { + t.Errorf("Repositories.GetContents_Directory returned %+v, want %+v", directoryContents, want) + } +} + +func TestRepositoriesService_CreateFile(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "content":{ + "name":"p" + }, + "commit":{ + "message":"m", + "sha":"f5f369044773ff9c6383c087466d12adb6fa0828" + } + }`) + }) + message := "m" + content := []byte("c") + repositoryContentsOptions := &RepositoryContentFileOptions{ + Message: &message, + Content: content, + Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, + } + createResponse, _, err := client.Repositories.CreateFile(context.Background(), "o", "r", "p", repositoryContentsOptions) + if err != nil { + t.Errorf("Repositories.CreateFile returned error: %v", err) + } + want := &RepositoryContentResponse{ + Content: &RepositoryContent{Name: String("p")}, + Commit: Commit{ + Message: String("m"), + SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), + }, + } + if !reflect.DeepEqual(createResponse, want) { + t.Errorf("Repositories.CreateFile returned %+v, want %+v", createResponse, want) + } +} + +func TestRepositoriesService_UpdateFile(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "content":{ + "name":"p" + }, + "commit":{ + "message":"m", + "sha":"f5f369044773ff9c6383c087466d12adb6fa0828" + } + }`) + }) + message := "m" + content := []byte("c") + sha := "f5f369044773ff9c6383c087466d12adb6fa0828" + repositoryContentsOptions := &RepositoryContentFileOptions{ + Message: &message, + Content: content, + SHA: &sha, + Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, + } + updateResponse, _, err := client.Repositories.UpdateFile(context.Background(), "o", "r", "p", repositoryContentsOptions) + if err != nil { + t.Errorf("Repositories.UpdateFile returned error: %v", err) + } + want := &RepositoryContentResponse{ + Content: &RepositoryContent{Name: String("p")}, + Commit: Commit{ + Message: String("m"), + SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), + }, + } + if !reflect.DeepEqual(updateResponse, want) { + t.Errorf("Repositories.UpdateFile returned %+v, want %+v", updateResponse, want) + } +} + +func TestRepositoriesService_DeleteFile(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{ + "content": null, + "commit":{ + "message":"m", + "sha":"f5f369044773ff9c6383c087466d12adb6fa0828" + } + }`) + }) + message := "m" + sha := "f5f369044773ff9c6383c087466d12adb6fa0828" + repositoryContentsOptions := &RepositoryContentFileOptions{ + Message: &message, + SHA: &sha, + Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, + } + deleteResponse, _, err := client.Repositories.DeleteFile(context.Background(), "o", "r", "p", repositoryContentsOptions) + if err != nil { + t.Errorf("Repositories.DeleteFile returned error: %v", err) + } + want := &RepositoryContentResponse{ + Content: nil, + Commit: Commit{ + Message: String("m"), + SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), + }, + } + if !reflect.DeepEqual(deleteResponse, want) { + t.Errorf("Repositories.DeleteFile returned %+v, want %+v", deleteResponse, want) + } +} + +func TestRepositoriesService_GetArchiveLink(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "http://github.com/a", http.StatusFound) + }) + url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}) + if err != nil { + t.Errorf("Repositories.GetArchiveLink returned error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound) + } + want := "http://github.com/a" + if url.String() != want { + t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_deployments.go b/vendor/github.com/google/go-github/github/repos_deployments.go new file mode 100644 index 00000000..1300f05e --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_deployments.go @@ -0,0 +1,237 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "strings" +) + +// Deployment represents a deployment in a repo +type Deployment struct { + URL *string `json:"url,omitempty"` + ID *int64 `json:"id,omitempty"` + SHA *string `json:"sha,omitempty"` + Ref *string `json:"ref,omitempty"` + Task *string `json:"task,omitempty"` + Payload json.RawMessage `json:"payload,omitempty"` + Environment *string `json:"environment,omitempty"` + Description *string `json:"description,omitempty"` + Creator *User `json:"creator,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + StatusesURL *string `json:"statuses_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +// DeploymentRequest represents a deployment request +type DeploymentRequest struct { + Ref *string `json:"ref,omitempty"` + Task *string `json:"task,omitempty"` + AutoMerge *bool `json:"auto_merge,omitempty"` + RequiredContexts *[]string `json:"required_contexts,omitempty"` + Payload *string `json:"payload,omitempty"` + Environment *string `json:"environment,omitempty"` + Description *string `json:"description,omitempty"` + TransientEnvironment *bool `json:"transient_environment,omitempty"` + ProductionEnvironment *bool `json:"production_environment,omitempty"` +} + +// DeploymentsListOptions specifies the optional parameters to the +// RepositoriesService.ListDeployments method. +type DeploymentsListOptions struct { + // SHA of the Deployment. + SHA string `url:"sha,omitempty"` + + // List deployments for a given ref. + Ref string `url:"ref,omitempty"` + + // List deployments for a given task. + Task string `url:"task,omitempty"` + + // List deployments for a given environment. + Environment string `url:"environment,omitempty"` + + ListOptions +} + +// ListDeployments lists the deployments of a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployments +func (s *RepositoriesService) ListDeployments(ctx context.Context, owner, repo string, opt *DeploymentsListOptions) ([]*Deployment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var deployments []*Deployment + resp, err := s.client.Do(ctx, req, &deployments) + if err != nil { + return nil, resp, err + } + + return deployments, resp, nil +} + +// GetDeployment returns a single deployment of a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment +func (s *RepositoriesService) GetDeployment(ctx context.Context, owner, repo string, deploymentID int64) (*Deployment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments/%v", owner, repo, deploymentID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + deployment := new(Deployment) + resp, err := s.client.Do(ctx, req, deployment) + if err != nil { + return nil, resp, err + } + + return deployment, resp, nil +} + +// CreateDeployment creates a new deployment for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment +func (s *RepositoriesService) CreateDeployment(ctx context.Context, owner, repo string, request *DeploymentRequest) (*Deployment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) + + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + d := new(Deployment) + resp, err := s.client.Do(ctx, req, d) + if err != nil { + return nil, resp, err + } + + return d, resp, nil +} + +// DeploymentStatus represents the status of a +// particular deployment. +type DeploymentStatus struct { + ID *int64 `json:"id,omitempty"` + // State is the deployment state. + // Possible values are: "pending", "success", "failure", "error", "inactive". + State *string `json:"state,omitempty"` + Creator *User `json:"creator,omitempty"` + Description *string `json:"description,omitempty"` + TargetURL *string `json:"target_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + DeploymentURL *string `json:"deployment_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +// DeploymentStatusRequest represents a deployment request +type DeploymentStatusRequest struct { + State *string `json:"state,omitempty"` + LogURL *string `json:"log_url,omitempty"` + Description *string `json:"description,omitempty"` + EnvironmentURL *string `json:"environment_url,omitempty"` + AutoInactive *bool `json:"auto_inactive,omitempty"` +} + +// ListDeploymentStatuses lists the statuses of a given deployment of a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployment-statuses +func (s *RepositoriesService) ListDeploymentStatuses(ctx context.Context, owner, repo string, deployment int64, opt *ListOptions) ([]*DeploymentStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) + + var statuses []*DeploymentStatus + resp, err := s.client.Do(ctx, req, &statuses) + if err != nil { + return nil, resp, err + } + + return statuses, resp, nil +} + +// GetDeploymentStatus returns a single deployment status of a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment-status +func (s *RepositoriesService) GetDeploymentStatus(ctx context.Context, owner, repo string, deploymentID, deploymentStatusID int64) (*DeploymentStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses/%v", owner, repo, deploymentID, deploymentStatusID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + d := new(DeploymentStatus) + resp, err := s.client.Do(ctx, req, d) + if err != nil { + return nil, resp, err + } + + return d, resp, nil +} + +// CreateDeploymentStatus creates a new status for a deployment. +// +// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment-status +func (s *RepositoriesService) CreateDeploymentStatus(ctx context.Context, owner, repo string, deployment int64, request *DeploymentStatusRequest) (*DeploymentStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) + + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept headers when APIs fully launch. + acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + + d := new(DeploymentStatus) + resp, err := s.client.Do(ctx, req, d) + if err != nil { + return nil, resp, err + } + + return d, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_deployments_test.go b/vendor/github.com/google/go-github/github/repos_deployments_test.go new file mode 100644 index 00000000..b1e5ec6f --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_deployments_test.go @@ -0,0 +1,168 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestRepositoriesService_ListDeployments(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/deployments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"environment": "test"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &DeploymentsListOptions{Environment: "test"} + deployments, _, err := client.Repositories.ListDeployments(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListDeployments returned error: %v", err) + } + + want := []*Deployment{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(deployments, want) { + t.Errorf("Repositories.ListDeployments returned %+v, want %+v", deployments, want) + } +} + +func TestRepositoriesService_GetDeployment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/deployments/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + fmt.Fprint(w, `{"id":3}`) + }) + + deployment, _, err := client.Repositories.GetDeployment(context.Background(), "o", "r", 3) + if err != nil { + t.Errorf("Repositories.GetDeployment returned error: %v", err) + } + + want := &Deployment{ID: Int64(3)} + + if !reflect.DeepEqual(deployment, want) { + t.Errorf("Repositories.GetDeployment returned %+v, want %+v", deployment, want) + } +} + +func TestRepositoriesService_CreateDeployment(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &DeploymentRequest{Ref: String("1111"), Task: String("deploy"), TransientEnvironment: Bool(true)} + + acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/repos/o/r/deployments", func(w http.ResponseWriter, r *http.Request) { + v := new(DeploymentRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"ref": "1111", "task": "deploy"}`) + }) + + deployment, _, err := client.Repositories.CreateDeployment(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.CreateDeployment returned error: %v", err) + } + + want := &Deployment{Ref: String("1111"), Task: String("deploy")} + if !reflect.DeepEqual(deployment, want) { + t.Errorf("Repositories.CreateDeployment returned %+v, want %+v", deployment, want) + } +} + +func TestRepositoriesService_ListDeploymentStatuses(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/deployments/1/statuses", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGraphQLNodeIDPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + statutses, _, err := client.Repositories.ListDeploymentStatuses(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("Repositories.ListDeploymentStatuses returned error: %v", err) + } + + want := []*DeploymentStatus{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(statutses, want) { + t.Errorf("Repositories.ListDeploymentStatuses returned %+v, want %+v", statutses, want) + } +} + +func TestRepositoriesService_GetDeploymentStatus(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/repos/o/r/deployments/3/statuses/4", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `{"id":4}`) + }) + + deploymentStatus, _, err := client.Repositories.GetDeploymentStatus(context.Background(), "o", "r", 3, 4) + if err != nil { + t.Errorf("Repositories.GetDeploymentStatus returned error: %v", err) + } + + want := &DeploymentStatus{ID: Int64(4)} + if !reflect.DeepEqual(deploymentStatus, want) { + t.Errorf("Repositories.GetDeploymentStatus returned %+v, want %+v", deploymentStatus, want) + } +} + +func TestRepositoriesService_CreateDeploymentStatus(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &DeploymentStatusRequest{State: String("inactive"), Description: String("deploy"), AutoInactive: Bool(false)} + + acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} + mux.HandleFunc("/repos/o/r/deployments/1/statuses", func(w http.ResponseWriter, r *http.Request) { + v := new(DeploymentStatusRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"state": "inactive", "description": "deploy"}`) + }) + + deploymentStatus, _, err := client.Repositories.CreateDeploymentStatus(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Repositories.CreateDeploymentStatus returned error: %v", err) + } + + want := &DeploymentStatus{State: String("inactive"), Description: String("deploy")} + if !reflect.DeepEqual(deploymentStatus, want) { + t.Errorf("Repositories.CreateDeploymentStatus returned %+v, want %+v", deploymentStatus, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_forks.go b/vendor/github.com/google/go-github/github/repos_forks.go new file mode 100644 index 00000000..4ca19a42 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_forks.go @@ -0,0 +1,85 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RepositoryListForksOptions specifies the optional parameters to the +// RepositoriesService.ListForks method. +type RepositoryListForksOptions struct { + // How to sort the forks list. Possible values are: newest, oldest, + // watchers. Default is "newest". + Sort string `url:"sort,omitempty"` + + ListOptions +} + +// ListForks lists the forks of the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/forks/#list-forks +func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string, opt *RepositoryListForksOptions) ([]*Repository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when topics API fully launches. + req.Header.Set("Accept", mediaTypeTopicsPreview) + + var repos []*Repository + resp, err := s.client.Do(ctx, req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// RepositoryCreateForkOptions specifies the optional parameters to the +// RepositoriesService.CreateFork method. +type RepositoryCreateForkOptions struct { + // The organization to fork the repository into. + Organization string `url:"organization,omitempty"` +} + +// CreateFork creates a fork of the specified repository. +// +// This method might return an *AcceptedError and a status code of +// 202. This is because this is the status that GitHub returns to signify that +// it is now computing creating the fork in a background task. +// A follow up request, after a delay of a second or so, should result +// in a successful request. +// +// GitHub API docs: https://developer.github.com/v3/repos/forks/#create-a-fork +func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, opt *RepositoryCreateForkOptions) (*Repository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, nil, err + } + + fork := new(Repository) + resp, err := s.client.Do(ctx, req, fork) + if err != nil { + return nil, resp, err + } + + return fork, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_forks_test.go b/vendor/github.com/google/go-github/github/repos_forks_test.go new file mode 100644 index 00000000..8513f0e2 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_forks_test.go @@ -0,0 +1,81 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListForks(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + testFormValues(t, r, values{ + "sort": "newest", + "page": "3", + }) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + opt := &RepositoryListForksOptions{ + Sort: "newest", + ListOptions: ListOptions{Page: 3}, + } + repos, _, err := client.Repositories.ListForks(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListForks returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Repositories.ListForks returned %+v, want %+v", repos, want) + } +} + +func TestRepositoriesService_ListForks_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListForks(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_CreateFork(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testFormValues(t, r, values{"organization": "o"}) + fmt.Fprint(w, `{"id":1}`) + }) + + opt := &RepositoryCreateForkOptions{Organization: "o"} + repo, _, err := client.Repositories.CreateFork(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.CreateFork returned error: %v", err) + } + + want := &Repository{ID: Int64(1)} + if !reflect.DeepEqual(repo, want) { + t.Errorf("Repositories.CreateFork returned %+v, want %+v", repo, want) + } +} + +func TestRepositoriesService_CreateFork_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.CreateFork(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/repos_hooks.go b/vendor/github.com/google/go-github/github/repos_hooks.go new file mode 100644 index 00000000..f7ab3a13 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_hooks.go @@ -0,0 +1,192 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// WebHookPayload represents the data that is received from GitHub when a push +// event hook is triggered. The format of these payloads pre-date most of the +// GitHub v3 API, so there are lots of minor incompatibilities with the types +// defined in the rest of the API. Therefore, several types are duplicated +// here to account for these differences. +// +// GitHub API docs: https://help.github.com/articles/post-receive-hooks +type WebHookPayload struct { + After *string `json:"after,omitempty"` + Before *string `json:"before,omitempty"` + Commits []WebHookCommit `json:"commits,omitempty"` + Compare *string `json:"compare,omitempty"` + Created *bool `json:"created,omitempty"` + Deleted *bool `json:"deleted,omitempty"` + Forced *bool `json:"forced,omitempty"` + HeadCommit *WebHookCommit `json:"head_commit,omitempty"` + Pusher *User `json:"pusher,omitempty"` + Ref *string `json:"ref,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` +} + +func (w WebHookPayload) String() string { + return Stringify(w) +} + +// WebHookCommit represents the commit variant we receive from GitHub in a +// WebHookPayload. +type WebHookCommit struct { + Added []string `json:"added,omitempty"` + Author *WebHookAuthor `json:"author,omitempty"` + Committer *WebHookAuthor `json:"committer,omitempty"` + Distinct *bool `json:"distinct,omitempty"` + ID *string `json:"id,omitempty"` + Message *string `json:"message,omitempty"` + Modified []string `json:"modified,omitempty"` + Removed []string `json:"removed,omitempty"` + Timestamp *time.Time `json:"timestamp,omitempty"` +} + +func (w WebHookCommit) String() string { + return Stringify(w) +} + +// WebHookAuthor represents the author or committer of a commit, as specified +// in a WebHookCommit. The commit author may not correspond to a GitHub User. +type WebHookAuthor struct { + Email *string `json:"email,omitempty"` + Name *string `json:"name,omitempty"` + Username *string `json:"username,omitempty"` +} + +func (w WebHookAuthor) String() string { + return Stringify(w) +} + +// Hook represents a GitHub (web and service) hook for a repository. +type Hook struct { + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` + Events []string `json:"events,omitempty"` + Active *bool `json:"active,omitempty"` + Config map[string]interface{} `json:"config,omitempty"` + ID *int64 `json:"id,omitempty"` +} + +func (h Hook) String() string { + return Stringify(h) +} + +// CreateHook creates a Hook for the specified repository. +// Name and Config are required fields. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#create-a-hook +func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string, hook *Hook) (*Hook, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) + req, err := s.client.NewRequest("POST", u, hook) + if err != nil { + return nil, nil, err + } + + h := new(Hook) + resp, err := s.client.Do(ctx, req, h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} + +// ListHooks lists all Hooks for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#list +func (s *RepositoriesService) ListHooks(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Hook, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hooks []*Hook + resp, err := s.client.Do(ctx, req, &hooks) + if err != nil { + return nil, resp, err + } + + return hooks, resp, nil +} + +// GetHook returns a single specified Hook. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#get-single-hook +func (s *RepositoriesService) GetHook(ctx context.Context, owner, repo string, id int64) (*Hook, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + hook := new(Hook) + resp, err := s.client.Do(ctx, req, hook) + return hook, resp, err +} + +// EditHook updates a specified Hook. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#edit-a-hook +func (s *RepositoriesService) EditHook(ctx context.Context, owner, repo string, id int64, hook *Hook) (*Hook, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) + req, err := s.client.NewRequest("PATCH", u, hook) + if err != nil { + return nil, nil, err + } + h := new(Hook) + resp, err := s.client.Do(ctx, req, h) + return h, resp, err +} + +// DeleteHook deletes a specified Hook. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#delete-a-hook +func (s *RepositoriesService) DeleteHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// PingHook triggers a 'ping' event to be sent to the Hook. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#ping-a-hook +func (s *RepositoriesService) PingHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%d/pings", owner, repo, id) + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// TestHook triggers a test Hook by github. +// +// GitHub API docs: https://developer.github.com/v3/repos/hooks/#test-a-push-hook +func (s *RepositoriesService) TestHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%d/tests", owner, repo, id) + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/repos_hooks_test.go b/vendor/github.com/google/go-github/github/repos_hooks_test.go new file mode 100644 index 00000000..a1435619 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_hooks_test.go @@ -0,0 +1,206 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_CreateHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Hook{Name: String("t")} + + mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { + v := new(Hook) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + hook, _, err := client.Repositories.CreateHook(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.CreateHook returned error: %v", err) + } + + want := &Hook{ID: Int64(1)} + if !reflect.DeepEqual(hook, want) { + t.Errorf("Repositories.CreateHook returned %+v, want %+v", hook, want) + } +} + +func TestRepositoriesService_CreateHook_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.CreateHook(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_ListHooks(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + + hooks, _, err := client.Repositories.ListHooks(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListHooks returned error: %v", err) + } + + want := []*Hook{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(hooks, want) { + t.Errorf("Repositories.ListHooks returned %+v, want %+v", hooks, want) + } +} + +func TestRepositoriesService_ListHooks_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListHooks(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_GetHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + hook, _, err := client.Repositories.GetHook(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetHook returned error: %v", err) + } + + want := &Hook{ID: Int64(1)} + if !reflect.DeepEqual(hook, want) { + t.Errorf("Repositories.GetHook returned %+v, want %+v", hook, want) + } +} + +func TestRepositoriesService_GetHook_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.GetHook(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} + +func TestRepositoriesService_EditHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Hook{Name: String("t")} + + mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { + v := new(Hook) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + hook, _, err := client.Repositories.EditHook(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Repositories.EditHook returned error: %v", err) + } + + want := &Hook{ID: Int64(1)} + if !reflect.DeepEqual(hook, want) { + t.Errorf("Repositories.EditHook returned %+v, want %+v", hook, want) + } +} + +func TestRepositoriesService_EditHook_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.EditHook(context.Background(), "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_DeleteHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Repositories.DeleteHook(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteHook returned error: %v", err) + } +} + +func TestRepositoriesService_DeleteHook_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Repositories.DeleteHook(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} + +func TestRepositoriesService_PingHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + _, err := client.Repositories.PingHook(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.PingHook returned error: %v", err) + } +} + +func TestRepositoriesService_TestHook(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/hooks/1/tests", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + _, err := client.Repositories.TestHook(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.TestHook returned error: %v", err) + } +} + +func TestRepositoriesService_TestHook_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Repositories.TestHook(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/repos_invitations.go b/vendor/github.com/google/go-github/github/repos_invitations.go new file mode 100644 index 00000000..34bf3830 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_invitations.go @@ -0,0 +1,98 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RepositoryInvitation represents an invitation to collaborate on a repo. +type RepositoryInvitation struct { + ID *int64 `json:"id,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Invitee *User `json:"invitee,omitempty"` + Inviter *User `json:"inviter,omitempty"` + + // Permissions represents the permissions that the associated user will have + // on the repository. Possible values are: "read", "write", "admin". + Permissions *string `json:"permissions,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +// ListInvitations lists all currently-open repository invitations. +// +// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-invitations-for-a-repository +func (s *RepositoriesService) ListInvitations(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/invitations", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) + + invites := []*RepositoryInvitation{} + resp, err := s.client.Do(ctx, req, &invites) + if err != nil { + return nil, resp, err + } + + return invites, resp, nil +} + +// DeleteInvitation deletes a repository invitation. +// +// GitHub API docs: https://developer.github.com/v3/repos/invitations/#delete-a-repository-invitation +func (s *RepositoriesService) DeleteInvitation(ctx context.Context, owner, repo string, invitationID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) + + return s.client.Do(ctx, req, nil) +} + +// UpdateInvitation updates the permissions associated with a repository +// invitation. +// +// permissions represents the permissions that the associated user will have +// on the repository. Possible values are: "read", "write", "admin". +// +// GitHub API docs: https://developer.github.com/v3/repos/invitations/#update-a-repository-invitation +func (s *RepositoriesService) UpdateInvitation(ctx context.Context, owner, repo string, invitationID int64, permissions string) (*RepositoryInvitation, *Response, error) { + opts := &struct { + Permissions string `json:"permissions"` + }{Permissions: permissions} + u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) + req, err := s.client.NewRequest("PATCH", u, opts) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) + + invite := &RepositoryInvitation{} + resp, err := s.client.Do(ctx, req, invite) + if err != nil { + return nil, resp, err + } + + return invite, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_invitations_test.go b/vendor/github.com/google/go-github/github/repos_invitations_test.go new file mode 100644 index 00000000..27bec41d --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_invitations_test.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListInvitations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListOptions{Page: 2} + got, _, err := client.Repositories.ListInvitations(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListInvitations returned error: %v", err) + } + + want := []*RepositoryInvitation{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.ListInvitations = %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_DeleteInvitation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/invitations/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Repositories.DeleteInvitation(context.Background(), "o", "r", 2) + if err != nil { + t.Errorf("Repositories.DeleteInvitation returned error: %v", err) + } +} + +func TestRepositoriesService_UpdateInvitation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/invitations/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + fmt.Fprintf(w, `{"id":1}`) + }) + + got, _, err := client.Repositories.UpdateInvitation(context.Background(), "o", "r", 2, "write") + if err != nil { + t.Errorf("Repositories.UpdateInvitation returned error: %v", err) + } + + want := &RepositoryInvitation{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.UpdateInvitation = %+v, want %+v", got, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_keys.go b/vendor/github.com/google/go-github/github/repos_keys.go new file mode 100644 index 00000000..966d7b54 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_keys.go @@ -0,0 +1,111 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// The Key type is defined in users_keys.go + +// ListKeys lists the deploy keys for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/keys/#list +func (s *RepositoriesService) ListKeys(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Key, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var keys []*Key + resp, err := s.client.Do(ctx, req, &keys) + if err != nil { + return nil, resp, err + } + + return keys, resp, nil +} + +// GetKey fetches a single deploy key. +// +// GitHub API docs: https://developer.github.com/v3/repos/keys/#get +func (s *RepositoriesService) GetKey(ctx context.Context, owner string, repo string, id int64) (*Key, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + key := new(Key) + resp, err := s.client.Do(ctx, req, key) + if err != nil { + return nil, resp, err + } + + return key, resp, nil +} + +// CreateKey adds a deploy key for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/keys/#create +func (s *RepositoriesService) CreateKey(ctx context.Context, owner string, repo string, key *Key) (*Key, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) + + req, err := s.client.NewRequest("POST", u, key) + if err != nil { + return nil, nil, err + } + + k := new(Key) + resp, err := s.client.Do(ctx, req, k) + if err != nil { + return nil, resp, err + } + + return k, resp, nil +} + +// EditKey edits a deploy key. +// +// GitHub API docs: https://developer.github.com/v3/repos/keys/#edit +func (s *RepositoriesService) EditKey(ctx context.Context, owner string, repo string, id int, key *Key) (*Key, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) + + req, err := s.client.NewRequest("PATCH", u, key) + if err != nil { + return nil, nil, err + } + + k := new(Key) + resp, err := s.client.Do(ctx, req, k) + if err != nil { + return nil, resp, err + } + + return k, resp, nil +} + +// DeleteKey deletes a deploy key. +// +// GitHub API docs: https://developer.github.com/v3/repos/keys/#delete +func (s *RepositoriesService) DeleteKey(ctx context.Context, owner string, repo string, id int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/repos_keys_test.go b/vendor/github.com/google/go-github/github/repos_keys_test.go new file mode 100644 index 00000000..75a17356 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_keys_test.go @@ -0,0 +1,169 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListKeys(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + keys, _, err := client.Repositories.ListKeys(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListKeys returned error: %v", err) + } + + want := []*Key{{ID: Int64(1)}} + if !reflect.DeepEqual(keys, want) { + t.Errorf("Repositories.ListKeys returned %+v, want %+v", keys, want) + } +} + +func TestRepositoriesService_ListKeys_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListKeys(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_GetKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + key, _, err := client.Repositories.GetKey(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetKey returned error: %v", err) + } + + want := &Key{ID: Int64(1)} + if !reflect.DeepEqual(key, want) { + t.Errorf("Repositories.GetKey returned %+v, want %+v", key, want) + } +} + +func TestRepositoriesService_GetKey_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.GetKey(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} + +func TestRepositoriesService_CreateKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Key{Key: String("k"), Title: String("t")} + + mux.HandleFunc("/repos/o/r/keys", func(w http.ResponseWriter, r *http.Request) { + v := new(Key) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + key, _, err := client.Repositories.CreateKey(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.GetKey returned error: %v", err) + } + + want := &Key{ID: Int64(1)} + if !reflect.DeepEqual(key, want) { + t.Errorf("Repositories.GetKey returned %+v, want %+v", key, want) + } +} + +func TestRepositoriesService_CreateKey_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.CreateKey(context.Background(), "%", "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_EditKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Key{Key: String("k"), Title: String("t")} + + mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { + v := new(Key) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + key, _, err := client.Repositories.EditKey(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Repositories.EditKey returned error: %v", err) + } + + want := &Key{ID: Int64(1)} + if !reflect.DeepEqual(key, want) { + t.Errorf("Repositories.EditKey returned %+v, want %+v", key, want) + } +} + +func TestRepositoriesService_EditKey_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.EditKey(context.Background(), "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_DeleteKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Repositories.DeleteKey(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteKey returned error: %v", err) + } +} + +func TestRepositoriesService_DeleteKey_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Repositories.DeleteKey(context.Background(), "%", "%", 1) + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/repos_merging.go b/vendor/github.com/google/go-github/github/repos_merging.go new file mode 100644 index 00000000..04383c1a --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_merging.go @@ -0,0 +1,38 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RepositoryMergeRequest represents a request to merge a branch in a +// repository. +type RepositoryMergeRequest struct { + Base *string `json:"base,omitempty"` + Head *string `json:"head,omitempty"` + CommitMessage *string `json:"commit_message,omitempty"` +} + +// Merge a branch in the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/merging/#perform-a-merge +func (s *RepositoriesService) Merge(ctx context.Context, owner, repo string, request *RepositoryMergeRequest) (*RepositoryCommit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/merges", owner, repo) + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + commit := new(RepositoryCommit) + resp, err := s.client.Do(ctx, req, commit) + if err != nil { + return nil, resp, err + } + + return commit, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_merging_test.go b/vendor/github.com/google/go-github/github/repos_merging_test.go new file mode 100644 index 00000000..086f24bb --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_merging_test.go @@ -0,0 +1,48 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_Merge(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &RepositoryMergeRequest{ + Base: String("b"), + Head: String("h"), + CommitMessage: String("c"), + } + + mux.HandleFunc("/repos/o/r/merges", func(w http.ResponseWriter, r *http.Request) { + v := new(RepositoryMergeRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"sha":"s"}`) + }) + + commit, _, err := client.Repositories.Merge(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.Merge returned error: %v", err) + } + + want := &RepositoryCommit{SHA: String("s")} + if !reflect.DeepEqual(commit, want) { + t.Errorf("Repositories.Merge returned %+v, want %+v", commit, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_pages.go b/vendor/github.com/google/go-github/github/repos_pages.go new file mode 100644 index 00000000..94a95f2b --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_pages.go @@ -0,0 +1,143 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Pages represents a GitHub Pages site configuration. +type Pages struct { + URL *string `json:"url,omitempty"` + Status *string `json:"status,omitempty"` + CNAME *string `json:"cname,omitempty"` + Custom404 *bool `json:"custom_404,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +// PagesError represents a build error for a GitHub Pages site. +type PagesError struct { + Message *string `json:"message,omitempty"` +} + +// PagesBuild represents the build information for a GitHub Pages site. +type PagesBuild struct { + URL *string `json:"url,omitempty"` + Status *string `json:"status,omitempty"` + Error *PagesError `json:"error,omitempty"` + Pusher *User `json:"pusher,omitempty"` + Commit *string `json:"commit,omitempty"` + Duration *int `json:"duration,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// GetPagesInfo fetches information about a GitHub Pages site. +// +// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site +func (s *RepositoriesService) GetPagesInfo(ctx context.Context, owner, repo string) (*Pages, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypePagesPreview) + + site := new(Pages) + resp, err := s.client.Do(ctx, req, site) + if err != nil { + return nil, resp, err + } + + return site, resp, nil +} + +// ListPagesBuilds lists the builds for a GitHub Pages site. +// +// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds +func (s *RepositoriesService) ListPagesBuilds(ctx context.Context, owner, repo string, opt *ListOptions) ([]*PagesBuild, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pages []*PagesBuild + resp, err := s.client.Do(ctx, req, &pages) + if err != nil { + return nil, resp, err + } + + return pages, resp, nil +} + +// GetLatestPagesBuild fetches the latest build information for a GitHub pages site. +// +// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build +func (s *RepositoriesService) GetLatestPagesBuild(ctx context.Context, owner, repo string) (*PagesBuild, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + build := new(PagesBuild) + resp, err := s.client.Do(ctx, req, build) + if err != nil { + return nil, resp, err + } + + return build, resp, nil +} + +// GetPageBuild fetches the specific build information for a GitHub pages site. +// +// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build +func (s *RepositoriesService) GetPageBuild(ctx context.Context, owner, repo string, id int64) (*PagesBuild, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages/builds/%v", owner, repo, id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + build := new(PagesBuild) + resp, err := s.client.Do(ctx, req, build) + if err != nil { + return nil, resp, err + } + + return build, resp, nil +} + +// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit. +// +// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build +func (s *RepositoriesService) RequestPageBuild(ctx context.Context, owner, repo string) (*PagesBuild, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypePagesPreview) + + build := new(PagesBuild) + resp, err := s.client.Do(ctx, req, build) + if err != nil { + return nil, resp, err + } + + return build, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_pages_test.go b/vendor/github.com/google/go-github/github/repos_pages_test.go new file mode 100644 index 00000000..21a00f9d --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_pages_test.go @@ -0,0 +1,134 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_GetPagesInfo(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypePagesPreview) + fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h"}`) + }) + + page, _, err := client.Repositories.GetPagesInfo(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.GetPagesInfo returned error: %v", err) + } + + want := &Pages{URL: String("u"), Status: String("s"), CNAME: String("c"), Custom404: Bool(false), HTMLURL: String("h")} + if !reflect.DeepEqual(page, want) { + t.Errorf("Repositories.GetPagesInfo returned %+v, want %+v", page, want) + } +} + +func TestRepositoriesService_ListPagesBuilds(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"url":"u","status":"s","commit":"c"}]`) + }) + + pages, _, err := client.Repositories.ListPagesBuilds(context.Background(), "o", "r", nil) + if err != nil { + t.Errorf("Repositories.ListPagesBuilds returned error: %v", err) + } + + want := []*PagesBuild{{URL: String("u"), Status: String("s"), Commit: String("c")}} + if !reflect.DeepEqual(pages, want) { + t.Errorf("Repositories.ListPagesBuilds returned %+v, want %+v", pages, want) + } +} + +func TestRepositoriesService_ListPagesBuilds_withOptions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `[]`) + }) + + _, _, err := client.Repositories.ListPagesBuilds(context.Background(), "o", "r", &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Repositories.ListPagesBuilds returned error: %v", err) + } +} + +func TestRepositoriesService_GetLatestPagesBuild(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pages/builds/latest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"url":"u","status":"s","commit":"c"}`) + }) + + build, _, err := client.Repositories.GetLatestPagesBuild(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.GetLatestPagesBuild returned error: %v", err) + } + + want := &PagesBuild{URL: String("u"), Status: String("s"), Commit: String("c")} + if !reflect.DeepEqual(build, want) { + t.Errorf("Repositories.GetLatestPagesBuild returned %+v, want %+v", build, want) + } +} + +func TestRepositoriesService_GetPageBuild(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pages/builds/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"url":"u","status":"s","commit":"c"}`) + }) + + build, _, err := client.Repositories.GetPageBuild(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetPageBuild returned error: %v", err) + } + + want := &PagesBuild{URL: String("u"), Status: String("s"), Commit: String("c")} + if !reflect.DeepEqual(build, want) { + t.Errorf("Repositories.GetPageBuild returned %+v, want %+v", build, want) + } +} + +func TestRepositoriesService_RequestPageBuild(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypePagesPreview) + fmt.Fprint(w, `{"url":"u","status":"s"}`) + }) + + build, _, err := client.Repositories.RequestPageBuild(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.RequestPageBuild returned error: %v", err) + } + + want := &PagesBuild{URL: String("u"), Status: String("s")} + if !reflect.DeepEqual(build, want) { + t.Errorf("Repositories.RequestPageBuild returned %+v, want %+v", build, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_projects.go b/vendor/github.com/google/go-github/github/repos_projects.go new file mode 100644 index 00000000..770ffc76 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_projects.go @@ -0,0 +1,69 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ProjectListOptions specifies the optional parameters to the +// OrganizationsService.ListProjects and RepositoriesService.ListProjects methods. +type ProjectListOptions struct { + // Indicates the state of the projects to return. Can be either open, closed, or all. Default: open + State string `url:"state,omitempty"` + + ListOptions +} + +// ListProjects lists the projects for a repo. +// +// GitHub API docs: https://developer.github.com/v3/projects/#list-repository-projects +func (s *RepositoriesService) ListProjects(ctx context.Context, owner, repo string, opt *ProjectListOptions) ([]*Project, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + var projects []*Project + resp, err := s.client.Do(ctx, req, &projects) + if err != nil { + return nil, resp, err + } + + return projects, resp, nil +} + +// CreateProject creates a GitHub Project for the specified repository. +// +// GitHub API docs: https://developer.github.com/v3/projects/#create-a-repository-project +func (s *RepositoriesService) CreateProject(ctx context.Context, owner, repo string, opt *ProjectOptions) (*Project, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) + req, err := s.client.NewRequest("POST", u, opt) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeProjectsPreview) + + project := &Project{} + resp, err := s.client.Do(ctx, req, project) + if err != nil { + return nil, resp, err + } + + return project, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_projects_test.go b/vendor/github.com/google/go-github/github/repos_projects_test.go new file mode 100644 index 00000000..c0392254 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_projects_test.go @@ -0,0 +1,68 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListProjects(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/projects", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ProjectListOptions{ListOptions: ListOptions{Page: 2}} + projects, _, err := client.Repositories.ListProjects(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListProjects returned error: %v", err) + } + + want := []*Project{{ID: Int64(1)}} + if !reflect.DeepEqual(projects, want) { + t.Errorf("Repositories.ListProjects returned %+v, want %+v", projects, want) + } +} + +func TestRepositoriesService_CreateProject(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProjectOptions{Name: "Project Name", Body: "Project body."} + + mux.HandleFunc("/repos/o/r/projects", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + + v := &ProjectOptions{} + json.NewDecoder(r.Body).Decode(v) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + project, _, err := client.Repositories.CreateProject(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.CreateProject returned error: %v", err) + } + + want := &Project{ID: Int64(1)} + if !reflect.DeepEqual(project, want) { + t.Errorf("Repositories.CreateProject returned %+v, want %+v", project, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_releases.go b/vendor/github.com/google/go-github/github/repos_releases.go new file mode 100644 index 00000000..7ad2b278 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_releases.go @@ -0,0 +1,327 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" + "fmt" + "io" + "mime" + "net/http" + "os" + "path/filepath" + "strings" +) + +// RepositoryRelease represents a GitHub release in a repository. +type RepositoryRelease struct { + ID *int64 `json:"id,omitempty"` + TagName *string `json:"tag_name,omitempty"` + TargetCommitish *string `json:"target_commitish,omitempty"` + Name *string `json:"name,omitempty"` + Body *string `json:"body,omitempty"` + Draft *bool `json:"draft,omitempty"` + Prerelease *bool `json:"prerelease,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + PublishedAt *Timestamp `json:"published_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + AssetsURL *string `json:"assets_url,omitempty"` + Assets []ReleaseAsset `json:"assets,omitempty"` + UploadURL *string `json:"upload_url,omitempty"` + ZipballURL *string `json:"zipball_url,omitempty"` + TarballURL *string `json:"tarball_url,omitempty"` + Author *User `json:"author,omitempty"` +} + +func (r RepositoryRelease) String() string { + return Stringify(r) +} + +// ReleaseAsset represents a GitHub release asset in a repository. +type ReleaseAsset struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + Name *string `json:"name,omitempty"` + Label *string `json:"label,omitempty"` + State *string `json:"state,omitempty"` + ContentType *string `json:"content_type,omitempty"` + Size *int `json:"size,omitempty"` + DownloadCount *int `json:"download_count,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + BrowserDownloadURL *string `json:"browser_download_url,omitempty"` + Uploader *User `json:"uploader,omitempty"` +} + +func (r ReleaseAsset) String() string { + return Stringify(r) +} + +// ListReleases lists the releases for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository +func (s *RepositoriesService) ListReleases(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var releases []*RepositoryRelease + resp, err := s.client.Do(ctx, req, &releases) + if err != nil { + return nil, resp, err + } + return releases, resp, nil +} + +// GetRelease fetches a single release. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release +func (s *RepositoriesService) GetRelease(ctx context.Context, owner, repo string, id int64) (*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) + return s.getSingleRelease(ctx, u) +} + +// GetLatestRelease fetches the latest published release for the repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-the-latest-release +func (s *RepositoriesService) GetLatestRelease(ctx context.Context, owner, repo string) (*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/latest", owner, repo) + return s.getSingleRelease(ctx, u) +} + +// GetReleaseByTag fetches a release with the specified tag. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name +func (s *RepositoriesService) GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/tags/%s", owner, repo, tag) + return s.getSingleRelease(ctx, u) +} + +func (s *RepositoriesService) getSingleRelease(ctx context.Context, url string) (*RepositoryRelease, *Response, error) { + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + release := new(RepositoryRelease) + resp, err := s.client.Do(ctx, req, release) + if err != nil { + return nil, resp, err + } + return release, resp, nil +} + +// CreateRelease adds a new release for a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#create-a-release +func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo string, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) + + req, err := s.client.NewRequest("POST", u, release) + if err != nil { + return nil, nil, err + } + + r := new(RepositoryRelease) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + return r, resp, nil +} + +// EditRelease edits a repository release. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release +func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo string, id int64, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) + + req, err := s.client.NewRequest("PATCH", u, release) + if err != nil { + return nil, nil, err + } + + r := new(RepositoryRelease) + resp, err := s.client.Do(ctx, req, r) + if err != nil { + return nil, resp, err + } + return r, resp, nil +} + +// DeleteRelease delete a single release from a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release +func (s *RepositoriesService) DeleteRelease(ctx context.Context, owner, repo string, id int64) (*Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// ListReleaseAssets lists the release's assets. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#list-assets-for-a-release +func (s *RepositoriesService) ListReleaseAssets(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*ReleaseAsset, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var assets []*ReleaseAsset + resp, err := s.client.Do(ctx, req, &assets) + if err != nil { + return nil, resp, err + } + return assets, resp, nil +} + +// GetReleaseAsset fetches a single release asset. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset +func (s *RepositoriesService) GetReleaseAsset(ctx context.Context, owner, repo string, id int64) (*ReleaseAsset, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + asset := new(ReleaseAsset) + resp, err := s.client.Do(ctx, req, asset) + if err != nil { + return nil, resp, err + } + return asset, resp, nil +} + +// DownloadReleaseAsset downloads a release asset or returns a redirect URL. +// +// DownloadReleaseAsset returns an io.ReadCloser that reads the contents of the +// specified release asset. It is the caller's responsibility to close the ReadCloser. +// If a redirect is returned, the redirect URL will be returned as a string instead +// of the io.ReadCloser. Exactly one of rc and redirectURL will be zero. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset +func (s *RepositoriesService) DownloadReleaseAsset(ctx context.Context, owner, repo string, id int64) (rc io.ReadCloser, redirectURL string, err error) { + u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, "", err + } + req.Header.Set("Accept", defaultMediaType) + + s.client.clientMu.Lock() + defer s.client.clientMu.Unlock() + + var loc string + saveRedirect := s.client.client.CheckRedirect + s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + loc = req.URL.String() + return errors.New("disable redirect") + } + defer func() { s.client.client.CheckRedirect = saveRedirect }() + + req = withContext(ctx, req) + resp, err := s.client.client.Do(req) + if err != nil { + if !strings.Contains(err.Error(), "disable redirect") { + return nil, "", err + } + return nil, loc, nil // Intentionally return no error with valid redirect URL. + } + + if err := CheckResponse(resp); err != nil { + resp.Body.Close() + return nil, "", err + } + + return resp.Body, "", nil +} + +// EditReleaseAsset edits a repository release asset. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release-asset +func (s *RepositoriesService) EditReleaseAsset(ctx context.Context, owner, repo string, id int64, release *ReleaseAsset) (*ReleaseAsset, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) + + req, err := s.client.NewRequest("PATCH", u, release) + if err != nil { + return nil, nil, err + } + + asset := new(ReleaseAsset) + resp, err := s.client.Do(ctx, req, asset) + if err != nil { + return nil, resp, err + } + return asset, resp, nil +} + +// DeleteReleaseAsset delete a single release asset from a repository. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release-asset +func (s *RepositoriesService) DeleteReleaseAsset(ctx context.Context, owner, repo string, id int64) (*Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// UploadReleaseAsset creates an asset by uploading a file into a release repository. +// To upload assets that cannot be represented by an os.File, call NewUploadRequest directly. +// +// GitHub API docs: https://developer.github.com/v3/repos/releases/#upload-a-release-asset +func (s *RepositoriesService) UploadReleaseAsset(ctx context.Context, owner, repo string, id int64, opt *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) { + u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + stat, err := file.Stat() + if err != nil { + return nil, nil, err + } + if stat.IsDir() { + return nil, nil, errors.New("the asset to upload can't be a directory") + } + + mediaType := mime.TypeByExtension(filepath.Ext(file.Name())) + req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType) + if err != nil { + return nil, nil, err + } + + asset := new(ReleaseAsset) + resp, err := s.client.Do(ctx, req, asset) + if err != nil { + return nil, resp, err + } + return asset, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_releases_test.go b/vendor/github.com/google/go-github/github/repos_releases_test.go new file mode 100644 index 00000000..126a4284 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_releases_test.go @@ -0,0 +1,353 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "reflect" + "strings" + "testing" +) + +func TestRepositoriesService_ListReleases(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + releases, _, err := client.Repositories.ListReleases(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListReleases returned error: %v", err) + } + want := []*RepositoryRelease{{ID: Int64(1)}} + if !reflect.DeepEqual(releases, want) { + t.Errorf("Repositories.ListReleases returned %+v, want %+v", releases, want) + } +} + +func TestRepositoriesService_GetRelease(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1,"author":{"login":"l"}}`) + }) + + release, resp, err := client.Repositories.GetRelease(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetRelease returned error: %v\n%v", err, resp.Body) + } + + want := &RepositoryRelease{ID: Int64(1), Author: &User{Login: String("l")}} + if !reflect.DeepEqual(release, want) { + t.Errorf("Repositories.GetRelease returned %+v, want %+v", release, want) + } +} + +func TestRepositoriesService_GetLatestRelease(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/latest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":3}`) + }) + + release, resp, err := client.Repositories.GetLatestRelease(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.GetLatestRelease returned error: %v\n%v", err, resp.Body) + } + + want := &RepositoryRelease{ID: Int64(3)} + if !reflect.DeepEqual(release, want) { + t.Errorf("Repositories.GetLatestRelease returned %+v, want %+v", release, want) + } +} + +func TestRepositoriesService_GetReleaseByTag(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/tags/foo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":13}`) + }) + + release, resp, err := client.Repositories.GetReleaseByTag(context.Background(), "o", "r", "foo") + if err != nil { + t.Errorf("Repositories.GetReleaseByTag returned error: %v\n%v", err, resp.Body) + } + + want := &RepositoryRelease{ID: Int64(13)} + if !reflect.DeepEqual(release, want) { + t.Errorf("Repositories.GetReleaseByTag returned %+v, want %+v", release, want) + } +} + +func TestRepositoriesService_CreateRelease(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &RepositoryRelease{Name: String("v1.0")} + + mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) { + v := new(RepositoryRelease) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `{"id":1}`) + }) + + release, _, err := client.Repositories.CreateRelease(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.CreateRelease returned error: %v", err) + } + + want := &RepositoryRelease{ID: Int64(1)} + if !reflect.DeepEqual(release, want) { + t.Errorf("Repositories.CreateRelease returned %+v, want %+v", release, want) + } +} + +func TestRepositoriesService_EditRelease(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &RepositoryRelease{Name: String("n")} + + mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { + v := new(RepositoryRelease) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `{"id":1}`) + }) + + release, _, err := client.Repositories.EditRelease(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Repositories.EditRelease returned error: %v", err) + } + want := &RepositoryRelease{ID: Int64(1)} + if !reflect.DeepEqual(release, want) { + t.Errorf("Repositories.EditRelease returned = %+v, want %+v", release, want) + } +} + +func TestRepositoriesService_DeleteRelease(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Repositories.DeleteRelease(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteRelease returned error: %v", err) + } +} + +func TestRepositoriesService_ListReleaseAssets(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + assets, _, err := client.Repositories.ListReleaseAssets(context.Background(), "o", "r", 1, opt) + if err != nil { + t.Errorf("Repositories.ListReleaseAssets returned error: %v", err) + } + want := []*ReleaseAsset{{ID: Int64(1)}} + if !reflect.DeepEqual(assets, want) { + t.Errorf("Repositories.ListReleaseAssets returned %+v, want %+v", assets, want) + } +} + +func TestRepositoriesService_GetReleaseAsset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + asset, _, err := client.Repositories.GetReleaseAsset(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetReleaseAsset returned error: %v", err) + } + want := &ReleaseAsset{ID: Int64(1)} + if !reflect.DeepEqual(asset, want) { + t.Errorf("Repositories.GetReleaseAsset returned %+v, want %+v", asset, want) + } +} + +func TestRepositoriesService_DownloadReleaseAsset_Stream(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment; filename=hello-world.txt") + fmt.Fprint(w, "Hello World") + }) + + reader, _, err := client.Repositories.DownloadReleaseAsset(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) + } + want := []byte("Hello World") + content, err := ioutil.ReadAll(reader) + if err != nil { + t.Errorf("Repositories.DownloadReleaseAsset returned bad reader: %v", err) + } + if !bytes.Equal(want, content) { + t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", content, want) + } +} + +func TestRepositoriesService_DownloadReleaseAsset_Redirect(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + http.Redirect(w, r, "/yo", http.StatusFound) + }) + + _, got, err := client.Repositories.DownloadReleaseAsset(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) + } + want := "/yo" + if !strings.HasSuffix(got, want) { + t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_DownloadReleaseAsset_APIError(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + w.WriteHeader(http.StatusNotFound) + fmt.Fprint(w, `{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}`) + }) + + resp, loc, err := client.Repositories.DownloadReleaseAsset(context.Background(), "o", "r", 1) + if err == nil { + t.Error("Repositories.DownloadReleaseAsset did not return an error") + } + + if resp != nil { + resp.Close() + t.Error("Repositories.DownloadReleaseAsset returned stream, want nil") + } + + if loc != "" { + t.Errorf(`Repositories.DownloadReleaseAsset returned "%s", want empty ""`, loc) + } +} + +func TestRepositoriesService_EditReleaseAsset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ReleaseAsset{Name: String("n")} + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + v := new(ReleaseAsset) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `{"id":1}`) + }) + + asset, _, err := client.Repositories.EditReleaseAsset(context.Background(), "o", "r", 1, input) + if err != nil { + t.Errorf("Repositories.EditReleaseAsset returned error: %v", err) + } + want := &ReleaseAsset{ID: Int64(1)} + if !reflect.DeepEqual(asset, want) { + t.Errorf("Repositories.EditReleaseAsset returned = %+v, want %+v", asset, want) + } +} + +func TestRepositoriesService_DeleteReleaseAsset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Repositories.DeleteReleaseAsset(context.Background(), "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteReleaseAsset returned error: %v", err) + } +} + +func TestRepositoriesService_UploadReleaseAsset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "text/plain; charset=utf-8") + testHeader(t, r, "Content-Length", "12") + testFormValues(t, r, values{"name": "n"}) + testBody(t, r, "Upload me !\n") + + fmt.Fprintf(w, `{"id":1}`) + }) + + file, dir, err := openTestFile("upload.txt", "Upload me !\n") + if err != nil { + t.Fatalf("Unable to create temp file: %v", err) + } + defer os.RemoveAll(dir) + + opt := &UploadOptions{Name: "n"} + asset, _, err := client.Repositories.UploadReleaseAsset(context.Background(), "o", "r", 1, opt, file) + if err != nil { + t.Errorf("Repositories.UploadReleaseAssert returned error: %v", err) + } + want := &ReleaseAsset{ID: Int64(1)} + if !reflect.DeepEqual(asset, want) { + t.Errorf("Repositories.UploadReleaseAssert returned %+v, want %+v", asset, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_stats.go b/vendor/github.com/google/go-github/github/repos_stats.go new file mode 100644 index 00000000..bb355aea --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_stats.go @@ -0,0 +1,226 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// ContributorStats represents a contributor to a repository and their +// weekly contributions to a given repo. +type ContributorStats struct { + Author *Contributor `json:"author,omitempty"` + Total *int `json:"total,omitempty"` + Weeks []WeeklyStats `json:"weeks,omitempty"` +} + +func (c ContributorStats) String() string { + return Stringify(c) +} + +// WeeklyStats represents the number of additions, deletions and commits +// a Contributor made in a given week. +type WeeklyStats struct { + Week *Timestamp `json:"w,omitempty"` + Additions *int `json:"a,omitempty"` + Deletions *int `json:"d,omitempty"` + Commits *int `json:"c,omitempty"` +} + +func (w WeeklyStats) String() string { + return Stringify(w) +} + +// ListContributorsStats gets a repo's contributor list with additions, +// deletions and commit counts. +// +// If this is the first time these statistics are requested for the given +// repository, this method will return an *AcceptedError and a status code of +// 202. This is because this is the status that GitHub returns to signify that +// it is now computing the requested statistics. A follow up request, after a +// delay of a second or so, should result in a successful request. +// +// GitHub API docs: https://developer.github.com/v3/repos/statistics/#contributors +func (s *RepositoriesService) ListContributorsStats(ctx context.Context, owner, repo string) ([]*ContributorStats, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/stats/contributors", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var contributorStats []*ContributorStats + resp, err := s.client.Do(ctx, req, &contributorStats) + if err != nil { + return nil, resp, err + } + + return contributorStats, resp, nil +} + +// WeeklyCommitActivity represents the weekly commit activity for a repository. +// The days array is a group of commits per day, starting on Sunday. +type WeeklyCommitActivity struct { + Days []int `json:"days,omitempty"` + Total *int `json:"total,omitempty"` + Week *Timestamp `json:"week,omitempty"` +} + +func (w WeeklyCommitActivity) String() string { + return Stringify(w) +} + +// ListCommitActivity returns the last year of commit activity +// grouped by week. The days array is a group of commits per day, +// starting on Sunday. +// +// If this is the first time these statistics are requested for the given +// repository, this method will return an *AcceptedError and a status code of +// 202. This is because this is the status that GitHub returns to signify that +// it is now computing the requested statistics. A follow up request, after a +// delay of a second or so, should result in a successful request. +// +// GitHub API docs: https://developer.github.com/v3/repos/statistics/#commit-activity +func (s *RepositoriesService) ListCommitActivity(ctx context.Context, owner, repo string) ([]*WeeklyCommitActivity, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/stats/commit_activity", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var weeklyCommitActivity []*WeeklyCommitActivity + resp, err := s.client.Do(ctx, req, &weeklyCommitActivity) + if err != nil { + return nil, resp, err + } + + return weeklyCommitActivity, resp, nil +} + +// ListCodeFrequency returns a weekly aggregate of the number of additions and +// deletions pushed to a repository. Returned WeeklyStats will contain +// additions and deletions, but not total commits. +// +// If this is the first time these statistics are requested for the given +// repository, this method will return an *AcceptedError and a status code of +// 202. This is because this is the status that GitHub returns to signify that +// it is now computing the requested statistics. A follow up request, after a +// delay of a second or so, should result in a successful request. +// +// GitHub API docs: https://developer.github.com/v3/repos/statistics/#code-frequency +func (s *RepositoriesService) ListCodeFrequency(ctx context.Context, owner, repo string) ([]*WeeklyStats, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/stats/code_frequency", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var weeks [][]int + resp, err := s.client.Do(ctx, req, &weeks) + + // convert int slices into WeeklyStats + var stats []*WeeklyStats + for _, week := range weeks { + if len(week) != 3 { + continue + } + stat := &WeeklyStats{ + Week: &Timestamp{time.Unix(int64(week[0]), 0)}, + Additions: Int(week[1]), + Deletions: Int(week[2]), + } + stats = append(stats, stat) + } + + return stats, resp, err +} + +// RepositoryParticipation is the number of commits by everyone +// who has contributed to the repository (including the owner) +// as well as the number of commits by the owner themself. +type RepositoryParticipation struct { + All []int `json:"all,omitempty"` + Owner []int `json:"owner,omitempty"` +} + +func (r RepositoryParticipation) String() string { + return Stringify(r) +} + +// ListParticipation returns the total commit counts for the 'owner' +// and total commit counts in 'all'. 'all' is everyone combined, +// including the 'owner' in the last 52 weeks. If you’d like to get +// the commit counts for non-owners, you can subtract 'all' from 'owner'. +// +// The array order is oldest week (index 0) to most recent week. +// +// If this is the first time these statistics are requested for the given +// repository, this method will return an *AcceptedError and a status code of +// 202. This is because this is the status that GitHub returns to signify that +// it is now computing the requested statistics. A follow up request, after a +// delay of a second or so, should result in a successful request. +// +// GitHub API docs: https://developer.github.com/v3/repos/statistics/#participation +func (s *RepositoriesService) ListParticipation(ctx context.Context, owner, repo string) (*RepositoryParticipation, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/stats/participation", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + participation := new(RepositoryParticipation) + resp, err := s.client.Do(ctx, req, participation) + if err != nil { + return nil, resp, err + } + + return participation, resp, nil +} + +// PunchCard represents the number of commits made during a given hour of a +// day of the week. +type PunchCard struct { + Day *int // Day of the week (0-6: =Sunday - Saturday). + Hour *int // Hour of day (0-23). + Commits *int // Number of commits. +} + +// ListPunchCard returns the number of commits per hour in each day. +// +// If this is the first time these statistics are requested for the given +// repository, this method will return an *AcceptedError and a status code of +// 202. This is because this is the status that GitHub returns to signify that +// it is now computing the requested statistics. A follow up request, after a +// delay of a second or so, should result in a successful request. +// +// GitHub API docs: https://developer.github.com/v3/repos/statistics/#punch-card +func (s *RepositoriesService) ListPunchCard(ctx context.Context, owner, repo string) ([]*PunchCard, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/stats/punch_card", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var results [][]int + resp, err := s.client.Do(ctx, req, &results) + + // convert int slices into Punchcards + var cards []*PunchCard + for _, result := range results { + if len(result) != 3 { + continue + } + card := &PunchCard{ + Day: Int(result[0]), + Hour: Int(result[1]), + Commits: Int(result[2]), + } + cards = append(cards, card) + } + + return cards, resp, err +} diff --git a/vendor/github.com/google/go-github/github/repos_stats_test.go b/vendor/github.com/google/go-github/github/repos_stats_test.go new file mode 100644 index 00000000..875acfc7 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_stats_test.go @@ -0,0 +1,211 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestRepositoriesService_ListContributorsStats(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, ` +[ + { + "author": { + "id": 1 + }, + "total": 135, + "weeks": [ + { + "w": 1367712000, + "a": 6898, + "d": 77, + "c": 10 + } + ] + } +] +`) + }) + + stats, _, err := client.Repositories.ListContributorsStats(context.Background(), "o", "r") + if err != nil { + t.Errorf("RepositoriesService.ListContributorsStats returned error: %v", err) + } + + want := []*ContributorStats{ + { + Author: &Contributor{ + ID: Int64(1), + }, + Total: Int(135), + Weeks: []WeeklyStats{ + { + Week: &Timestamp{time.Date(2013, 05, 05, 00, 00, 00, 0, time.UTC).Local()}, + Additions: Int(6898), + Deletions: Int(77), + Commits: Int(10), + }, + }, + }, + } + + if !reflect.DeepEqual(stats, want) { + t.Errorf("RepositoriesService.ListContributorsStats returned %+v, want %+v", stats, want) + } +} + +func TestRepositoriesService_ListCommitActivity(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/stats/commit_activity", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, ` +[ + { + "days": [0, 3, 26, 20, 39, 1, 0], + "total": 89, + "week": 1336280400 + } +] +`) + }) + + activity, _, err := client.Repositories.ListCommitActivity(context.Background(), "o", "r") + if err != nil { + t.Errorf("RepositoriesService.ListCommitActivity returned error: %v", err) + } + + want := []*WeeklyCommitActivity{ + { + Days: []int{0, 3, 26, 20, 39, 1, 0}, + Total: Int(89), + Week: &Timestamp{time.Date(2012, 05, 06, 05, 00, 00, 0, time.UTC).Local()}, + }, + } + + if !reflect.DeepEqual(activity, want) { + t.Errorf("RepositoriesService.ListCommitActivity returned %+v, want %+v", activity, want) + } +} + +func TestRepositoriesService_ListCodeFrequency(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/stats/code_frequency", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `[[1302998400, 1124, -435]]`) + }) + + code, _, err := client.Repositories.ListCodeFrequency(context.Background(), "o", "r") + if err != nil { + t.Errorf("RepositoriesService.ListCodeFrequency returned error: %v", err) + } + + want := []*WeeklyStats{{ + Week: &Timestamp{time.Date(2011, 04, 17, 00, 00, 00, 0, time.UTC).Local()}, + Additions: Int(1124), + Deletions: Int(-435), + }} + + if !reflect.DeepEqual(code, want) { + t.Errorf("RepositoriesService.ListCodeFrequency returned %+v, want %+v", code, want) + } +} + +func TestRepositoriesService_Participation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/stats/participation", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, ` +{ + "all": [ + 11,21,15,2,8,1,8,23,17,21,11,10,33, + 91,38,34,22,23,32,3,43,87,71,18,13,5, + 13,16,66,27,12,45,110,117,13,8,18,9,19, + 26,39,12,20,31,46,91,45,10,24,9,29,7 + ], + "owner": [ + 3,2,3,0,2,0,5,14,7,9,1,5,0, + 48,19,2,0,1,10,2,23,40,35,8,8,2, + 10,6,30,0,2,9,53,104,3,3,10,4,7, + 11,21,4,4,22,26,63,11,2,14,1,10,3 + ] +} +`) + }) + + participation, _, err := client.Repositories.ListParticipation(context.Background(), "o", "r") + if err != nil { + t.Errorf("RepositoriesService.ListParticipation returned error: %v", err) + } + + want := &RepositoryParticipation{ + All: []int{ + 11, 21, 15, 2, 8, 1, 8, 23, 17, 21, 11, 10, 33, + 91, 38, 34, 22, 23, 32, 3, 43, 87, 71, 18, 13, 5, + 13, 16, 66, 27, 12, 45, 110, 117, 13, 8, 18, 9, 19, + 26, 39, 12, 20, 31, 46, 91, 45, 10, 24, 9, 29, 7, + }, + Owner: []int{ + 3, 2, 3, 0, 2, 0, 5, 14, 7, 9, 1, 5, 0, + 48, 19, 2, 0, 1, 10, 2, 23, 40, 35, 8, 8, 2, + 10, 6, 30, 0, 2, 9, 53, 104, 3, 3, 10, 4, 7, + 11, 21, 4, 4, 22, 26, 63, 11, 2, 14, 1, 10, 3, + }, + } + + if !reflect.DeepEqual(participation, want) { + t.Errorf("RepositoriesService.ListParticipation returned %+v, want %+v", participation, want) + } +} + +func TestRepositoriesService_ListPunchCard(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/stats/punch_card", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `[ + [0, 0, 5], + [0, 1, 43], + [0, 2, 21] + ]`) + }) + + card, _, err := client.Repositories.ListPunchCard(context.Background(), "o", "r") + if err != nil { + t.Errorf("RepositoriesService.ListPunchCard returned error: %v", err) + } + + want := []*PunchCard{ + {Day: Int(0), Hour: Int(0), Commits: Int(5)}, + {Day: Int(0), Hour: Int(1), Commits: Int(43)}, + {Day: Int(0), Hour: Int(2), Commits: Int(21)}, + } + + if !reflect.DeepEqual(card, want) { + t.Errorf("RepositoriesService.ListPunchCard returned %+v, want %+v", card, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_statuses.go b/vendor/github.com/google/go-github/github/repos_statuses.go new file mode 100644 index 00000000..f94fdc85 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_statuses.go @@ -0,0 +1,129 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// RepoStatus represents the status of a repository at a particular reference. +type RepoStatus struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + + // State is the current state of the repository. Possible values are: + // pending, success, error, or failure. + State *string `json:"state,omitempty"` + + // TargetURL is the URL of the page representing this status. It will be + // linked from the GitHub UI to allow users to see the source of the status. + TargetURL *string `json:"target_url,omitempty"` + + // Description is a short high level summary of the status. + Description *string `json:"description,omitempty"` + + // A string label to differentiate this status from the statuses of other systems. + Context *string `json:"context,omitempty"` + + Creator *User `json:"creator,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` +} + +func (r RepoStatus) String() string { + return Stringify(r) +} + +// ListStatuses lists the statuses of a repository at the specified +// reference. ref can be a SHA, a branch name, or a tag name. +// +// GitHub API docs: https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref +func (s *RepositoriesService) ListStatuses(ctx context.Context, owner, repo, ref string, opt *ListOptions) ([]*RepoStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/statuses", owner, repo, ref) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var statuses []*RepoStatus + resp, err := s.client.Do(ctx, req, &statuses) + if err != nil { + return nil, resp, err + } + + return statuses, resp, nil +} + +// CreateStatus creates a new status for a repository at the specified +// reference. Ref can be a SHA, a branch name, or a tag name. +// +// GitHub API docs: https://developer.github.com/v3/repos/statuses/#create-a-status +func (s *RepositoriesService) CreateStatus(ctx context.Context, owner, repo, ref string, status *RepoStatus) (*RepoStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/statuses/%v", owner, repo, ref) + req, err := s.client.NewRequest("POST", u, status) + if err != nil { + return nil, nil, err + } + + repoStatus := new(RepoStatus) + resp, err := s.client.Do(ctx, req, repoStatus) + if err != nil { + return nil, resp, err + } + + return repoStatus, resp, nil +} + +// CombinedStatus represents the combined status of a repository at a particular reference. +type CombinedStatus struct { + // State is the combined state of the repository. Possible values are: + // failure, pending, or success. + State *string `json:"state,omitempty"` + + Name *string `json:"name,omitempty"` + SHA *string `json:"sha,omitempty"` + TotalCount *int `json:"total_count,omitempty"` + Statuses []RepoStatus `json:"statuses,omitempty"` + + CommitURL *string `json:"commit_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` +} + +func (s CombinedStatus) String() string { + return Stringify(s) +} + +// GetCombinedStatus returns the combined status of a repository at the specified +// reference. ref can be a SHA, a branch name, or a tag name. +// +// GitHub API docs: https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref +func (s *RepositoriesService) GetCombinedStatus(ctx context.Context, owner, repo, ref string, opt *ListOptions) (*CombinedStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/status", owner, repo, ref) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + status := new(CombinedStatus) + resp, err := s.client.Do(ctx, req, status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_statuses_test.go b/vendor/github.com/google/go-github/github/repos_statuses_test.go new file mode 100644 index 00000000..b556b716 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_statuses_test.go @@ -0,0 +1,103 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListStatuses(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/commits/r/statuses", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + statuses, _, err := client.Repositories.ListStatuses(context.Background(), "o", "r", "r", opt) + if err != nil { + t.Errorf("Repositories.ListStatuses returned error: %v", err) + } + + want := []*RepoStatus{{ID: Int64(1)}} + if !reflect.DeepEqual(statuses, want) { + t.Errorf("Repositories.ListStatuses returned %+v, want %+v", statuses, want) + } +} + +func TestRepositoriesService_ListStatuses_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListStatuses(context.Background(), "%", "r", "r", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_CreateStatus(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &RepoStatus{State: String("s"), TargetURL: String("t"), Description: String("d")} + + mux.HandleFunc("/repos/o/r/statuses/r", func(w http.ResponseWriter, r *http.Request) { + v := new(RepoStatus) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `{"id":1}`) + }) + + status, _, err := client.Repositories.CreateStatus(context.Background(), "o", "r", "r", input) + if err != nil { + t.Errorf("Repositories.CreateStatus returned error: %v", err) + } + + want := &RepoStatus{ID: Int64(1)} + if !reflect.DeepEqual(status, want) { + t.Errorf("Repositories.CreateStatus returned %+v, want %+v", status, want) + } +} + +func TestRepositoriesService_CreateStatus_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.CreateStatus(context.Background(), "%", "r", "r", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_GetCombinedStatus(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/commits/r/status", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `{"state":"success", "statuses":[{"id":1}]}`) + }) + + opt := &ListOptions{Page: 2} + status, _, err := client.Repositories.GetCombinedStatus(context.Background(), "o", "r", "r", opt) + if err != nil { + t.Errorf("Repositories.GetCombinedStatus returned error: %v", err) + } + + want := &CombinedStatus{State: String("success"), Statuses: []RepoStatus{{ID: Int64(1)}}} + if !reflect.DeepEqual(status, want) { + t.Errorf("Repositories.GetCombinedStatus returned %+v, want %+v", status, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_test.go b/vendor/github.com/google/go-github/github/repos_test.go new file mode 100644 index 00000000..d553ccca --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_test.go @@ -0,0 +1,1091 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestRepositoriesService_List_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + repos, _, err := client.Repositories.List(context.Background(), "", nil) + if err != nil { + t.Errorf("Repositories.List returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Repositories.List returned %+v, want %+v", repos, want) + } +} + +func TestRepositoriesService_List_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + mux.HandleFunc("/users/u/repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + testFormValues(t, r, values{ + "visibility": "public", + "affiliation": "owner,collaborator", + "sort": "created", + "direction": "asc", + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &RepositoryListOptions{ + Visibility: "public", + Affiliation: "owner,collaborator", + Sort: "created", + Direction: "asc", + ListOptions: ListOptions{Page: 2}, + } + repos, _, err := client.Repositories.List(context.Background(), "u", opt) + if err != nil { + t.Errorf("Repositories.List returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Repositories.List returned %+v, want %+v", repos, want) + } +} + +func TestRepositoriesService_List_specifiedUser_type(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + mux.HandleFunc("/users/u/repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + testFormValues(t, r, values{ + "type": "owner", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &RepositoryListOptions{ + Type: "owner", + } + repos, _, err := client.Repositories.List(context.Background(), "u", opt) + if err != nil { + t.Errorf("Repositories.List returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Repositories.List returned %+v, want %+v", repos, want) + } +} + +func TestRepositoriesService_List_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.List(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_ListByOrg(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + mux.HandleFunc("/orgs/o/repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + testFormValues(t, r, values{ + "type": "forks", + "page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &RepositoryListByOrgOptions{"forks", ListOptions{Page: 2}} + repos, _, err := client.Repositories.ListByOrg(context.Background(), "o", opt) + if err != nil { + t.Errorf("Repositories.ListByOrg returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Repositories.ListByOrg returned %+v, want %+v", repos, want) + } +} + +func TestRepositoriesService_ListByOrg_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListByOrg(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_ListAll(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "since": "1", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &RepositoryListAllOptions{1} + repos, _, err := client.Repositories.ListAll(context.Background(), opt) + if err != nil { + t.Errorf("Repositories.ListAll returned error: %v", err) + } + + want := []*Repository{{ID: Int64(1)}} + if !reflect.DeepEqual(repos, want) { + t.Errorf("Repositories.ListAll returned %+v, want %+v", repos, want) + } +} + +func TestRepositoriesService_Create_user(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Repository{Name: String("n")} + + mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) { + v := new(Repository) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + repo, _, err := client.Repositories.Create(context.Background(), "", input) + if err != nil { + t.Errorf("Repositories.Create returned error: %v", err) + } + + want := &Repository{ID: Int64(1)} + if !reflect.DeepEqual(repo, want) { + t.Errorf("Repositories.Create returned %+v, want %+v", repo, want) + } +} + +func TestRepositoriesService_Create_org(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Repository{Name: String("n")} + + mux.HandleFunc("/orgs/o/repos", func(w http.ResponseWriter, r *http.Request) { + v := new(Repository) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + repo, _, err := client.Repositories.Create(context.Background(), "o", input) + if err != nil { + t.Errorf("Repositories.Create returned error: %v", err) + } + + want := &Repository{ID: Int64(1)} + if !reflect.DeepEqual(repo, want) { + t.Errorf("Repositories.Create returned %+v, want %+v", repo, want) + } +} + +func TestRepositoriesService_Create_invalidOrg(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.Create(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_Get(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} + mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) + fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`) + }) + + repo, _, err := client.Repositories.Get(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.Get returned error: %v", err) + } + + want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}} + if !reflect.DeepEqual(repo, want) { + t.Errorf("Repositories.Get returned %+v, want %+v", repo, want) + } +} + +func TestRepositoriesService_GetCodeOfConduct(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/community/code_of_conduct", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) + fmt.Fprint(w, `{ + "key": "key", + "name": "name", + "url": "url", + "body": "body"}`, + ) + }) + + coc, _, err := client.Repositories.GetCodeOfConduct(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.GetCodeOfConduct returned error: %v", err) + } + + want := &CodeOfConduct{ + Key: String("key"), + Name: String("name"), + URL: String("url"), + Body: String("body"), + } + + if !reflect.DeepEqual(coc, want) { + t.Errorf("Repositories.Get returned %+v, want %+v", coc, want) + } +} + +func TestRepositoriesService_GetByID(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repositories/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeLicensesPreview) + fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`) + }) + + repo, _, err := client.Repositories.GetByID(context.Background(), 1) + if err != nil { + t.Fatalf("Repositories.GetByID returned error: %v", err) + } + + want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}} + if !reflect.DeepEqual(repo, want) { + t.Errorf("Repositories.GetByID returned %+v, want %+v", repo, want) + } +} + +func TestRepositoriesService_Edit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + i := true + input := &Repository{HasIssues: &i} + + mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + v := new(Repository) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + fmt.Fprint(w, `{"id":1}`) + }) + + repo, _, err := client.Repositories.Edit(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.Edit returned error: %v", err) + } + + want := &Repository{ID: Int64(1)} + if !reflect.DeepEqual(repo, want) { + t.Errorf("Repositories.Edit returned %+v, want %+v", repo, want) + } +} + +func TestRepositoriesService_Delete(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Repositories.Delete(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.Delete returned error: %v", err) + } +} + +func TestRepositoriesService_Get_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.Get(context.Background(), "%", "r") + testURLParseError(t, err) +} + +func TestRepositoriesService_Edit_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.Edit(context.Background(), "%", "r", nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_ListContributors(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/contributors", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "anon": "true", + "page": "2", + }) + fmt.Fprint(w, `[{"contributions":42}]`) + }) + + opts := &ListContributorsOptions{Anon: "true", ListOptions: ListOptions{Page: 2}} + contributors, _, err := client.Repositories.ListContributors(context.Background(), "o", "r", opts) + if err != nil { + t.Errorf("Repositories.ListContributors returned error: %v", err) + } + + want := []*Contributor{{Contributions: Int(42)}} + if !reflect.DeepEqual(contributors, want) { + t.Errorf("Repositories.ListContributors returned %+v, want %+v", contributors, want) + } +} + +func TestRepositoriesService_ListLanguages(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/languages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"go":1}`) + }) + + languages, _, err := client.Repositories.ListLanguages(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.ListLanguages returned error: %v", err) + } + + want := map[string]int{"go": 1} + if !reflect.DeepEqual(languages, want) { + t.Errorf("Repositories.ListLanguages returned %+v, want %+v", languages, want) + } +} + +func TestRepositoriesService_ListTeams(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + teams, _, err := client.Repositories.ListTeams(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListTeams returned error: %v", err) + } + + want := []*Team{{ID: Int64(1)}} + if !reflect.DeepEqual(teams, want) { + t.Errorf("Repositories.ListTeams returned %+v, want %+v", teams, want) + } +} + +func TestRepositoriesService_ListTags(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/tags", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"name":"n", "commit" : {"sha" : "s", "url" : "u"}, "zipball_url": "z", "tarball_url": "t"}]`) + }) + + opt := &ListOptions{Page: 2} + tags, _, err := client.Repositories.ListTags(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListTags returned error: %v", err) + } + + want := []*RepositoryTag{ + { + Name: String("n"), + Commit: &Commit{ + SHA: String("s"), + URL: String("u"), + }, + ZipballURL: String("z"), + TarballURL: String("t"), + }, + } + if !reflect.DeepEqual(tags, want) { + t.Errorf("Repositories.ListTags returned %+v, want %+v", tags, want) + } +} + +func TestRepositoriesService_ListBranches(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"name":"master", "commit" : {"sha" : "a57781", "url" : "https://api.github.com/repos/o/r/commits/a57781"}}]`) + }) + + opt := &ListOptions{Page: 2} + branches, _, err := client.Repositories.ListBranches(context.Background(), "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListBranches returned error: %v", err) + } + + want := []*Branch{{Name: String("master"), Commit: &RepositoryCommit{SHA: String("a57781"), URL: String("https://api.github.com/repos/o/r/commits/a57781")}}} + if !reflect.DeepEqual(branches, want) { + t.Errorf("Repositories.ListBranches returned %+v, want %+v", branches, want) + } +} + +func TestRepositoriesService_GetBranch(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprint(w, `{"name":"n", "commit":{"sha":"s","commit":{"message":"m"}}, "protected":true}`) + }) + + branch, _, err := client.Repositories.GetBranch(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.GetBranch returned error: %v", err) + } + + want := &Branch{ + Name: String("n"), + Commit: &RepositoryCommit{ + SHA: String("s"), + Commit: &Commit{ + Message: String("m"), + }, + }, + Protected: Bool(true), + } + + if !reflect.DeepEqual(branch, want) { + t.Errorf("Repositories.GetBranch returned %+v, want %+v", branch, want) + } +} + +func TestRepositoriesService_GetBranchProtection(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) { + v := new(ProtectionRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprintf(w, `{"required_status_checks":{"strict":true,"contexts":["continuous-integration"]},"required_pull_request_reviews":{"dismissal_restrictions":{"users":[{"id":3,"login":"u"}],"teams":[{"id":4,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true},"enforce_admins":{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true},"restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]}}`) + }) + + protection, _, err := client.Repositories.GetBranchProtection(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.GetBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: []string{"continuous-integration"}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: DismissalRestrictions{ + Users: []*User{ + {Login: String("u"), ID: Int64(3)}, + }, + Teams: []*Team{ + {Slug: String("t"), ID: Int64(4)}, + }, + }, + RequireCodeOwnerReviews: true, + }, + EnforceAdmins: &AdminEnforcement{ + URL: String("/repos/o/r/branches/b/protection/enforce_admins"), + Enabled: true, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: String("u"), ID: Int64(1)}, + }, + Teams: []*Team{ + {Slug: String("t"), ID: Int64(2)}, + }, + }, + } + if !reflect.DeepEqual(protection, want) { + t.Errorf("Repositories.GetBranchProtection returned %+v, want %+v", protection, want) + } +} + +func TestRepositoriesService_UpdateBranchProtection(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProtectionRequest{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: []string{"continuous-integration"}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + DismissStaleReviews: true, + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: []string{"uu"}, + Teams: []string{"tt"}, + }, + }, + Restrictions: &BranchRestrictionsRequest{ + Users: []string{"u"}, + Teams: []string{"t"}, + }, + } + + mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) { + v := new(ProtectionRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprintf(w, `{"required_status_checks":{"strict":true,"contexts":["continuous-integration"]},"required_pull_request_reviews":{"dismissal_restrictions":{"users":[{"id":3,"login":"uu"}],"teams":[{"id":4,"slug":"tt"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true},"restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]}}`) + }) + + protection, _, err := client.Repositories.UpdateBranchProtection(context.Background(), "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: []string{"continuous-integration"}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: DismissalRestrictions{ + Users: []*User{ + {Login: String("uu"), ID: Int64(3)}, + }, + Teams: []*Team{ + {Slug: String("tt"), ID: Int64(4)}, + }, + }, + RequireCodeOwnerReviews: true, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: String("u"), ID: Int64(1)}, + }, + Teams: []*Team{ + {Slug: String("t"), ID: Int64(2)}, + }, + }, + } + if !reflect.DeepEqual(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } +} + +func TestRepositoriesService_RemoveBranchProtection(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Repositories.RemoveBranchProtection(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.RemoveBranchProtection returned error: %v", err) + } +} + +func TestRepositoriesService_ListLanguages_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Repositories.ListLanguages(context.Background(), "%", "%") + testURLParseError(t, err) +} + +func TestRepositoriesService_License(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/license", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name": "LICENSE", "path": "LICENSE", "license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","featured":true}}`) + }) + + got, _, err := client.Repositories.License(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.License returned error: %v", err) + } + + want := &RepositoryLicense{ + Name: String("LICENSE"), + Path: String("LICENSE"), + License: &License{ + Name: String("MIT License"), + Key: String("mit"), + SPDXID: String("MIT"), + URL: String("https://api.github.com/licenses/mit"), + Featured: Bool(true), + }, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.License returned %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_GetRequiredStatusChecks(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/required_status_checks", func(w http.ResponseWriter, r *http.Request) { + v := new(ProtectionRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprint(w, `{"strict": true,"contexts": ["x","y","z"]}`) + }) + + checks, _, err := client.Repositories.GetRequiredStatusChecks(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.GetRequiredStatusChecks returned error: %v", err) + } + + want := &RequiredStatusChecks{ + Strict: true, + Contexts: []string{"x", "y", "z"}, + } + if !reflect.DeepEqual(checks, want) { + t.Errorf("Repositories.GetRequiredStatusChecks returned %+v, want %+v", checks, want) + } +} + +func TestRepositoriesService_ListRequiredStatusChecksContexts(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/required_status_checks/contexts", func(w http.ResponseWriter, r *http.Request) { + v := new(ProtectionRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprint(w, `["x", "y", "z"]`) + }) + + contexts, _, err := client.Repositories.ListRequiredStatusChecksContexts(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.ListRequiredStatusChecksContexts returned error: %v", err) + } + + want := []string{"x", "y", "z"} + if !reflect.DeepEqual(contexts, want) { + t.Errorf("Repositories.ListRequiredStatusChecksContexts returned %+v, want %+v", contexts, want) + } +} + +func TestRepositoriesService_GetPullRequestReviewEnforcement(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true}`) + }) + + enforcement, _, err := client.Repositories.GetPullRequestReviewEnforcement(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.GetPullRequestReviewEnforcement returned error: %v", err) + } + + want := &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: DismissalRestrictions{ + Users: []*User{ + {Login: String("u"), ID: Int64(1)}, + }, + Teams: []*Team{ + {Slug: String("t"), ID: Int64(2)}, + }, + }, + RequireCodeOwnerReviews: true, + } + + if !reflect.DeepEqual(enforcement, want) { + t.Errorf("Repositories.GetPullRequestReviewEnforcement returned %+v, want %+v", enforcement, want) + } +} + +func TestRepositoriesService_UpdatePullRequestReviewEnforcement(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &PullRequestReviewsEnforcementUpdate{ + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: []string{"u"}, + Teams: []string{"t"}, + }, + } + + mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { + v := new(PullRequestReviewsEnforcementUpdate) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[{"id":1,"login":"u"}],"teams":[{"id":2,"slug":"t"}]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true}`) + }) + + enforcement, _, err := client.Repositories.UpdatePullRequestReviewEnforcement(context.Background(), "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.UpdatePullRequestReviewEnforcement returned error: %v", err) + } + + want := &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: DismissalRestrictions{ + Users: []*User{ + {Login: String("u"), ID: Int64(1)}, + }, + Teams: []*Team{ + {Slug: String("t"), ID: Int64(2)}, + }, + }, + RequireCodeOwnerReviews: true, + } + if !reflect.DeepEqual(enforcement, want) { + t.Errorf("Repositories.UpdatePullRequestReviewEnforcement returned %+v, want %+v", enforcement, want) + } +} + +func TestRepositoriesService_DisableDismissalRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + testBody(t, r, `{"dismissal_restrictions":[]}`+"\n") + fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[],"teams":[]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true}`) + }) + + enforcement, _, err := client.Repositories.DisableDismissalRestrictions(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.DisableDismissalRestrictions returned error: %v", err) + } + + want := &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: DismissalRestrictions{ + Users: []*User{}, + Teams: []*Team{}, + }, + RequireCodeOwnerReviews: true, + } + if !reflect.DeepEqual(enforcement, want) { + t.Errorf("Repositories.DisableDismissalRestrictions returned %+v, want %+v", enforcement, want) + } +} + +func TestRepositoriesService_RemovePullRequestReviewEnforcement(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Repositories.RemovePullRequestReviewEnforcement(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.RemovePullRequestReviewEnforcement returned error: %v", err) + } +} + +func TestRepositoriesService_GetAdminEnforcement(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/enforce_admins", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true}`) + }) + + enforcement, _, err := client.Repositories.GetAdminEnforcement(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.GetAdminEnforcement returned error: %v", err) + } + + want := &AdminEnforcement{ + URL: String("/repos/o/r/branches/b/protection/enforce_admins"), + Enabled: true, + } + + if !reflect.DeepEqual(enforcement, want) { + t.Errorf("Repositories.GetAdminEnforcement returned %+v, want %+v", enforcement, want) + } +} + +func TestRepositoriesService_AddAdminEnforcement(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/enforce_admins", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true}`) + }) + + enforcement, _, err := client.Repositories.AddAdminEnforcement(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.AddAdminEnforcement returned error: %v", err) + } + + want := &AdminEnforcement{ + URL: String("/repos/o/r/branches/b/protection/enforce_admins"), + Enabled: true, + } + if !reflect.DeepEqual(enforcement, want) { + t.Errorf("Repositories.AddAdminEnforcement returned %+v, want %+v", enforcement, want) + } +} + +func TestRepositoriesService_RemoveAdminEnforcement(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/enforce_admins", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProtectedBranchesPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Repositories.RemoveAdminEnforcement(context.Background(), "o", "r", "b") + if err != nil { + t.Errorf("Repositories.RemoveAdminEnforcement returned error: %v", err) + } +} + +func TestPullRequestReviewsEnforcementRequest_MarshalJSON_nilDismissalRestirctions(t *testing.T) { + req := PullRequestReviewsEnforcementRequest{} + + json, err := json.Marshal(req) + if err != nil { + t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned error: %v", err) + } + + want := `{"dismissal_restrictions":[],"dismiss_stale_reviews":false,"require_code_owner_reviews":false}` + if want != string(json) { + t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned %+v, want %+v", string(json), want) + } +} + +func TestRepositoriesService_ListAllTopics(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + fmt.Fprint(w, `{"names":["go", "go-github", "github"]}`) + }) + + got, _, err := client.Repositories.ListAllTopics(context.Background(), "o", "r") + if err != nil { + t.Fatalf("Repositories.ListAllTopics returned error: %v", err) + } + + want := []string{"go", "go-github", "github"} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.ListAllTopics returned %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_ListAllTopics_emptyTopics(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + fmt.Fprint(w, `{"names":[]}`) + }) + + got, _, err := client.Repositories.ListAllTopics(context.Background(), "o", "r") + if err != nil { + t.Fatalf("Repositories.ListAllTopics returned error: %v", err) + } + + want := []string{} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.ListAllTopics returned %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_ReplaceAllTopics(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + fmt.Fprint(w, `{"names":["go", "go-github", "github"]}`) + }) + + got, _, err := client.Repositories.ReplaceAllTopics(context.Background(), "o", "r", []string{"go", "go-github", "github"}) + if err != nil { + t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) + } + + want := []string{"go", "go-github", "github"} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_ReplaceAllTopics_nilSlice(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + testBody(t, r, `{"names":[]}`+"\n") + fmt.Fprint(w, `{"names":[]}`) + }) + + got, _, err := client.Repositories.ReplaceAllTopics(context.Background(), "o", "r", nil) + if err != nil { + t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) + } + + want := []string{} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_ReplaceAllTopics_emptySlice(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + testBody(t, r, `{"names":[]}`+"\n") + fmt.Fprint(w, `{"names":[]}`) + }) + + got, _, err := client.Repositories.ReplaceAllTopics(context.Background(), "o", "r", []string{}) + if err != nil { + t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) + } + + want := []string{} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) + } +} + +func TestRepositoriesService_Transfer(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := TransferRequest{NewOwner: "a", TeamID: []int64{123}} + + mux.HandleFunc("/repos/o/r/transfer", func(w http.ResponseWriter, r *http.Request) { + var v TransferRequest + json.NewDecoder(r.Body).Decode(&v) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeRepositoryTransferPreview) + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"owner":{"login":"a"}}`) + }) + + got, _, err := client.Repositories.Transfer(context.Background(), "o", "r", input) + if err != nil { + t.Errorf("Repositories.Transfer returned error: %v", err) + } + + want := &Repository{Owner: &User{Login: String("a")}} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.Transfer returned %+v, want %+v", got, want) + } +} diff --git a/vendor/github.com/google/go-github/github/repos_traffic.go b/vendor/github.com/google/go-github/github/repos_traffic.go new file mode 100644 index 00000000..fb1c9764 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_traffic.go @@ -0,0 +1,141 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// TrafficReferrer represent information about traffic from a referrer . +type TrafficReferrer struct { + Referrer *string `json:"referrer,omitempty"` + Count *int `json:"count,omitempty"` + Uniques *int `json:"uniques,omitempty"` +} + +// TrafficPath represent information about the traffic on a path of the repo. +type TrafficPath struct { + Path *string `json:"path,omitempty"` + Title *string `json:"title,omitempty"` + Count *int `json:"count,omitempty"` + Uniques *int `json:"uniques,omitempty"` +} + +// TrafficData represent information about a specific timestamp in views or clones list. +type TrafficData struct { + Timestamp *Timestamp `json:"timestamp,omitempty"` + Count *int `json:"count,omitempty"` + Uniques *int `json:"uniques,omitempty"` +} + +// TrafficViews represent information about the number of views in the last 14 days. +type TrafficViews struct { + Views []*TrafficData `json:"views,omitempty"` + Count *int `json:"count,omitempty"` + Uniques *int `json:"uniques,omitempty"` +} + +// TrafficClones represent information about the number of clones in the last 14 days. +type TrafficClones struct { + Clones []*TrafficData `json:"clones,omitempty"` + Count *int `json:"count,omitempty"` + Uniques *int `json:"uniques,omitempty"` +} + +// TrafficBreakdownOptions specifies the parameters to methods that support breakdown per day or week. +// Can be one of: day, week. Default: day. +type TrafficBreakdownOptions struct { + Per string `url:"per,omitempty"` +} + +// ListTrafficReferrers list the top 10 referrers over the last 14 days. +// +// GitHub API docs: https://developer.github.com/v3/repos/traffic/#list-referrers +func (s *RepositoriesService) ListTrafficReferrers(ctx context.Context, owner, repo string) ([]*TrafficReferrer, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/traffic/popular/referrers", owner, repo) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var trafficReferrers []*TrafficReferrer + resp, err := s.client.Do(ctx, req, &trafficReferrers) + if err != nil { + return nil, resp, err + } + + return trafficReferrers, resp, nil +} + +// ListTrafficPaths list the top 10 popular content over the last 14 days. +// +// GitHub API docs: https://developer.github.com/v3/repos/traffic/#list-paths +func (s *RepositoriesService) ListTrafficPaths(ctx context.Context, owner, repo string) ([]*TrafficPath, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/traffic/popular/paths", owner, repo) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var paths []*TrafficPath + resp, err := s.client.Do(ctx, req, &paths) + if err != nil { + return nil, resp, err + } + + return paths, resp, nil +} + +// ListTrafficViews get total number of views for the last 14 days and breaks it down either per day or week. +// +// GitHub API docs: https://developer.github.com/v3/repos/traffic/#views +func (s *RepositoriesService) ListTrafficViews(ctx context.Context, owner, repo string, opt *TrafficBreakdownOptions) (*TrafficViews, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/traffic/views", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + trafficViews := new(TrafficViews) + resp, err := s.client.Do(ctx, req, &trafficViews) + if err != nil { + return nil, resp, err + } + + return trafficViews, resp, nil +} + +// ListTrafficClones get total number of clones for the last 14 days and breaks it down either per day or week for the last 14 days. +// +// GitHub API docs: https://developer.github.com/v3/repos/traffic/#views +func (s *RepositoriesService) ListTrafficClones(ctx context.Context, owner, repo string, opt *TrafficBreakdownOptions) (*TrafficClones, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/traffic/clones", owner, repo) + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + trafficClones := new(TrafficClones) + resp, err := s.client.Do(ctx, req, &trafficClones) + if err != nil { + return nil, resp, err + } + + return trafficClones, resp, nil +} diff --git a/vendor/github.com/google/go-github/github/repos_traffic_test.go b/vendor/github.com/google/go-github/github/repos_traffic_test.go new file mode 100644 index 00000000..0a2eb4f3 --- /dev/null +++ b/vendor/github.com/google/go-github/github/repos_traffic_test.go @@ -0,0 +1,145 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestRepositoriesService_ListTrafficReferrers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/traffic/popular/referrers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `[{ + "referrer": "Google", + "count": 4, + "uniques": 3 + }]`) + }) + referrers, _, err := client.Repositories.ListTrafficReferrers(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.ListPaths returned error: %+v", err) + } + + want := []*TrafficReferrer{{ + Referrer: String("Google"), + Count: Int(4), + Uniques: Int(3), + }} + if !reflect.DeepEqual(referrers, want) { + t.Errorf("Repositories.ListReferrers returned %+v, want %+v", referrers, want) + } + +} + +func TestRepositoriesService_ListTrafficPaths(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/traffic/popular/paths", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `[{ + "path": "/github/hubot", + "title": "github/hubot: A customizable life embetterment robot.", + "count": 3542, + "uniques": 2225 + }]`) + }) + paths, _, err := client.Repositories.ListTrafficPaths(context.Background(), "o", "r") + if err != nil { + t.Errorf("Repositories.ListPaths returned error: %+v", err) + } + + want := []*TrafficPath{{ + Path: String("/github/hubot"), + Title: String("github/hubot: A customizable life embetterment robot."), + Count: Int(3542), + Uniques: Int(2225), + }} + if !reflect.DeepEqual(paths, want) { + t.Errorf("Repositories.ListPaths returned %+v, want %+v", paths, want) + } + +} + +func TestRepositoriesService_ListTrafficViews(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/traffic/views", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{"count": 7, + "uniques": 6, + "views": [{ + "timestamp": "2016-05-31T16:00:00.000Z", + "count": 7, + "uniques": 6 + }]}`) + }) + + views, _, err := client.Repositories.ListTrafficViews(context.Background(), "o", "r", nil) + if err != nil { + t.Errorf("Repositories.ListPaths returned error: %+v", err) + } + + want := &TrafficViews{ + Views: []*TrafficData{{ + Timestamp: &Timestamp{time.Date(2016, time.May, 31, 16, 0, 0, 0, time.UTC)}, + Count: Int(7), + Uniques: Int(6), + }}, + Count: Int(7), + Uniques: Int(6), + } + + if !reflect.DeepEqual(views, want) { + t.Errorf("Repositories.ListViews returned %+v, want %+v", views, want) + } + +} + +func TestRepositoriesService_ListTrafficClones(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/traffic/clones", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{"count": 7, + "uniques": 6, + "clones": [{ + "timestamp": "2016-05-31T16:00:00.00Z", + "count": 7, + "uniques": 6 + }]}`) + }) + + clones, _, err := client.Repositories.ListTrafficClones(context.Background(), "o", "r", nil) + if err != nil { + t.Errorf("Repositories.ListPaths returned error: %+v", err) + } + + want := &TrafficClones{ + Clones: []*TrafficData{{ + Timestamp: &Timestamp{time.Date(2016, time.May, 31, 16, 0, 0, 0, time.UTC)}, + Count: Int(7), + Uniques: Int(6), + }}, + Count: Int(7), + Uniques: Int(6), + } + + if !reflect.DeepEqual(clones, want) { + t.Errorf("Repositories.ListViews returned %+v, want %+v", clones, want) + } + +} diff --git a/vendor/github.com/google/go-github/github/search.go b/vendor/github.com/google/go-github/github/search.go new file mode 100644 index 00000000..a5973520 --- /dev/null +++ b/vendor/github.com/google/go-github/github/search.go @@ -0,0 +1,210 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + + qs "github.com/google/go-querystring/query" +) + +// SearchService provides access to the search related functions +// in the GitHub API. +// +// Each method takes a query string defining the search keywords and any search qualifiers. +// For example, when searching issues, the query "gopher is:issue language:go" will search +// for issues containing the word "gopher" in Go repositories. The method call +// opts := &github.SearchOptions{Sort: "created", Order: "asc"} +// cl.Search.Issues(ctx, "gopher is:issue language:go", opts) +// will search for such issues, sorting by creation date in ascending order +// (i.e., oldest first). +// +// GitHub API docs: https://developer.github.com/v3/search/ +type SearchService service + +// SearchOptions specifies optional parameters to the SearchService methods. +type SearchOptions struct { + // How to sort the search results. Possible values are: + // - for repositories: stars, fork, updated + // - for commits: author-date, committer-date + // - for code: indexed + // - for issues: comments, created, updated + // - for users: followers, repositories, joined + // + // Default is to sort by best match. + Sort string `url:"sort,omitempty"` + + // Sort order if sort parameter is provided. Possible values are: asc, + // desc. Default is desc. + Order string `url:"order,omitempty"` + + // Whether to retrieve text match metadata with a query + TextMatch bool `url:"-"` + + ListOptions +} + +// RepositoriesSearchResult represents the result of a repositories search. +type RepositoriesSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Repositories []Repository `json:"items,omitempty"` +} + +// Repositories searches repositories via various criteria. +// +// GitHub API docs: https://developer.github.com/v3/search/#search-repositories +func (s *SearchService) Repositories(ctx context.Context, query string, opt *SearchOptions) (*RepositoriesSearchResult, *Response, error) { + result := new(RepositoriesSearchResult) + resp, err := s.search(ctx, "repositories", query, opt, result) + return result, resp, err +} + +// CommitsSearchResult represents the result of a commits search. +type CommitsSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Commits []*CommitResult `json:"items,omitempty"` +} + +// CommitResult represents a commit object as returned in commit search endpoint response. +type CommitResult struct { + SHA *string `json:"sha,omitempty"` + Commit *Commit `json:"commit,omitempty"` + Author *User `json:"author,omitempty"` + Committer *User `json:"committer,omitempty"` + Parents []*Commit `json:"parents,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + URL *string `json:"url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` + + Repository *Repository `json:"repository,omitempty"` + Score *float64 `json:"score,omitempty"` +} + +// Commits searches commits via various criteria. +// +// GitHub API docs: https://developer.github.com/v3/search/#search-commits +func (s *SearchService) Commits(ctx context.Context, query string, opt *SearchOptions) (*CommitsSearchResult, *Response, error) { + result := new(CommitsSearchResult) + resp, err := s.search(ctx, "commits", query, opt, result) + return result, resp, err +} + +// IssuesSearchResult represents the result of an issues search. +type IssuesSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Issues []Issue `json:"items,omitempty"` +} + +// Issues searches issues via various criteria. +// +// GitHub API docs: https://developer.github.com/v3/search/#search-issues +func (s *SearchService) Issues(ctx context.Context, query string, opt *SearchOptions) (*IssuesSearchResult, *Response, error) { + result := new(IssuesSearchResult) + resp, err := s.search(ctx, "issues", query, opt, result) + return result, resp, err +} + +// UsersSearchResult represents the result of a users search. +type UsersSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Users []User `json:"items,omitempty"` +} + +// Users searches users via various criteria. +// +// GitHub API docs: https://developer.github.com/v3/search/#search-users +func (s *SearchService) Users(ctx context.Context, query string, opt *SearchOptions) (*UsersSearchResult, *Response, error) { + result := new(UsersSearchResult) + resp, err := s.search(ctx, "users", query, opt, result) + return result, resp, err +} + +// Match represents a single text match. +type Match struct { + Text *string `json:"text,omitempty"` + Indices []int `json:"indices,omitempty"` +} + +// TextMatch represents a text match for a SearchResult +type TextMatch struct { + ObjectURL *string `json:"object_url,omitempty"` + ObjectType *string `json:"object_type,omitempty"` + Property *string `json:"property,omitempty"` + Fragment *string `json:"fragment,omitempty"` + Matches []Match `json:"matches,omitempty"` +} + +func (tm TextMatch) String() string { + return Stringify(tm) +} + +// CodeSearchResult represents the result of a code search. +type CodeSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + CodeResults []CodeResult `json:"items,omitempty"` +} + +// CodeResult represents a single search result. +type CodeResult struct { + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` + SHA *string `json:"sha,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Repository *Repository `json:"repository,omitempty"` + TextMatches []TextMatch `json:"text_matches,omitempty"` +} + +func (c CodeResult) String() string { + return Stringify(c) +} + +// Code searches code via various criteria. +// +// GitHub API docs: https://developer.github.com/v3/search/#search-code +func (s *SearchService) Code(ctx context.Context, query string, opt *SearchOptions) (*CodeSearchResult, *Response, error) { + result := new(CodeSearchResult) + resp, err := s.search(ctx, "code", query, opt, result) + return result, resp, err +} + +// Helper function that executes search queries against different +// GitHub search types (repositories, commits, code, issues, users) +func (s *SearchService) search(ctx context.Context, searchType string, query string, opt *SearchOptions, result interface{}) (*Response, error) { + params, err := qs.Values(opt) + if err != nil { + return nil, err + } + params.Set("q", query) + u := fmt.Sprintf("search/%s?%s", searchType, params.Encode()) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, err + } + + switch { + case searchType == "commits": + // Accept header for search commits preview endpoint + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeCommitSearchPreview) + case searchType == "repositories": + // Accept header for search repositories based on topics preview endpoint + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeTopicsPreview) + case opt != nil && opt.TextMatch: + // Accept header defaults to "application/vnd.github.v3+json" + // We change it here to fetch back text-match metadata + req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") + } + + return s.client.Do(ctx, req, result) +} diff --git a/vendor/github.com/google/go-github/github/search_test.go b/vendor/github.com/google/go-github/github/search_test.go new file mode 100644 index 00000000..cc9faacf --- /dev/null +++ b/vendor/github.com/google/go-github/github/search_test.go @@ -0,0 +1,268 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + + "testing" +) + +func TestSearchService_Repositories(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/search/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "forks", + "order": "desc", + "page": "2", + "per_page": "2", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"id":1},{"id":2}]}`) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} + result, _, err := client.Search.Repositories(context.Background(), "blah", opts) + if err != nil { + t.Errorf("Search.Repositories returned error: %v", err) + } + + want := &RepositoriesSearchResult{ + Total: Int(4), + IncompleteResults: Bool(false), + Repositories: []Repository{{ID: Int64(1)}, {ID: Int64(2)}}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Repositories returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Commits(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/search/commits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "author-date", + "order": "desc", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"sha":"random_hash1"},{"sha":"random_hash2"}]}`) + }) + + opts := &SearchOptions{Sort: "author-date", Order: "desc"} + result, _, err := client.Search.Commits(context.Background(), "blah", opts) + if err != nil { + t.Errorf("Search.Commits returned error: %v", err) + } + + want := &CommitsSearchResult{ + Total: Int(4), + IncompleteResults: Bool(false), + Commits: []*CommitResult{{SHA: String("random_hash1")}, {SHA: String("random_hash2")}}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Commits returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Issues(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/search/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "forks", + "order": "desc", + "page": "2", + "per_page": "2", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": true, "items": [{"number":1},{"number":2}]}`) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} + result, _, err := client.Search.Issues(context.Background(), "blah", opts) + if err != nil { + t.Errorf("Search.Issues returned error: %v", err) + } + + want := &IssuesSearchResult{ + Total: Int(4), + IncompleteResults: Bool(true), + Issues: []Issue{{Number: Int(1)}, {Number: Int(2)}}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Issues returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Issues_withQualifiers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/search/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "gopher is:issue label:bug language:go", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": true, "items": [{"number":1},{"number":2}]}`) + }) + + opts := &SearchOptions{} + result, _, err := client.Search.Issues(context.Background(), "gopher is:issue label:bug language:go", opts) + if err != nil { + t.Errorf("Search.Issues returned error: %v", err) + } + + want := &IssuesSearchResult{ + Total: Int(4), + IncompleteResults: Bool(true), + Issues: []Issue{{Number: Int(1)}, {Number: Int(2)}}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Issues returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Users(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/search/users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "forks", + "order": "desc", + "page": "2", + "per_page": "2", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"id":1},{"id":2}]}`) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} + result, _, err := client.Search.Users(context.Background(), "blah", opts) + if err != nil { + t.Errorf("Search.Issues returned error: %v", err) + } + + want := &UsersSearchResult{ + Total: Int(4), + IncompleteResults: Bool(false), + Users: []User{{ID: Int64(1)}, {ID: Int64(2)}}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Users returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Code(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/search/code", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "forks", + "order": "desc", + "page": "2", + "per_page": "2", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"name":"1"},{"name":"2"}]}`) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} + result, _, err := client.Search.Code(context.Background(), "blah", opts) + if err != nil { + t.Errorf("Search.Code returned error: %v", err) + } + + want := &CodeSearchResult{ + Total: Int(4), + IncompleteResults: Bool(false), + CodeResults: []CodeResult{{Name: String("1")}, {Name: String("2")}}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Code returned %+v, want %+v", result, want) + } +} + +func TestSearchService_CodeTextMatch(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/search/code", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + textMatchResponse := ` + { + "total_count": 1, + "incomplete_results": false, + "items": [ + { + "name":"gopher1", + "text_matches": [ + { + "fragment": "I'm afraid my friend what you have found\nIs a gopher who lives to feed", + "matches": [ + { + "text": "gopher", + "indices": [ + 14, + 21 + ] + } + ] + } + ] + } + ] + } + ` + + fmt.Fprint(w, textMatchResponse) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}, TextMatch: true} + result, _, err := client.Search.Code(context.Background(), "blah", opts) + if err != nil { + t.Errorf("Search.Code returned error: %v", err) + } + + wantedCodeResult := CodeResult{ + Name: String("gopher1"), + TextMatches: []TextMatch{{ + Fragment: String("I'm afraid my friend what you have found\nIs a gopher who lives to feed"), + Matches: []Match{{Text: String("gopher"), Indices: []int{14, 21}}}, + }, + }, + } + + want := &CodeSearchResult{ + Total: Int(1), + IncompleteResults: Bool(false), + CodeResults: []CodeResult{wantedCodeResult}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Code returned %+v, want %+v", result, want) + } +} diff --git a/vendor/github.com/google/go-github/github/strings.go b/vendor/github.com/google/go-github/github/strings.go new file mode 100644 index 00000000..431e1cc6 --- /dev/null +++ b/vendor/github.com/google/go-github/github/strings.go @@ -0,0 +1,93 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "fmt" + "io" + + "reflect" +) + +var timestampType = reflect.TypeOf(Timestamp{}) + +// Stringify attempts to create a reasonable string representation of types in +// the GitHub library. It does things like resolve pointers to their values +// and omits struct fields with nil values. +func Stringify(message interface{}) string { + var buf bytes.Buffer + v := reflect.ValueOf(message) + stringifyValue(&buf, v) + return buf.String() +} + +// stringifyValue was heavily inspired by the goprotobuf library. + +func stringifyValue(w io.Writer, val reflect.Value) { + if val.Kind() == reflect.Ptr && val.IsNil() { + w.Write([]byte("")) + return + } + + v := reflect.Indirect(val) + + switch v.Kind() { + case reflect.String: + fmt.Fprintf(w, `"%s"`, v) + case reflect.Slice: + w.Write([]byte{'['}) + for i := 0; i < v.Len(); i++ { + if i > 0 { + w.Write([]byte{' '}) + } + + stringifyValue(w, v.Index(i)) + } + + w.Write([]byte{']'}) + return + case reflect.Struct: + if v.Type().Name() != "" { + w.Write([]byte(v.Type().String())) + } + + // special handling of Timestamp values + if v.Type() == timestampType { + fmt.Fprintf(w, "{%s}", v.Interface()) + return + } + + w.Write([]byte{'{'}) + + var sep bool + for i := 0; i < v.NumField(); i++ { + fv := v.Field(i) + if fv.Kind() == reflect.Ptr && fv.IsNil() { + continue + } + if fv.Kind() == reflect.Slice && fv.IsNil() { + continue + } + + if sep { + w.Write([]byte(", ")) + } else { + sep = true + } + + w.Write([]byte(v.Type().Field(i).Name)) + w.Write([]byte{':'}) + stringifyValue(w, fv) + } + + w.Write([]byte{'}'}) + default: + if v.CanInterface() { + fmt.Fprint(w, v.Interface()) + } + } +} diff --git a/vendor/github.com/google/go-github/github/strings_test.go b/vendor/github.com/google/go-github/github/strings_test.go new file mode 100644 index 00000000..b0a82089 --- /dev/null +++ b/vendor/github.com/google/go-github/github/strings_test.go @@ -0,0 +1,141 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "testing" + "time" +) + +func TestStringify(t *testing.T) { + var nilPointer *string + + var tests = []struct { + in interface{} + out string + }{ + // basic types + {"foo", `"foo"`}, + {123, `123`}, + {1.5, `1.5`}, + {false, `false`}, + { + []string{"a", "b"}, + `["a" "b"]`, + }, + { + struct { + A []string + }{nil}, + // nil slice is skipped + `{}`, + }, + { + struct { + A string + }{"foo"}, + // structs not of a named type get no prefix + `{A:"foo"}`, + }, + + // pointers + {nilPointer, ``}, + {String("foo"), `"foo"`}, + {Int(123), `123`}, + {Bool(false), `false`}, + { + []*string{String("a"), String("b")}, + `["a" "b"]`, + }, + + // actual GitHub structs + { + Timestamp{time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)}, + `github.Timestamp{2006-01-02 15:04:05 +0000 UTC}`, + }, + { + &Timestamp{time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)}, + `github.Timestamp{2006-01-02 15:04:05 +0000 UTC}`, + }, + { + User{ID: Int64(123), Name: String("n")}, + `github.User{ID:123, Name:"n"}`, + }, + { + Repository{Owner: &User{ID: Int64(123)}}, + `github.Repository{Owner:github.User{ID:123}}`, + }, + } + + for i, tt := range tests { + s := Stringify(tt.in) + if s != tt.out { + t.Errorf("%d. Stringify(%q) => %q, want %q", i, tt.in, s, tt.out) + } + } +} + +// Directly test the String() methods on various GitHub types. We don't do an +// exaustive test of all the various field types, since TestStringify() above +// takes care of that. Rather, we just make sure that Stringify() is being +// used to build the strings, which we do by verifying that pointers are +// stringified as their underlying value. +func TestString(t *testing.T) { + var tests = []struct { + in interface{} + out string + }{ + {CodeResult{Name: String("n")}, `github.CodeResult{Name:"n"}`}, + {CommitAuthor{Name: String("n")}, `github.CommitAuthor{Name:"n"}`}, + {CommitFile{SHA: String("s")}, `github.CommitFile{SHA:"s"}`}, + {CommitStats{Total: Int(1)}, `github.CommitStats{Total:1}`}, + {CommitsComparison{TotalCommits: Int(1)}, `github.CommitsComparison{TotalCommits:1}`}, + {Commit{SHA: String("s")}, `github.Commit{SHA:"s"}`}, + {Event{ID: String("1")}, `github.Event{ID:"1"}`}, + {GistComment{ID: Int64(1)}, `github.GistComment{ID:1}`}, + {GistFile{Size: Int(1)}, `github.GistFile{Size:1}`}, + {Gist{ID: String("1")}, `github.Gist{ID:"1", Files:map[]}`}, + {GitObject{SHA: String("s")}, `github.GitObject{SHA:"s"}`}, + {Gitignore{Name: String("n")}, `github.Gitignore{Name:"n"}`}, + {Hook{ID: Int64(1)}, `github.Hook{Config:map[], ID:1}`}, + {IssueComment{ID: Int64(1)}, `github.IssueComment{ID:1}`}, + {Issue{Number: Int(1)}, `github.Issue{Number:1}`}, + {Key{ID: Int64(1)}, `github.Key{ID:1}`}, + {Label{ID: Int64(1), Name: String("l")}, `github.Label{ID:1, Name:"l"}`}, + {Organization{ID: Int64(1)}, `github.Organization{ID:1}`}, + {PullRequestComment{ID: Int64(1)}, `github.PullRequestComment{ID:1}`}, + {PullRequest{Number: Int(1)}, `github.PullRequest{Number:1}`}, + {PullRequestReview{ID: Int64(1)}, `github.PullRequestReview{ID:1}`}, + {DraftReviewComment{Position: Int(1)}, `github.DraftReviewComment{Position:1}`}, + {PullRequestReviewRequest{Body: String("r")}, `github.PullRequestReviewRequest{Body:"r"}`}, + {PullRequestReviewDismissalRequest{Message: String("r")}, `github.PullRequestReviewDismissalRequest{Message:"r"}`}, + {PushEventCommit{SHA: String("s")}, `github.PushEventCommit{SHA:"s"}`}, + {PushEvent{PushID: Int64(1)}, `github.PushEvent{PushID:1}`}, + {Reference{Ref: String("r")}, `github.Reference{Ref:"r"}`}, + {ReleaseAsset{ID: Int64(1)}, `github.ReleaseAsset{ID:1}`}, + {RepoStatus{ID: Int64(1)}, `github.RepoStatus{ID:1}`}, + {RepositoryComment{ID: Int64(1)}, `github.RepositoryComment{ID:1}`}, + {RepositoryCommit{SHA: String("s")}, `github.RepositoryCommit{SHA:"s"}`}, + {RepositoryContent{Name: String("n")}, `github.RepositoryContent{Name:"n"}`}, + {RepositoryRelease{ID: Int64(1)}, `github.RepositoryRelease{ID:1}`}, + {Repository{ID: Int64(1)}, `github.Repository{ID:1}`}, + {Team{ID: Int64(1)}, `github.Team{ID:1}`}, + {TreeEntry{SHA: String("s")}, `github.TreeEntry{SHA:"s"}`}, + {Tree{SHA: String("s")}, `github.Tree{SHA:"s"}`}, + {User{ID: Int64(1)}, `github.User{ID:1}`}, + {WebHookAuthor{Name: String("n")}, `github.WebHookAuthor{Name:"n"}`}, + {WebHookCommit{ID: String("1")}, `github.WebHookCommit{ID:"1"}`}, + {WebHookPayload{Ref: String("r")}, `github.WebHookPayload{Ref:"r"}`}, + } + + for i, tt := range tests { + s := tt.in.(fmt.Stringer).String() + if s != tt.out { + t.Errorf("%d. String() => %q, want %q", i, tt.in, tt.out) + } + } +} diff --git a/vendor/github.com/google/go-github/github/timestamp.go b/vendor/github.com/google/go-github/github/timestamp.go new file mode 100644 index 00000000..a1c1554a --- /dev/null +++ b/vendor/github.com/google/go-github/github/timestamp.go @@ -0,0 +1,41 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "strconv" + "time" +) + +// Timestamp represents a time that can be unmarshalled from a JSON string +// formatted as either an RFC3339 or Unix timestamp. This is necessary for some +// fields since the GitHub API is inconsistent in how it represents times. All +// exported methods of time.Time can be called on Timestamp. +type Timestamp struct { + time.Time +} + +func (t Timestamp) String() string { + return t.Time.String() +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// Time is expected in RFC3339 or Unix format. +func (t *Timestamp) UnmarshalJSON(data []byte) (err error) { + str := string(data) + i, err := strconv.ParseInt(str, 10, 64) + if err == nil { + (*t).Time = time.Unix(i, 0) + } else { + (*t).Time, err = time.Parse(`"`+time.RFC3339+`"`, str) + } + return +} + +// Equal reports whether t and u are equal based on time.Equal +func (t Timestamp) Equal(u Timestamp) bool { + return t.Time.Equal(u.Time) +} diff --git a/vendor/github.com/google/go-github/github/timestamp_test.go b/vendor/github.com/google/go-github/github/timestamp_test.go new file mode 100644 index 00000000..ea7fa0eb --- /dev/null +++ b/vendor/github.com/google/go-github/github/timestamp_test.go @@ -0,0 +1,189 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "testing" + "time" +) + +const ( + emptyTimeStr = `"0001-01-01T00:00:00Z"` + referenceTimeStr = `"2006-01-02T15:04:05Z"` + referenceTimeStrFractional = `"2006-01-02T15:04:05.000Z"` // This format was returned by the Projects API before October 1, 2017. + referenceUnixTimeStr = `1136214245` +) + +var ( + referenceTime = time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC) + unixOrigin = time.Unix(0, 0).In(time.UTC) +) + +func TestTimestamp_Marshal(t *testing.T) { + testCases := []struct { + desc string + data Timestamp + want string + wantErr bool + equal bool + }{ + {"Reference", Timestamp{referenceTime}, referenceTimeStr, false, true}, + {"Empty", Timestamp{}, emptyTimeStr, false, true}, + {"Mismatch", Timestamp{}, referenceTimeStr, false, false}, + } + for _, tc := range testCases { + out, err := json.Marshal(tc.data) + if gotErr := err != nil; gotErr != tc.wantErr { + t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) + } + got := string(out) + equal := got == tc.want + if (got == tc.want) != tc.equal { + t.Errorf("%s: got=%s, want=%s, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) + } + } +} + +func TestTimestamp_Unmarshal(t *testing.T) { + testCases := []struct { + desc string + data string + want Timestamp + wantErr bool + equal bool + }{ + {"Reference", referenceTimeStr, Timestamp{referenceTime}, false, true}, + {"ReferenceUnix", referenceUnixTimeStr, Timestamp{referenceTime}, false, true}, + {"ReferenceFractional", referenceTimeStrFractional, Timestamp{referenceTime}, false, true}, + {"Empty", emptyTimeStr, Timestamp{}, false, true}, + {"UnixStart", `0`, Timestamp{unixOrigin}, false, true}, + {"Mismatch", referenceTimeStr, Timestamp{}, false, false}, + {"MismatchUnix", `0`, Timestamp{}, false, false}, + {"Invalid", `"asdf"`, Timestamp{referenceTime}, true, false}, + } + for _, tc := range testCases { + var got Timestamp + err := json.Unmarshal([]byte(tc.data), &got) + if gotErr := err != nil; gotErr != tc.wantErr { + t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) + continue + } + equal := got.Equal(tc.want) + if equal != tc.equal { + t.Errorf("%s: got=%#v, want=%#v, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) + } + } +} + +func TestTimstamp_MarshalReflexivity(t *testing.T) { + testCases := []struct { + desc string + data Timestamp + }{ + {"Reference", Timestamp{referenceTime}}, + {"Empty", Timestamp{}}, + } + for _, tc := range testCases { + data, err := json.Marshal(tc.data) + if err != nil { + t.Errorf("%s: Marshal err=%v", tc.desc, err) + } + var got Timestamp + err = json.Unmarshal(data, &got) + if err != nil { + t.Errorf("%s: Unmarshal err=%v", tc.desc, err) + } + if !got.Equal(tc.data) { + t.Errorf("%s: %+v != %+v", tc.desc, got, data) + } + } +} + +type WrappedTimestamp struct { + A int + Time Timestamp +} + +func TestWrappedTimstamp_Marshal(t *testing.T) { + testCases := []struct { + desc string + data WrappedTimestamp + want string + wantErr bool + equal bool + }{ + {"Reference", WrappedTimestamp{0, Timestamp{referenceTime}}, fmt.Sprintf(`{"A":0,"Time":%s}`, referenceTimeStr), false, true}, + {"Empty", WrappedTimestamp{}, fmt.Sprintf(`{"A":0,"Time":%s}`, emptyTimeStr), false, true}, + {"Mismatch", WrappedTimestamp{}, fmt.Sprintf(`{"A":0,"Time":%s}`, referenceTimeStr), false, false}, + } + for _, tc := range testCases { + out, err := json.Marshal(tc.data) + if gotErr := err != nil; gotErr != tc.wantErr { + t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) + } + got := string(out) + equal := got == tc.want + if equal != tc.equal { + t.Errorf("%s: got=%s, want=%s, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) + } + } +} + +func TestWrappedTimstamp_Unmarshal(t *testing.T) { + testCases := []struct { + desc string + data string + want WrappedTimestamp + wantErr bool + equal bool + }{ + {"Reference", referenceTimeStr, WrappedTimestamp{0, Timestamp{referenceTime}}, false, true}, + {"ReferenceUnix", referenceUnixTimeStr, WrappedTimestamp{0, Timestamp{referenceTime}}, false, true}, + {"Empty", emptyTimeStr, WrappedTimestamp{0, Timestamp{}}, false, true}, + {"UnixStart", `0`, WrappedTimestamp{0, Timestamp{unixOrigin}}, false, true}, + {"Mismatch", referenceTimeStr, WrappedTimestamp{0, Timestamp{}}, false, false}, + {"MismatchUnix", `0`, WrappedTimestamp{0, Timestamp{}}, false, false}, + {"Invalid", `"asdf"`, WrappedTimestamp{0, Timestamp{referenceTime}}, true, false}, + } + for _, tc := range testCases { + var got Timestamp + err := json.Unmarshal([]byte(tc.data), &got) + if gotErr := err != nil; gotErr != tc.wantErr { + t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) + continue + } + equal := got.Time.Equal(tc.want.Time.Time) + if equal != tc.equal { + t.Errorf("%s: got=%#v, want=%#v, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) + } + } +} + +func TestWrappedTimstamp_MarshalReflexivity(t *testing.T) { + testCases := []struct { + desc string + data WrappedTimestamp + }{ + {"Reference", WrappedTimestamp{0, Timestamp{referenceTime}}}, + {"Empty", WrappedTimestamp{0, Timestamp{}}}, + } + for _, tc := range testCases { + bytes, err := json.Marshal(tc.data) + if err != nil { + t.Errorf("%s: Marshal err=%v", tc.desc, err) + } + var got WrappedTimestamp + err = json.Unmarshal(bytes, &got) + if err != nil { + t.Errorf("%s: Unmarshal err=%v", tc.desc, err) + } + if !got.Time.Equal(tc.data.Time) { + t.Errorf("%s: %+v != %+v", tc.desc, got, tc.data) + } + } +} diff --git a/vendor/github.com/google/go-github/github/users.go b/vendor/github.com/google/go-github/github/users.go new file mode 100644 index 00000000..ef8f3dd5 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users.go @@ -0,0 +1,230 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// UsersService handles communication with the user related +// methods of the GitHub API. +// +// GitHub API docs: https://developer.github.com/v3/users/ +type UsersService service + +// User represents a GitHub user. +type User struct { + Login *string `json:"login,omitempty"` + ID *int64 `json:"id,omitempty"` + AvatarURL *string `json:"avatar_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + GravatarID *string `json:"gravatar_id,omitempty"` + Name *string `json:"name,omitempty"` + Company *string `json:"company,omitempty"` + Blog *string `json:"blog,omitempty"` + Location *string `json:"location,omitempty"` + Email *string `json:"email,omitempty"` + Hireable *bool `json:"hireable,omitempty"` + Bio *string `json:"bio,omitempty"` + PublicRepos *int `json:"public_repos,omitempty"` + PublicGists *int `json:"public_gists,omitempty"` + Followers *int `json:"followers,omitempty"` + Following *int `json:"following,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + SuspendedAt *Timestamp `json:"suspended_at,omitempty"` + Type *string `json:"type,omitempty"` + SiteAdmin *bool `json:"site_admin,omitempty"` + TotalPrivateRepos *int `json:"total_private_repos,omitempty"` + OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` + PrivateGists *int `json:"private_gists,omitempty"` + DiskUsage *int `json:"disk_usage,omitempty"` + Collaborators *int `json:"collaborators,omitempty"` + Plan *Plan `json:"plan,omitempty"` + + // API URLs + URL *string `json:"url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + FollowingURL *string `json:"following_url,omitempty"` + FollowersURL *string `json:"followers_url,omitempty"` + GistsURL *string `json:"gists_url,omitempty"` + OrganizationsURL *string `json:"organizations_url,omitempty"` + ReceivedEventsURL *string `json:"received_events_url,omitempty"` + ReposURL *string `json:"repos_url,omitempty"` + StarredURL *string `json:"starred_url,omitempty"` + SubscriptionsURL *string `json:"subscriptions_url,omitempty"` + + // TextMatches is only populated from search results that request text matches + // See: search.go and https://developer.github.com/v3/search/#text-match-metadata + TextMatches []TextMatch `json:"text_matches,omitempty"` + + // Permissions identifies the permissions that a user has on a given + // repository. This is only populated when calling Repositories.ListCollaborators. + Permissions *map[string]bool `json:"permissions,omitempty"` +} + +func (u User) String() string { + return Stringify(u) +} + +// Get fetches a user. Passing the empty string will fetch the authenticated +// user. +// +// GitHub API docs: https://developer.github.com/v3/users/#get-a-single-user +func (s *UsersService) Get(ctx context.Context, user string) (*User, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v", user) + } else { + u = "user" + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + uResp := new(User) + resp, err := s.client.Do(ctx, req, uResp) + if err != nil { + return nil, resp, err + } + + return uResp, resp, nil +} + +// GetByID fetches a user. +// +// Note: GetByID uses the undocumented GitHub API endpoint /user/:id. +func (s *UsersService) GetByID(ctx context.Context, id int64) (*User, *Response, error) { + u := fmt.Sprintf("user/%d", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + user := new(User) + resp, err := s.client.Do(ctx, req, user) + if err != nil { + return nil, resp, err + } + + return user, resp, nil +} + +// Edit the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/#update-the-authenticated-user +func (s *UsersService) Edit(ctx context.Context, user *User) (*User, *Response, error) { + u := "user" + req, err := s.client.NewRequest("PATCH", u, user) + if err != nil { + return nil, nil, err + } + + uResp := new(User) + resp, err := s.client.Do(ctx, req, uResp) + if err != nil { + return nil, resp, err + } + + return uResp, resp, nil +} + +// UserListOptions specifies optional parameters to the UsersService.ListAll +// method. +type UserListOptions struct { + // ID of the last user seen + Since int64 `url:"since,omitempty"` + + ListOptions +} + +// ListAll lists all GitHub users. +// +// To paginate through all users, populate 'Since' with the ID of the last user. +// +// GitHub API docs: https://developer.github.com/v3/users/#get-all-users +func (s *UsersService) ListAll(ctx context.Context, opt *UserListOptions) ([]*User, *Response, error) { + u, err := addOptions("users", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var users []*User + resp, err := s.client.Do(ctx, req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// ListInvitations lists all currently-open repository invitations for the +// authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-a-users-repository-invitations +func (s *UsersService) ListInvitations(ctx context.Context, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { + u, err := addOptions("user/repository_invitations", opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) + + invites := []*RepositoryInvitation{} + resp, err := s.client.Do(ctx, req, &invites) + if err != nil { + return nil, resp, err + } + + return invites, resp, nil +} + +// AcceptInvitation accepts the currently-open repository invitation for the +// authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/repos/invitations/#accept-a-repository-invitation +func (s *UsersService) AcceptInvitation(ctx context.Context, invitationID int64) (*Response, error) { + u := fmt.Sprintf("user/repository_invitations/%v", invitationID) + req, err := s.client.NewRequest("PATCH", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) + + return s.client.Do(ctx, req, nil) +} + +// DeclineInvitation declines the currently-open repository invitation for the +// authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/repos/invitations/#decline-a-repository-invitation +func (s *UsersService) DeclineInvitation(ctx context.Context, invitationID int64) (*Response, error) { + u := fmt.Sprintf("user/repository_invitations/%v", invitationID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/users_administration.go b/vendor/github.com/google/go-github/github/users_administration.go new file mode 100644 index 00000000..e042398d --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_administration.go @@ -0,0 +1,67 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// PromoteSiteAdmin promotes a user to a site administrator of a GitHub Enterprise instance. +// +// GitHub API docs: https://developer.github.com/v3/users/administration/#promote-an-ordinary-user-to-a-site-administrator +func (s *UsersService) PromoteSiteAdmin(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("users/%v/site_admin", user) + + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// DemoteSiteAdmin demotes a user from site administrator of a GitHub Enterprise instance. +// +// GitHub API docs: https://developer.github.com/v3/users/administration/#demote-a-site-administrator-to-an-ordinary-user +func (s *UsersService) DemoteSiteAdmin(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("users/%v/site_admin", user) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Suspend a user on a GitHub Enterprise instance. +// +// GitHub API docs: https://developer.github.com/v3/users/administration/#suspend-a-user +func (s *UsersService) Suspend(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("users/%v/suspended", user) + + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Unsuspend a user on a GitHub Enterprise instance. +// +// GitHub API docs: https://developer.github.com/v3/users/administration/#unsuspend-a-user +func (s *UsersService) Unsuspend(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("users/%v/suspended", user) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/users_administration_test.go b/vendor/github.com/google/go-github/github/users_administration_test.go new file mode 100644 index 00000000..4b0955e9 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_administration_test.go @@ -0,0 +1,72 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "net/http" + "testing" +) + +func TestUsersService_PromoteSiteAdmin(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/site_admin", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Users.PromoteSiteAdmin(context.Background(), "u") + if err != nil { + t.Errorf("Users.PromoteSiteAdmin returned error: %v", err) + } +} + +func TestUsersService_DemoteSiteAdmin(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/site_admin", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Users.DemoteSiteAdmin(context.Background(), "u") + if err != nil { + t.Errorf("Users.DemoteSiteAdmin returned error: %v", err) + } +} + +func TestUsersService_Suspend(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/suspended", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Users.Suspend(context.Background(), "u") + if err != nil { + t.Errorf("Users.Suspend returned error: %v", err) + } +} + +func TestUsersService_Unsuspend(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/suspended", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Users.Unsuspend(context.Background(), "u") + if err != nil { + t.Errorf("Users.Unsuspend returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/users_blocking.go b/vendor/github.com/google/go-github/github/users_blocking.go new file mode 100644 index 00000000..39e45601 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_blocking.go @@ -0,0 +1,91 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListBlockedUsers lists all the blocked users by the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/blocking/#list-blocked-users +func (s *UsersService) ListBlockedUsers(ctx context.Context, opt *ListOptions) ([]*User, *Response, error) { + u := "user/blocks" + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + var blockedUsers []*User + resp, err := s.client.Do(ctx, req, &blockedUsers) + if err != nil { + return nil, resp, err + } + + return blockedUsers, resp, nil +} + +// IsBlocked reports whether specified user is blocked by the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/blocking/#check-whether-youve-blocked-a-user +func (s *UsersService) IsBlocked(ctx context.Context, user string) (bool, *Response, error) { + u := fmt.Sprintf("user/blocks/%v", user) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + resp, err := s.client.Do(ctx, req, nil) + isBlocked, err := parseBoolResponse(err) + return isBlocked, resp, err +} + +// BlockUser blocks specified user for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/blocking/#block-a-user +func (s *UsersService) BlockUser(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("user/blocks/%v", user) + + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + return s.client.Do(ctx, req, nil) +} + +// UnblockUser unblocks specified user for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/blocking/#unblock-a-user +func (s *UsersService) UnblockUser(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("user/blocks/%v", user) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeBlockUsersPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/users_blocking_test.go b/vendor/github.com/google/go-github/github/users_blocking_test.go new file mode 100644 index 00000000..83e8e30c --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_blocking_test.go @@ -0,0 +1,90 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestUsersService_ListBlockedUsers(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/blocks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{ + "login": "octocat" + }]`) + }) + + opt := &ListOptions{Page: 2} + blockedUsers, _, err := client.Users.ListBlockedUsers(context.Background(), opt) + if err != nil { + t.Errorf("Users.ListBlockedUsers returned error: %v", err) + } + + want := []*User{{Login: String("octocat")}} + if !reflect.DeepEqual(blockedUsers, want) { + t.Errorf("Users.ListBlockedUsers returned %+v, want %+v", blockedUsers, want) + } +} + +func TestUsersService_IsBlocked(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/blocks/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + w.WriteHeader(http.StatusNoContent) + }) + + isBlocked, _, err := client.Users.IsBlocked(context.Background(), "u") + if err != nil { + t.Errorf("Users.IsBlocked returned error: %v", err) + } + if want := true; isBlocked != want { + t.Errorf("Users.IsBlocked returned %+v, want %+v", isBlocked, want) + } +} + +func TestUsersService_BlockUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/blocks/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Users.BlockUser(context.Background(), "u") + if err != nil { + t.Errorf("Users.BlockUser returned error: %v", err) + } +} + +func TestUsersService_UnblockUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/blocks/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeBlockUsersPreview) + w.WriteHeader(http.StatusNoContent) + }) + + _, err := client.Users.UnblockUser(context.Background(), "u") + if err != nil { + t.Errorf("Users.UnblockUser returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/users_emails.go b/vendor/github.com/google/go-github/github/users_emails.go new file mode 100644 index 00000000..0bbd4627 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_emails.go @@ -0,0 +1,71 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import "context" + +// UserEmail represents user's email address +type UserEmail struct { + Email *string `json:"email,omitempty"` + Primary *bool `json:"primary,omitempty"` + Verified *bool `json:"verified,omitempty"` +} + +// ListEmails lists all email addresses for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user +func (s *UsersService) ListEmails(ctx context.Context, opt *ListOptions) ([]*UserEmail, *Response, error) { + u := "user/emails" + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var emails []*UserEmail + resp, err := s.client.Do(ctx, req, &emails) + if err != nil { + return nil, resp, err + } + + return emails, resp, nil +} + +// AddEmails adds email addresses of the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/emails/#add-email-addresses +func (s *UsersService) AddEmails(ctx context.Context, emails []string) ([]*UserEmail, *Response, error) { + u := "user/emails" + req, err := s.client.NewRequest("POST", u, emails) + if err != nil { + return nil, nil, err + } + + var e []*UserEmail + resp, err := s.client.Do(ctx, req, &e) + if err != nil { + return nil, resp, err + } + + return e, resp, nil +} + +// DeleteEmails deletes email addresses from authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/emails/#delete-email-addresses +func (s *UsersService) DeleteEmails(ctx context.Context, emails []string) (*Response, error) { + u := "user/emails" + req, err := s.client.NewRequest("DELETE", u, emails) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/users_emails_test.go b/vendor/github.com/google/go-github/github/users_emails_test.go new file mode 100644 index 00000000..3f72b409 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_emails_test.go @@ -0,0 +1,95 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestUsersService_ListEmails(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/emails", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{ + "email": "user@example.com", + "verified": false, + "primary": true + }]`) + }) + + opt := &ListOptions{Page: 2} + emails, _, err := client.Users.ListEmails(context.Background(), opt) + if err != nil { + t.Errorf("Users.ListEmails returned error: %v", err) + } + + want := []*UserEmail{{Email: String("user@example.com"), Verified: Bool(false), Primary: Bool(true)}} + if !reflect.DeepEqual(emails, want) { + t.Errorf("Users.ListEmails returned %+v, want %+v", emails, want) + } +} + +func TestUsersService_AddEmails(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := []string{"new@example.com"} + + mux.HandleFunc("/user/emails", func(w http.ResponseWriter, r *http.Request) { + var v []string + json.NewDecoder(r.Body).Decode(&v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `[{"email":"old@example.com"}, {"email":"new@example.com"}]`) + }) + + emails, _, err := client.Users.AddEmails(context.Background(), input) + if err != nil { + t.Errorf("Users.AddEmails returned error: %v", err) + } + + want := []*UserEmail{ + {Email: String("old@example.com")}, + {Email: String("new@example.com")}, + } + if !reflect.DeepEqual(emails, want) { + t.Errorf("Users.AddEmails returned %+v, want %+v", emails, want) + } +} + +func TestUsersService_DeleteEmails(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := []string{"user@example.com"} + + mux.HandleFunc("/user/emails", func(w http.ResponseWriter, r *http.Request) { + var v []string + json.NewDecoder(r.Body).Decode(&v) + + testMethod(t, r, "DELETE") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + }) + + _, err := client.Users.DeleteEmails(context.Background(), input) + if err != nil { + t.Errorf("Users.DeleteEmails returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/users_followers.go b/vendor/github.com/google/go-github/github/users_followers.go new file mode 100644 index 00000000..c2224096 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_followers.go @@ -0,0 +1,119 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListFollowers lists the followers for a user. Passing the empty string will +// fetch followers for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/followers/#list-followers-of-a-user +func (s *UsersService) ListFollowers(ctx context.Context, user string, opt *ListOptions) ([]*User, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/followers", user) + } else { + u = "user/followers" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var users []*User + resp, err := s.client.Do(ctx, req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// ListFollowing lists the people that a user is following. Passing the empty +// string will list people the authenticated user is following. +// +// GitHub API docs: https://developer.github.com/v3/users/followers/#list-users-followed-by-another-user +func (s *UsersService) ListFollowing(ctx context.Context, user string, opt *ListOptions) ([]*User, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/following", user) + } else { + u = "user/following" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var users []*User + resp, err := s.client.Do(ctx, req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// IsFollowing checks if "user" is following "target". Passing the empty +// string for "user" will check if the authenticated user is following "target". +// +// GitHub API docs: https://developer.github.com/v3/users/followers/#check-if-you-are-following-a-user +func (s *UsersService) IsFollowing(ctx context.Context, user, target string) (bool, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/following/%v", user, target) + } else { + u = fmt.Sprintf("user/following/%v", target) + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return false, nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + following, err := parseBoolResponse(err) + return following, resp, err +} + +// Follow will cause the authenticated user to follow the specified user. +// +// GitHub API docs: https://developer.github.com/v3/users/followers/#follow-a-user +func (s *UsersService) Follow(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("user/following/%v", user) + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// Unfollow will cause the authenticated user to unfollow the specified user. +// +// GitHub API docs: https://developer.github.com/v3/users/followers/#unfollow-a-user +func (s *UsersService) Unfollow(ctx context.Context, user string) (*Response, error) { + u := fmt.Sprintf("user/following/%v", user) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/users_followers_test.go b/vendor/github.com/google/go-github/github/users_followers_test.go new file mode 100644 index 00000000..777fe801 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_followers_test.go @@ -0,0 +1,238 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestUsersService_ListFollowers_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/followers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + users, _, err := client.Users.ListFollowers(context.Background(), "", opt) + if err != nil { + t.Errorf("Users.ListFollowers returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(users, want) { + t.Errorf("Users.ListFollowers returned %+v, want %+v", users, want) + } +} + +func TestUsersService_ListFollowers_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/followers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + + users, _, err := client.Users.ListFollowers(context.Background(), "u", nil) + if err != nil { + t.Errorf("Users.ListFollowers returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(users, want) { + t.Errorf("Users.ListFollowers returned %+v, want %+v", users, want) + } +} + +func TestUsersService_ListFollowers_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Users.ListFollowers(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestUsersService_ListFollowing_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/following", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opts := &ListOptions{Page: 2} + users, _, err := client.Users.ListFollowing(context.Background(), "", opts) + if err != nil { + t.Errorf("Users.ListFollowing returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(users, want) { + t.Errorf("Users.ListFollowing returned %+v, want %+v", users, want) + } +} + +func TestUsersService_ListFollowing_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/following", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + + users, _, err := client.Users.ListFollowing(context.Background(), "u", nil) + if err != nil { + t.Errorf("Users.ListFollowing returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}} + if !reflect.DeepEqual(users, want) { + t.Errorf("Users.ListFollowing returned %+v, want %+v", users, want) + } +} + +func TestUsersService_ListFollowing_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Users.ListFollowing(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestUsersService_IsFollowing_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/following/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + following, _, err := client.Users.IsFollowing(context.Background(), "", "t") + if err != nil { + t.Errorf("Users.IsFollowing returned error: %v", err) + } + if want := true; following != want { + t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) + } +} + +func TestUsersService_IsFollowing_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/following/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + following, _, err := client.Users.IsFollowing(context.Background(), "u", "t") + if err != nil { + t.Errorf("Users.IsFollowing returned error: %v", err) + } + if want := true; following != want { + t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) + } +} + +func TestUsersService_IsFollowing_false(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/following/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + following, _, err := client.Users.IsFollowing(context.Background(), "u", "t") + if err != nil { + t.Errorf("Users.IsFollowing returned error: %v", err) + } + if want := false; following != want { + t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) + } +} + +func TestUsersService_IsFollowing_error(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/following/t", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Error(w, "BadRequest", http.StatusBadRequest) + }) + + following, _, err := client.Users.IsFollowing(context.Background(), "u", "t") + if err == nil { + t.Errorf("Expected HTTP 400 response") + } + if want := false; following != want { + t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) + } +} + +func TestUsersService_IsFollowing_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Users.IsFollowing(context.Background(), "%", "%") + testURLParseError(t, err) +} + +func TestUsersService_Follow(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/following/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + _, err := client.Users.Follow(context.Background(), "u") + if err != nil { + t.Errorf("Users.Follow returned error: %v", err) + } +} + +func TestUsersService_Follow_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Users.Follow(context.Background(), "%") + testURLParseError(t, err) +} + +func TestUsersService_Unfollow(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/following/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Users.Unfollow(context.Background(), "u") + if err != nil { + t.Errorf("Users.Follow returned error: %v", err) + } +} + +func TestUsersService_Unfollow_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, err := client.Users.Unfollow(context.Background(), "%") + testURLParseError(t, err) +} diff --git a/vendor/github.com/google/go-github/github/users_gpg_keys.go b/vendor/github.com/google/go-github/github/users_gpg_keys.go new file mode 100644 index 00000000..d8bbc520 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_gpg_keys.go @@ -0,0 +1,140 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "time" +) + +// GPGKey represents a GitHub user's public GPG key used to verify GPG signed commits and tags. +// +// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/ +type GPGKey struct { + ID *int64 `json:"id,omitempty"` + PrimaryKeyID *int64 `json:"primary_key_id,omitempty"` + KeyID *string `json:"key_id,omitempty"` + PublicKey *string `json:"public_key,omitempty"` + Emails []GPGEmail `json:"emails,omitempty"` + Subkeys []GPGKey `json:"subkeys,omitempty"` + CanSign *bool `json:"can_sign,omitempty"` + CanEncryptComms *bool `json:"can_encrypt_comms,omitempty"` + CanEncryptStorage *bool `json:"can_encrypt_storage,omitempty"` + CanCertify *bool `json:"can_certify,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` +} + +// String stringifies a GPGKey. +func (k GPGKey) String() string { + return Stringify(k) +} + +// GPGEmail represents an email address associated to a GPG key. +type GPGEmail struct { + Email *string `json:"email,omitempty"` + Verified *bool `json:"verified,omitempty"` +} + +// ListGPGKeys lists the public GPG keys for a user. Passing the empty +// string will fetch keys for the authenticated user. It requires authentication +// via Basic Auth or via OAuth with at least read:gpg_key scope. +// +// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#list-gpg-keys-for-a-user +func (s *UsersService) ListGPGKeys(ctx context.Context, user string, opt *ListOptions) ([]*GPGKey, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/gpg_keys", user) + } else { + u = "user/gpg_keys" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGitSigningPreview) + + var keys []*GPGKey + resp, err := s.client.Do(ctx, req, &keys) + if err != nil { + return nil, resp, err + } + + return keys, resp, nil +} + +// GetGPGKey gets extended details for a single GPG key. It requires authentication +// via Basic Auth or via OAuth with at least read:gpg_key scope. +// +// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#get-a-single-gpg-key +func (s *UsersService) GetGPGKey(ctx context.Context, id int64) (*GPGKey, *Response, error) { + u := fmt.Sprintf("user/gpg_keys/%v", id) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGitSigningPreview) + + key := &GPGKey{} + resp, err := s.client.Do(ctx, req, key) + if err != nil { + return nil, resp, err + } + + return key, resp, nil +} + +// CreateGPGKey creates a GPG key. It requires authenticatation via Basic Auth +// or OAuth with at least write:gpg_key scope. +// +// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#create-a-gpg-key +func (s *UsersService) CreateGPGKey(ctx context.Context, armoredPublicKey string) (*GPGKey, *Response, error) { + gpgKey := &struct { + ArmoredPublicKey string `json:"armored_public_key"` + }{ArmoredPublicKey: armoredPublicKey} + req, err := s.client.NewRequest("POST", "user/gpg_keys", gpgKey) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGitSigningPreview) + + key := &GPGKey{} + resp, err := s.client.Do(ctx, req, key) + if err != nil { + return nil, resp, err + } + + return key, resp, nil +} + +// DeleteGPGKey deletes a GPG key. It requires authentication via Basic Auth or +// via OAuth with at least admin:gpg_key scope. +// +// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#delete-a-gpg-key +func (s *UsersService) DeleteGPGKey(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("user/gpg_keys/%v", id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeGitSigningPreview) + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/users_gpg_keys_test.go b/vendor/github.com/google/go-github/github/users_gpg_keys_test.go new file mode 100644 index 00000000..ca0207eb --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_gpg_keys_test.go @@ -0,0 +1,142 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestUsersService_ListGPGKeys_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/gpg_keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1,"primary_key_id":2}]`) + }) + + opt := &ListOptions{Page: 2} + keys, _, err := client.Users.ListGPGKeys(context.Background(), "", opt) + if err != nil { + t.Errorf("Users.ListGPGKeys returned error: %v", err) + } + + want := []*GPGKey{{ID: Int64(1), PrimaryKeyID: Int64(2)}} + if !reflect.DeepEqual(keys, want) { + t.Errorf("Users.ListGPGKeys = %+v, want %+v", keys, want) + } +} + +func TestUsersService_ListGPGKeys_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/gpg_keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + fmt.Fprint(w, `[{"id":1,"primary_key_id":2}]`) + }) + + keys, _, err := client.Users.ListGPGKeys(context.Background(), "u", nil) + if err != nil { + t.Errorf("Users.ListGPGKeys returned error: %v", err) + } + + want := []*GPGKey{{ID: Int64(1), PrimaryKeyID: Int64(2)}} + if !reflect.DeepEqual(keys, want) { + t.Errorf("Users.ListGPGKeys = %+v, want %+v", keys, want) + } +} + +func TestUsersService_ListGPGKeys_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Users.ListGPGKeys(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestUsersService_GetGPGKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/gpg_keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + key, _, err := client.Users.GetGPGKey(context.Background(), 1) + if err != nil { + t.Errorf("Users.GetGPGKey returned error: %v", err) + } + + want := &GPGKey{ID: Int64(1)} + if !reflect.DeepEqual(key, want) { + t.Errorf("Users.GetGPGKey = %+v, want %+v", key, want) + } +} + +func TestUsersService_CreateGPGKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := ` +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: GPGTools - https://gpgtools.org + +mQINBFcEd9kBEACo54TDbGhKlXKWMvJgecEUKPPcv7XdnpKdGb3LRw5MvFwT0V0f +... +=tqfb +-----END PGP PUBLIC KEY BLOCK-----` + + mux.HandleFunc("/user/gpg_keys", func(w http.ResponseWriter, r *http.Request) { + var gpgKey struct { + ArmoredPublicKey *string `json:"armored_public_key,omitempty"` + } + json.NewDecoder(r.Body).Decode(&gpgKey) + + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + if gpgKey.ArmoredPublicKey == nil || *gpgKey.ArmoredPublicKey != input { + t.Errorf("gpgKey = %+v, want %q", gpgKey, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + gpgKey, _, err := client.Users.CreateGPGKey(context.Background(), input) + if err != nil { + t.Errorf("Users.GetGPGKey returned error: %v", err) + } + + want := &GPGKey{ID: Int64(1)} + if !reflect.DeepEqual(gpgKey, want) { + t.Errorf("Users.GetGPGKey = %+v, want %+v", gpgKey, want) + } +} + +func TestUsersService_DeleteGPGKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/gpg_keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeGitSigningPreview) + }) + + _, err := client.Users.DeleteGPGKey(context.Background(), 1) + if err != nil { + t.Errorf("Users.DeleteGPGKey returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/users_keys.go b/vendor/github.com/google/go-github/github/users_keys.go new file mode 100644 index 00000000..ddc832a1 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_keys.go @@ -0,0 +1,108 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Key represents a public SSH key used to authenticate a user or deploy script. +type Key struct { + ID *int64 `json:"id,omitempty"` + Key *string `json:"key,omitempty"` + URL *string `json:"url,omitempty"` + Title *string `json:"title,omitempty"` + ReadOnly *bool `json:"read_only,omitempty"` +} + +func (k Key) String() string { + return Stringify(k) +} + +// ListKeys lists the verified public keys for a user. Passing the empty +// string will fetch keys for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user +func (s *UsersService) ListKeys(ctx context.Context, user string, opt *ListOptions) ([]*Key, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/keys", user) + } else { + u = "user/keys" + } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var keys []*Key + resp, err := s.client.Do(ctx, req, &keys) + if err != nil { + return nil, resp, err + } + + return keys, resp, nil +} + +// GetKey fetches a single public key. +// +// GitHub API docs: https://developer.github.com/v3/users/keys/#get-a-single-public-key +func (s *UsersService) GetKey(ctx context.Context, id int64) (*Key, *Response, error) { + u := fmt.Sprintf("user/keys/%v", id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + key := new(Key) + resp, err := s.client.Do(ctx, req, key) + if err != nil { + return nil, resp, err + } + + return key, resp, nil +} + +// CreateKey adds a public key for the authenticated user. +// +// GitHub API docs: https://developer.github.com/v3/users/keys/#create-a-public-key +func (s *UsersService) CreateKey(ctx context.Context, key *Key) (*Key, *Response, error) { + u := "user/keys" + + req, err := s.client.NewRequest("POST", u, key) + if err != nil { + return nil, nil, err + } + + k := new(Key) + resp, err := s.client.Do(ctx, req, k) + if err != nil { + return nil, resp, err + } + + return k, resp, nil +} + +// DeleteKey deletes a public key. +// +// GitHub API docs: https://developer.github.com/v3/users/keys/#delete-a-public-key +func (s *UsersService) DeleteKey(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("user/keys/%v", id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/github/users_keys_test.go b/vendor/github.com/google/go-github/github/users_keys_test.go new file mode 100644 index 00000000..3115fdb2 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_keys_test.go @@ -0,0 +1,128 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestUsersService_ListKeys_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + keys, _, err := client.Users.ListKeys(context.Background(), "", opt) + if err != nil { + t.Errorf("Users.ListKeys returned error: %v", err) + } + + want := []*Key{{ID: Int64(1)}} + if !reflect.DeepEqual(keys, want) { + t.Errorf("Users.ListKeys returned %+v, want %+v", keys, want) + } +} + +func TestUsersService_ListKeys_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + + keys, _, err := client.Users.ListKeys(context.Background(), "u", nil) + if err != nil { + t.Errorf("Users.ListKeys returned error: %v", err) + } + + want := []*Key{{ID: Int64(1)}} + if !reflect.DeepEqual(keys, want) { + t.Errorf("Users.ListKeys returned %+v, want %+v", keys, want) + } +} + +func TestUsersService_ListKeys_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Users.ListKeys(context.Background(), "%", nil) + testURLParseError(t, err) +} + +func TestUsersService_GetKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + key, _, err := client.Users.GetKey(context.Background(), 1) + if err != nil { + t.Errorf("Users.GetKey returned error: %v", err) + } + + want := &Key{ID: Int64(1)} + if !reflect.DeepEqual(key, want) { + t.Errorf("Users.GetKey returned %+v, want %+v", key, want) + } +} + +func TestUsersService_CreateKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Key{Key: String("k"), Title: String("t")} + + mux.HandleFunc("/user/keys", func(w http.ResponseWriter, r *http.Request) { + v := new(Key) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + key, _, err := client.Users.CreateKey(context.Background(), input) + if err != nil { + t.Errorf("Users.GetKey returned error: %v", err) + } + + want := &Key{ID: Int64(1)} + if !reflect.DeepEqual(key, want) { + t.Errorf("Users.GetKey returned %+v, want %+v", key, want) + } +} + +func TestUsersService_DeleteKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + _, err := client.Users.DeleteKey(context.Background(), 1) + if err != nil { + t.Errorf("Users.DeleteKey returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/users_test.go b/vendor/github.com/google/go-github/github/users_test.go new file mode 100644 index 00000000..750b6733 --- /dev/null +++ b/vendor/github.com/google/go-github/github/users_test.go @@ -0,0 +1,245 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestUser_marshall(t *testing.T) { + testJSONMarshal(t, &User{}, "{}") + + u := &User{ + Login: String("l"), + ID: Int64(1), + URL: String("u"), + AvatarURL: String("a"), + GravatarID: String("g"), + Name: String("n"), + Company: String("c"), + Blog: String("b"), + Location: String("l"), + Email: String("e"), + Hireable: Bool(true), + PublicRepos: Int(1), + Followers: Int(1), + Following: Int(1), + CreatedAt: &Timestamp{referenceTime}, + SuspendedAt: &Timestamp{referenceTime}, + } + want := `{ + "login": "l", + "id": 1, + "avatar_url": "a", + "gravatar_id": "g", + "name": "n", + "company": "c", + "blog": "b", + "location": "l", + "email": "e", + "hireable": true, + "public_repos": 1, + "followers": 1, + "following": 1, + "created_at": ` + referenceTimeStr + `, + "suspended_at": ` + referenceTimeStr + `, + "url": "u" + }` + testJSONMarshal(t, u, want) +} + +func TestUsersService_Get_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + user, _, err := client.Users.Get(context.Background(), "") + if err != nil { + t.Errorf("Users.Get returned error: %v", err) + } + + want := &User{ID: Int64(1)} + if !reflect.DeepEqual(user, want) { + t.Errorf("Users.Get returned %+v, want %+v", user, want) + } +} + +func TestUsersService_Get_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + user, _, err := client.Users.Get(context.Background(), "u") + if err != nil { + t.Errorf("Users.Get returned error: %v", err) + } + + want := &User{ID: Int64(1)} + if !reflect.DeepEqual(user, want) { + t.Errorf("Users.Get returned %+v, want %+v", user, want) + } +} + +func TestUsersService_Get_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + _, _, err := client.Users.Get(context.Background(), "%") + testURLParseError(t, err) +} + +func TestUsersService_GetByID(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + user, _, err := client.Users.GetByID(context.Background(), 1) + if err != nil { + t.Fatalf("Users.GetByID returned error: %v", err) + } + + want := &User{ID: Int64(1)} + if !reflect.DeepEqual(user, want) { + t.Errorf("Users.GetByID returned %+v, want %+v", user, want) + } +} + +func TestUsersService_Edit(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &User{Name: String("n")} + + mux.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + v := new(User) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !reflect.DeepEqual(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + user, _, err := client.Users.Edit(context.Background(), input) + if err != nil { + t.Errorf("Users.Edit returned error: %v", err) + } + + want := &User{ID: Int64(1)} + if !reflect.DeepEqual(user, want) { + t.Errorf("Users.Edit returned %+v, want %+v", user, want) + } +} + +func TestUsersService_ListAll(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"since": "1", "page": "2"}) + fmt.Fprint(w, `[{"id":2}]`) + }) + + opt := &UserListOptions{1, ListOptions{Page: 2}} + users, _, err := client.Users.ListAll(context.Background(), opt) + if err != nil { + t.Errorf("Users.Get returned error: %v", err) + } + + want := []*User{{ID: Int64(2)}} + if !reflect.DeepEqual(users, want) { + t.Errorf("Users.ListAll returned %+v, want %+v", users, want) + } +} + +func TestUsersService_ListInvitations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/repository_invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + }) + + got, _, err := client.Users.ListInvitations(context.Background(), nil) + if err != nil { + t.Errorf("Users.ListInvitations returned error: %v", err) + } + + want := []*RepositoryInvitation{{ID: Int64(1)}, {ID: Int64(2)}} + if !reflect.DeepEqual(got, want) { + t.Errorf("Users.ListInvitations = %+v, want %+v", got, want) + } +} + +func TestUsersService_ListInvitations_withOptions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/repository_invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + }) + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + }) + + _, _, err := client.Users.ListInvitations(context.Background(), &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Users.ListInvitations returned error: %v", err) + } +} +func TestUsersService_AcceptInvitation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/repository_invitations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + w.WriteHeader(http.StatusNoContent) + }) + + if _, err := client.Users.AcceptInvitation(context.Background(), 1); err != nil { + t.Errorf("Users.AcceptInvitation returned error: %v", err) + } +} + +func TestUsersService_DeclineInvitation(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/repository_invitations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) + w.WriteHeader(http.StatusNoContent) + }) + + if _, err := client.Users.DeclineInvitation(context.Background(), 1); err != nil { + t.Errorf("Users.DeclineInvitation returned error: %v", err) + } +} diff --git a/vendor/github.com/google/go-github/github/with_appengine.go b/vendor/github.com/google/go-github/github/with_appengine.go new file mode 100644 index 00000000..87a228ad --- /dev/null +++ b/vendor/github.com/google/go-github/github/with_appengine.go @@ -0,0 +1,25 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +// This file provides glue for making github work on App Engine. +// In order to get the entire github package to compile with +// Go 1.6, you will need to rewrite all the import "context" lines. +// Fortunately, this is easy with "gofmt": +// +// gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go + +package github + +import ( + "context" + "net/http" +) + +func withContext(ctx context.Context, req *http.Request) *http.Request { + // No-op because App Engine adds context to a request differently. + return req +} diff --git a/vendor/github.com/google/go-github/github/without_appengine.go b/vendor/github.com/google/go-github/github/without_appengine.go new file mode 100644 index 00000000..6f8fdac5 --- /dev/null +++ b/vendor/github.com/google/go-github/github/without_appengine.go @@ -0,0 +1,19 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +// This file provides glue for making github work without App Engine. + +package github + +import ( + "context" + "net/http" +) + +func withContext(ctx context.Context, req *http.Request) *http.Request { + return req.WithContext(ctx) +} diff --git a/vendor/github.com/google/go-github/test/README.md b/vendor/github.com/google/go-github/test/README.md new file mode 100644 index 00000000..cb3ef551 --- /dev/null +++ b/vendor/github.com/google/go-github/test/README.md @@ -0,0 +1,62 @@ +go-github tests +=============== + +This directory contains additional test suites beyond the unit tests already in +[../github](../github). Whereas the unit tests run very quickly (since they +don't make any network calls) and are run by Travis on every commit, the tests +in this directory are only run manually. + +The test packages are: + +integration +----------- + +This will exercise the entire go-github library (or at least as much as is +practical) against the live GitHub API. These tests will verify that the +library is properly coded against the actual behavior of the API, and will +(hopefully) fail upon any incompatible change in the API. + +Because these tests are running using live data, there is a much higher +probability of false positives in test failures due to network issues, test +data having been changed, etc. + +These tests send real network traffic to the GitHub API and will exhaust the +default unregistered rate limit (60 requests per hour) very quickly. +Additionally, in order to test the methods that modify data, a real OAuth token +will need to be present. While the tests will try to be well-behaved in terms +of what data they modify, it is **strongly** recommended that these tests only +be run using a dedicated test account. + +Run tests using: + + GITHUB_AUTH_TOKEN=XXX go test -v -tags=integration ./integration + +Additionally there are a set of integration tests for the Authorizations API. +These tests require a GitHub user (username and password), and also that a +[GitHub Application](https://github.com/settings/applications/new) (with +attendant Client ID and Client Secret) be available. Then, to execute just the +Authorization tests: + + GITHUB_USERNAME='' GITHUB_PASSWORD='' GITHUB_CLIENT_ID='' GITHUB_CLIENT_SECRET='' go test -v -tags=integration -run=Authorizations ./integration + +If some or all of these environment variables are not available, certain of the +Authorization integration tests will be skipped. + +fields +------ + +This will identify the fields being returned by the live GitHub API that are +not currently being mapped into the relevant Go data type. Sometimes fields +are deliberately not mapped, so the results of this tool should just be taken +as a hint. + +This test sends real network traffic to the GitHub API and will exhaust the +default unregistered rate limit (60 requests per hour) very quickly. +Additionally, some data is only returned for authenticated API calls. Unlike +the integration tests above, these tests only read data, so it's less +imperitive that these be run using a dedicated test account (though you still +really should). + +Run the fields tool using: + + GITHUB_AUTH_TOKEN=XXX go run ./fields/fields.go diff --git a/vendor/github.com/google/go-github/test/fields/fields.go b/vendor/github.com/google/go-github/test/fields/fields.go new file mode 100644 index 00000000..7ee1f6e0 --- /dev/null +++ b/vendor/github.com/google/go-github/test/fields/fields.go @@ -0,0 +1,148 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This tool tests for the JSON mappings in the go-github data types. It will +// identify fields that are returned by the live GitHub API, but that are not +// currently mapped into a struct field of the relevant go-github type. This +// helps to ensure that all relevant data returned by the API is being made +// accessible, particularly new fields that are periodically (and sometimes +// quietly) added to the API over time. +// +// These tests simply aid in identifying which fields aren't being mapped; it +// is not necessarily true that every one of them should always be mapped. +// Some fields may be undocumented for a reason, either because they aren't +// actually used yet or should not be relied upon. +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "reflect" + "strings" + + "github.com/google/go-github/github" + "golang.org/x/oauth2" +) + +var ( + client *github.Client + + // auth indicates whether tests are being run with an OAuth token. + // Tests can use this flag to skip certain tests when run without auth. + auth bool + + skipURLs = flag.Bool("skip_urls", false, "skip url fields") +) + +func main() { + flag.Parse() + + token := os.Getenv("GITHUB_AUTH_TOKEN") + if token == "" { + print("!!! No OAuth token. Some tests won't run. !!!\n\n") + client = github.NewClient(nil) + } else { + tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + )) + client = github.NewClient(tc) + auth = true + } + + for _, tt := range []struct { + url string + typ interface{} + }{ + //{"rate_limit", &github.RateLimits{}}, + {"users/octocat", &github.User{}}, + {"user", &github.User{}}, + {"users/willnorris/keys", &[]github.Key{}}, + {"orgs/google-test", &github.Organization{}}, + {"repos/google/go-github", &github.Repository{}}, + {"repos/google/go-github/issues/1", &github.Issue{}}, + {"/gists/9257657", &github.Gist{}}, + } { + err := testType(tt.url, tt.typ) + if err != nil { + fmt.Printf("error: %v\n", err) + } + } +} + +// testType fetches the JSON resource at urlStr and compares its keys to the +// struct fields of typ. +func testType(urlStr string, typ interface{}) error { + slice := reflect.Indirect(reflect.ValueOf(typ)).Kind() == reflect.Slice + + req, err := client.NewRequest("GET", urlStr, nil) + if err != nil { + return err + } + + // start with a json.RawMessage so we can decode multiple ways below + raw := new(json.RawMessage) + _, err = client.Do(context.Background(), req, raw) + if err != nil { + return err + } + + // unmarshal directly to a map + var m1 map[string]interface{} + if slice { + var s []map[string]interface{} + err = json.Unmarshal(*raw, &s) + if err != nil { + return err + } + m1 = s[0] + } else { + err = json.Unmarshal(*raw, &m1) + if err != nil { + return err + } + } + + // unmarshal to typ first, then re-marshal and unmarshal to a map + err = json.Unmarshal(*raw, typ) + if err != nil { + return err + } + + var byt []byte + if slice { + // use first item in slice + v := reflect.Indirect(reflect.ValueOf(typ)) + byt, err = json.Marshal(v.Index(0).Interface()) + if err != nil { + return err + } + } else { + byt, err = json.Marshal(typ) + if err != nil { + return err + } + } + + var m2 map[string]interface{} + err = json.Unmarshal(byt, &m2) + if err != nil { + return err + } + + // now compare the two maps + for k, v := range m1 { + if *skipURLs && strings.HasSuffix(k, "_url") { + continue + } + if _, ok := m2[k]; !ok { + fmt.Printf("%v missing field for key: %v (example value: %v)\n", reflect.TypeOf(typ), k, v) + } + } + + return nil +} diff --git a/vendor/github.com/google/go-github/test/integration/activity_test.go b/vendor/github.com/google/go-github/test/integration/activity_test.go new file mode 100644 index 00000000..df6c34d6 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/activity_test.go @@ -0,0 +1,141 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "testing" + + "github.com/google/go-github/github" +) + +const ( + owner = "google" + repo = "go-github" +) + +func TestActivity_Starring(t *testing.T) { + stargazers, _, err := client.Activity.ListStargazers(context.Background(), owner, repo, nil) + if err != nil { + t.Fatalf("Activity.ListStargazers returned error: %v", err) + } + + if len(stargazers) == 0 { + t.Errorf("Activity.ListStargazers(%q, %q) returned no stargazers", owner, repo) + } + + // the rest of the tests requires auth + if !checkAuth("TestActivity_Starring") { + return + } + + // first, check if already starred the target repository + star, _, err := client.Activity.IsStarred(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.IsStarred returned error: %v", err) + } + if star { + t.Fatalf("Already starring %v/%v. Please manually unstar it first.", owner, repo) + } + + // star the target repository + _, err = client.Activity.Star(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.Star returned error: %v", err) + } + + // check again and verify starred + star, _, err = client.Activity.IsStarred(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.IsStarred returned error: %v", err) + } + if !star { + t.Fatalf("Not starred %v/%v after starring it.", owner, repo) + } + + // unstar + _, err = client.Activity.Unstar(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.Unstar returned error: %v", err) + } + + // check again and verify not watching + star, _, err = client.Activity.IsStarred(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.IsStarred returned error: %v", err) + } + if star { + t.Fatalf("Still starred %v/%v after unstarring it.", owner, repo) + } +} + +func deleteSubscription(t *testing.T) { + // delete subscription + _, err := client.Activity.DeleteRepositorySubscription(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.DeleteRepositorySubscription returned error: %v", err) + } + + // check again and verify not watching + sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.GetRepositorySubscription returned error: %v", err) + } + if sub != nil { + t.Fatalf("Still watching %v/%v after deleting subscription.", owner, repo) + } +} + +func createSubscription(t *testing.T) { + // watch the target repository + sub := &github.Subscription{Subscribed: github.Bool(true)} + _, _, err := client.Activity.SetRepositorySubscription(context.Background(), owner, repo, sub) + if err != nil { + t.Fatalf("Activity.SetRepositorySubscription returned error: %v", err) + } + + // check again and verify watching + sub, _, err = client.Activity.GetRepositorySubscription(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.GetRepositorySubscription returned error: %v", err) + } + if sub == nil || !*sub.Subscribed { + t.Fatalf("Not watching %v/%v after setting subscription.", owner, repo) + } +} + +func TestActivity_Watching(t *testing.T) { + watchers, _, err := client.Activity.ListWatchers(context.Background(), owner, repo, nil) + if err != nil { + t.Fatalf("Activity.ListWatchers returned error: %v", err) + } + + if len(watchers) == 0 { + t.Errorf("Activity.ListWatchers(%q, %q) returned no watchers", owner, repo) + } + + // the rest of the tests requires auth + if !checkAuth("TestActivity_Watching") { + return + } + + // first, check if already watching the target repository + sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), owner, repo) + if err != nil { + t.Fatalf("Activity.GetRepositorySubscription returned error: %v", err) + } + + switch { + case sub != nil: // If already subscribing, delete then recreate subscription. + deleteSubscription(t) + createSubscription(t) + case sub == nil: // Otherwise, create subscription and then delete it. + createSubscription(t) + deleteSubscription(t) + } +} diff --git a/vendor/github.com/google/go-github/test/integration/authorizations_test.go b/vendor/github.com/google/go-github/test/integration/authorizations_test.go new file mode 100644 index 00000000..532ac76f --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/authorizations_test.go @@ -0,0 +1,306 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "math/rand" + "os" + "strconv" + "strings" + "testing" + "time" + + "github.com/google/go-github/github" +) + +const msgEnvMissing = "Skipping test because the required environment variable (%v) is not present." +const envKeyGitHubUsername = "GITHUB_USERNAME" +const envKeyGitHubPassword = "GITHUB_PASSWORD" +const envKeyClientID = "GITHUB_CLIENT_ID" +const envKeyClientSecret = "GITHUB_CLIENT_SECRET" +const InvalidTokenValue = "iamnotacroken" + +// TestAuthorizationsBasicOperations tests the basic CRUD operations of the API (mostly for +// the Personal Access Token scenario). +func TestAuthorizationsBasicOperations(t *testing.T) { + + client := getUserPassClient(t) + + auths, resp, err := client.Authorizations.List(context.Background(), nil) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + initialAuthCount := len(auths) + + authReq := generatePersonalAuthTokenRequest() + + createdAuth, resp, err := client.Authorizations.Create(context.Background(), authReq) + failOnError(t, err) + failIfNotStatusCode(t, resp, 201) + + if *authReq.Note != *createdAuth.Note { + t.Fatal("Returned Authorization does not match the requested Authorization.") + } + + auths, resp, err = client.Authorizations.List(context.Background(), nil) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + if len(auths) != initialAuthCount+1 { + t.Fatalf("The number of Authorizations should have increased. Expected [%v], was [%v]", initialAuthCount+1, len(auths)) + } + + // Test updating the authorization + authUpdate := new(github.AuthorizationUpdateRequest) + authUpdate.Note = github.String("Updated note: " + randString()) + + updatedAuth, resp, err := client.Authorizations.Edit(context.Background(), *createdAuth.ID, authUpdate) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + if *updatedAuth.Note != *authUpdate.Note { + t.Fatal("The returned Authorization does not match the requested updated value.") + } + + // Verify that the Get operation also reflects the update + retrievedAuth, resp, err := client.Authorizations.Get(context.Background(), *createdAuth.ID) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + if *retrievedAuth.Note != *updatedAuth.Note { + t.Fatal("The retrieved Authorization does not match the expected (updated) value.") + } + + // Now, let's delete... + resp, err = client.Authorizations.Delete(context.Background(), *createdAuth.ID) + failOnError(t, err) + failIfNotStatusCode(t, resp, 204) + + // Verify that we can no longer retrieve the auth + retrievedAuth, resp, err = client.Authorizations.Get(context.Background(), *createdAuth.ID) + if err == nil { + t.Fatal("Should have failed due to 404") + } + failIfNotStatusCode(t, resp, 404) + + // Verify that our count reset back to the initial value + auths, resp, err = client.Authorizations.List(context.Background(), nil) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + if len(auths) != initialAuthCount { + t.Fatalf("The number of Authorizations should match the initial count Expected [%v], got [%v]", initialAuthCount, len(auths)) + } + +} + +// TestAuthorizationsAppOperations tests the application/token related operations, such +// as creating, testing, resetting and revoking application OAuth tokens. +func TestAuthorizationsAppOperations(t *testing.T) { + + userAuthenticatedClient := getUserPassClient(t) + + appAuthenticatedClient := getOAuthAppClient(t) + + // We know these vars are set because getOAuthAppClient would have + // skipped the test by now + clientID := os.Getenv(envKeyClientID) + clientSecret := os.Getenv(envKeyClientSecret) + + authRequest := generateAppAuthTokenRequest(clientID, clientSecret) + + createdAuth, resp, err := userAuthenticatedClient.Authorizations.GetOrCreateForApp(context.Background(), clientID, authRequest) + failOnError(t, err) + failIfNotStatusCode(t, resp, 201) + + // Quick sanity check: + if *createdAuth.Note != *authRequest.Note { + t.Fatal("The returned auth does not match expected value.") + } + + // Let's try the same request again, this time it should return the same + // auth instead of creating a new one + secondAuth, resp, err := userAuthenticatedClient.Authorizations.GetOrCreateForApp(context.Background(), clientID, authRequest) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + // Verify that the IDs are the same + if *createdAuth.ID != *secondAuth.ID { + t.Fatalf("The ID of the second returned auth should be the same as the first. Expected [%v], got [%v]", createdAuth.ID, secondAuth.ID) + } + + // Verify the token + appAuth, resp, err := appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *createdAuth.Token) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + // Quick sanity check + if *appAuth.ID != *createdAuth.ID || *appAuth.Token != *createdAuth.Token { + t.Fatal("The returned auth/token does not match.") + } + + // Let's verify that we get a 404 for a non-existent token + _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, InvalidTokenValue) + if err == nil { + t.Fatal("An error should have been returned because of the invalid token.") + } + failIfNotStatusCode(t, resp, 404) + + // Let's reset the token + resetAuth, resp, err := appAuthenticatedClient.Authorizations.Reset(context.Background(), clientID, *createdAuth.Token) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + // Let's verify that we get a 404 for a non-existent token + _, resp, err = appAuthenticatedClient.Authorizations.Reset(context.Background(), clientID, InvalidTokenValue) + if err == nil { + t.Fatal("An error should have been returned because of the invalid token.") + } + failIfNotStatusCode(t, resp, 404) + + // Verify that the token has changed + if resetAuth.Token == createdAuth.Token { + t.Fatal("The reset token should be different from the original.") + } + + // Verify that we do have a token value + if *resetAuth.Token == "" { + t.Fatal("A token value should have been returned.") + } + + // Verify that the original token is now invalid + _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *createdAuth.Token) + if err == nil { + t.Fatal("The original token should be invalid.") + } + failIfNotStatusCode(t, resp, 404) + + // Check that the reset token is valid + _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *resetAuth.Token) + failOnError(t, err) + failIfNotStatusCode(t, resp, 200) + + // Let's revoke the token + resp, err = appAuthenticatedClient.Authorizations.Revoke(context.Background(), clientID, *resetAuth.Token) + failOnError(t, err) + failIfNotStatusCode(t, resp, 204) + + // Sleep for two seconds... I've seen cases where the revocation appears not + // to have take place immediately. + time.Sleep(time.Second * 2) + + // Now, the reset token should also be invalid + _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *resetAuth.Token) + if err == nil { + t.Fatal("The reset token should be invalid.") + } + failIfNotStatusCode(t, resp, 404) +} + +// generatePersonalAuthTokenRequest is a helper function that generates an +// AuthorizationRequest for a Personal Access Token (no client id). +func generatePersonalAuthTokenRequest() *github.AuthorizationRequest { + + rand := randString() + auth := github.AuthorizationRequest{ + Note: github.String("Personal token: Note generated by test: " + rand), + Scopes: []github.Scope{github.ScopePublicRepo}, + Fingerprint: github.String("Personal token: Fingerprint generated by test: " + rand), + } + + return &auth +} + +// generatePersonalAuthTokenRequest is a helper function that generates an +// AuthorizationRequest for an OAuth application Token (uses client id). +func generateAppAuthTokenRequest(clientID string, clientSecret string) *github.AuthorizationRequest { + + rand := randString() + auth := github.AuthorizationRequest{ + Note: github.String("App token: Note generated by test: " + rand), + Scopes: []github.Scope{github.ScopePublicRepo}, + Fingerprint: github.String("App token: Fingerprint generated by test: " + rand), + ClientID: github.String(clientID), + ClientSecret: github.String(clientSecret), + } + + return &auth +} + +// randString returns a (kinda) random string for uniqueness purposes. +func randString() string { + return strconv.FormatInt(rand.NewSource(time.Now().UnixNano()).Int63(), 10) +} + +// failOnError invokes t.Fatal() if err is present. +func failOnError(t *testing.T, err error) { + + if err != nil { + t.Fatal(err) + } +} + +// failIfNotStatusCode invokes t.Fatal() if the response's status code doesn't match the expected code. +func failIfNotStatusCode(t *testing.T, resp *github.Response, expectedCode int) { + + if resp.StatusCode != expectedCode { + t.Fatalf("Expected HTTP status code [%v] but received [%v]", expectedCode, resp.StatusCode) + } + +} + +// getUserPassClient returns a GitHub client for authorization testing. The client +// uses BasicAuth via GH username and password passed in environment variables +// (and will skip the calling test if those vars are not present). +func getUserPassClient(t *testing.T) *github.Client { + username, ok := os.LookupEnv(envKeyGitHubUsername) + if !ok { + t.Skipf(msgEnvMissing, envKeyGitHubUsername) + } + + password, ok := os.LookupEnv(envKeyGitHubPassword) + if !ok { + t.Skipf(msgEnvMissing, envKeyGitHubPassword) + } + + tp := github.BasicAuthTransport{ + Username: strings.TrimSpace(username), + Password: strings.TrimSpace(password), + } + + return github.NewClient(tp.Client()) +} + +// getOAuthAppClient returns a GitHub client for authorization testing. The client +// uses BasicAuth, but instead of username and password, it uses the client id +// and client secret passed in via environment variables +// (and will skip the calling test if those vars are not present). Certain API operations (check +// an authorization; reset an authorization; revoke an authorization for an app) +// require this authentication mechanism. +// +// See GitHub API docs: https://developer.com/v3/oauth_authorizations/#check-an-authorization +func getOAuthAppClient(t *testing.T) *github.Client { + + username, ok := os.LookupEnv(envKeyClientID) + if !ok { + t.Skipf(msgEnvMissing, envKeyClientID) + } + + password, ok := os.LookupEnv(envKeyClientSecret) + if !ok { + t.Skipf(msgEnvMissing, envKeyClientSecret) + } + + tp := github.BasicAuthTransport{ + Username: strings.TrimSpace(username), + Password: strings.TrimSpace(password), + } + + return github.NewClient(tp.Client()) +} diff --git a/vendor/github.com/google/go-github/test/integration/doc.go b/vendor/github.com/google/go-github/test/integration/doc.go new file mode 100644 index 00000000..5ee470c5 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/doc.go @@ -0,0 +1,11 @@ +// Copyright 2016 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package integration contains integration tests. +// +// These tests call the live GitHub API, and therefore require a little more +// setup to run. See https://github.com/google/go-github/tree/master/test#integration +// for more information. +package integration diff --git a/vendor/github.com/google/go-github/test/integration/github_test.go b/vendor/github.com/google/go-github/test/integration/github_test.go new file mode 100644 index 00000000..489d38a4 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/github_test.go @@ -0,0 +1,84 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "fmt" + "math/rand" + "net/http" + "os" + + "github.com/google/go-github/github" + "golang.org/x/oauth2" +) + +var ( + client *github.Client + + // auth indicates whether tests are being run with an OAuth token. + // Tests can use this flag to skip certain tests when run without auth. + auth bool +) + +func init() { + token := os.Getenv("GITHUB_AUTH_TOKEN") + if token == "" { + print("!!! No OAuth token. Some tests won't run. !!!\n\n") + client = github.NewClient(nil) + } else { + tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + )) + client = github.NewClient(tc) + auth = true + } + + // Environment variables required for Authorization integration tests + vars := []string{envKeyGitHubUsername, envKeyGitHubPassword, envKeyClientID, envKeyClientSecret} + + for _, v := range vars { + value := os.Getenv(v) + if value == "" { + print("!!! " + fmt.Sprintf(msgEnvMissing, v) + " !!!\n\n") + } + } + +} + +func checkAuth(name string) bool { + if !auth { + fmt.Printf("No auth - skipping portions of %v\n", name) + } + return auth +} + +func createRandomTestRepository(owner string, autoinit bool) (*github.Repository, error) { + // create random repo name that does not currently exist + var repoName string + for { + repoName = fmt.Sprintf("test-%d", rand.Int()) + _, resp, err := client.Repositories.Get(context.Background(), owner, repoName) + if err != nil { + if resp.StatusCode == http.StatusNotFound { + // found a non-existent repo, perfect + break + } + + return nil, err + } + } + + // create the repository + repo, _, err := client.Repositories.Create(context.Background(), "", &github.Repository{Name: github.String(repoName), AutoInit: github.Bool(autoinit)}) + if err != nil { + return nil, err + } + + return repo, nil +} diff --git a/vendor/github.com/google/go-github/test/integration/issues_test.go b/vendor/github.com/google/go-github/test/integration/issues_test.go new file mode 100644 index 00000000..a5e48900 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/issues_test.go @@ -0,0 +1,42 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "testing" +) + +func TestIssueEvents(t *testing.T) { + events, _, err := client.Issues.ListRepositoryEvents(context.Background(), "google", "go-github", nil) + if err != nil { + t.Fatalf("Issues.ListRepositoryEvents returned error: %v", err) + } + + if len(events) == 0 { + t.Errorf("ListRepositoryEvents returned no events") + } + + events, _, err = client.Issues.ListIssueEvents(context.Background(), "google", "go-github", 1, nil) + if err != nil { + t.Fatalf("Issues.ListIssueEvents returned error: %v", err) + } + + if len(events) == 0 { + t.Errorf("ListIssueEvents returned no events") + } + + event, _, err := client.Issues.GetEvent(context.Background(), "google", "go-github", *events[0].ID) + if err != nil { + t.Fatalf("Issues.GetEvent returned error: %v", err) + } + + if *event.URL != *events[0].URL { + t.Fatalf("Issues.GetEvent returned event URL: %v, want %v", *event.URL, *events[0].URL) + } +} diff --git a/vendor/github.com/google/go-github/test/integration/misc_test.go b/vendor/github.com/google/go-github/test/integration/misc_test.go new file mode 100644 index 00000000..1595cf07 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/misc_test.go @@ -0,0 +1,79 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "testing" + "time" +) + +func TestEmojis(t *testing.T) { + emoji, _, err := client.ListEmojis(context.Background()) + if err != nil { + t.Fatalf("ListEmojis returned error: %v", err) + } + + if len(emoji) == 0 { + t.Errorf("ListEmojis returned no emojis") + } + + if _, ok := emoji["+1"]; !ok { + t.Errorf("ListEmojis missing '+1' emoji") + } +} + +func TestAPIMeta(t *testing.T) { + meta, _, err := client.APIMeta(context.Background()) + if err != nil { + t.Fatalf("APIMeta returned error: %v", err) + } + + if len(meta.Hooks) == 0 { + t.Errorf("APIMeta returned no hook addresses") + } + + if len(meta.Git) == 0 { + t.Errorf("APIMeta returned no git addresses") + } + + if !*meta.VerifiablePasswordAuthentication { + t.Errorf("APIMeta VerifiablePasswordAuthentication is false") + } +} + +func TestRateLimits(t *testing.T) { + limits, _, err := client.RateLimits(context.Background()) + if err != nil { + t.Fatalf("RateLimits returned error: %v", err) + } + + // do some sanity checks + if limits.Core.Limit == 0 { + t.Errorf("RateLimits returned 0 core limit") + } + + if limits.Core.Limit < limits.Core.Remaining { + t.Errorf("Core.Limits is less than Core.Remaining.") + } + + if limits.Core.Reset.Time.Before(time.Now().Add(-1 * time.Minute)) { + t.Errorf("Core.Reset is more than 1 minute in the past; that doesn't seem right.") + } +} + +func TestListServiceHooks(t *testing.T) { + hooks, _, err := client.ListServiceHooks(context.Background()) + if err != nil { + t.Fatalf("ListServiceHooks returned error: %v", err) + } + + if len(hooks) == 0 { + t.Fatalf("ListServiceHooks returned no hooks") + } +} diff --git a/vendor/github.com/google/go-github/test/integration/pulls_test.go b/vendor/github.com/google/go-github/test/integration/pulls_test.go new file mode 100644 index 00000000..4aadfbd7 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/pulls_test.go @@ -0,0 +1,28 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "testing" +) + +func TestPullRequests_ListCommits(t *testing.T) { + commits, _, err := client.PullRequests.ListCommits(context.Background(), "google", "go-github", 2, nil) + if err != nil { + t.Fatalf("PullRequests.ListCommits() returned error: %v", err) + } + + if got, want := len(commits), 3; got != want { + t.Fatalf("PullRequests.ListCommits() returned %d commits, want %d", got, want) + } + + if got, want := *commits[0].Author.Login, "sqs"; got != want { + t.Fatalf("PullRequests.ListCommits()[0].Author.Login returned %v, want %v", got, want) + } +} diff --git a/vendor/github.com/google/go-github/test/integration/repos_test.go b/vendor/github.com/google/go-github/test/integration/repos_test.go new file mode 100644 index 00000000..9f3d2322 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/repos_test.go @@ -0,0 +1,190 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "net/http" + "reflect" + "testing" + + "github.com/google/go-github/github" +) + +func TestRepositories_CRUD(t *testing.T) { + if !checkAuth("TestRepositories_CRUD") { + return + } + + // get authenticated user + me, _, err := client.Users.Get(context.Background(), "") + if err != nil { + t.Fatalf("Users.Get('') returned error: %v", err) + } + + repo, err := createRandomTestRepository(*me.Login, false) + if err != nil { + t.Fatalf("createRandomTestRepository returned error: %v", err) + } + + // update the repository description + repo.Description = github.String("description") + repo.DefaultBranch = nil // FIXME: this shouldn't be necessary + _, _, err = client.Repositories.Edit(context.Background(), *repo.Owner.Login, *repo.Name, repo) + if err != nil { + t.Fatalf("Repositories.Edit() returned error: %v", err) + } + + // delete the repository + _, err = client.Repositories.Delete(context.Background(), *repo.Owner.Login, *repo.Name) + if err != nil { + t.Fatalf("Repositories.Delete() returned error: %v", err) + } + + // verify that the repository was deleted + _, resp, err := client.Repositories.Get(context.Background(), *repo.Owner.Login, *repo.Name) + if err == nil { + t.Fatalf("Test repository still exists after deleting it.") + } + if err != nil && resp.StatusCode != http.StatusNotFound { + t.Fatalf("Repositories.Get() returned error: %v", err) + } +} + +func TestRepositories_BranchesTags(t *testing.T) { + // branches + branches, _, err := client.Repositories.ListBranches(context.Background(), "git", "git", nil) + if err != nil { + t.Fatalf("Repositories.ListBranches() returned error: %v", err) + } + + if len(branches) == 0 { + t.Fatalf("Repositories.ListBranches('git', 'git') returned no branches") + } + + _, _, err = client.Repositories.GetBranch(context.Background(), "git", "git", *branches[0].Name) + if err != nil { + t.Fatalf("Repositories.GetBranch() returned error: %v", err) + } + + // tags + tags, _, err := client.Repositories.ListTags(context.Background(), "git", "git", nil) + if err != nil { + t.Fatalf("Repositories.ListTags() returned error: %v", err) + } + + if len(tags) == 0 { + t.Fatalf("Repositories.ListTags('git', 'git') returned no tags") + } +} + +func TestRepositories_EditBranches(t *testing.T) { + if !checkAuth("TestRepositories_EditBranches") { + return + } + + // get authenticated user + me, _, err := client.Users.Get(context.Background(), "") + if err != nil { + t.Fatalf("Users.Get('') returned error: %v", err) + } + + repo, err := createRandomTestRepository(*me.Login, true) + if err != nil { + t.Fatalf("createRandomTestRepository returned error: %v", err) + } + + branch, _, err := client.Repositories.GetBranch(context.Background(), *repo.Owner.Login, *repo.Name, "master") + if err != nil { + t.Fatalf("Repositories.GetBranch() returned error: %v", err) + } + + if *branch.Protected { + t.Fatalf("Branch %v of repo %v is already protected", "master", *repo.Name) + } + + // TODO: This test fails with 422 Validation Failed [{Resource: Field: Code: Message:}]. + // Someone familiar with protection requests needs to come up with + // a valid protection request that doesn't give 422 error. + protectionRequest := &github.ProtectionRequest{ + RequiredStatusChecks: &github.RequiredStatusChecks{ + Strict: true, + Contexts: []string{"continuous-integration"}, + }, + RequiredPullRequestReviews: &github.PullRequestReviewsEnforcementRequest{ + DismissalRestrictionsRequest: &github.DismissalRestrictionsRequest{ + Users: []string{}, + Teams: []string{}, + }, + DismissStaleReviews: true, + }, + EnforceAdmins: true, + // TODO: Only organization repositories can have users and team restrictions. + // In order to be able to test these Restrictions, need to add support + // for creating temporary organization repositories. + Restrictions: nil, + } + + protection, _, err := client.Repositories.UpdateBranchProtection(context.Background(), *repo.Owner.Login, *repo.Name, "master", protectionRequest) + if err != nil { + t.Fatalf("Repositories.UpdateBranchProtection() returned error: %v", err) + } + + want := &github.Protection{ + RequiredStatusChecks: &github.RequiredStatusChecks{ + Strict: true, + Contexts: []string{"continuous-integration"}, + }, + RequiredPullRequestReviews: &github.PullRequestReviewsEnforcement{ + DismissalRestrictions: github.DismissalRestrictions{ + Users: []*github.User{}, + Teams: []*github.Team{}, + }, + DismissStaleReviews: true, + }, + EnforceAdmins: &github.AdminEnforcement{ + Enabled: true, + }, + Restrictions: nil, + } + if !reflect.DeepEqual(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection() returned %+v, want %+v", protection, want) + } + + _, err = client.Repositories.Delete(context.Background(), *repo.Owner.Login, *repo.Name) + if err != nil { + t.Fatalf("Repositories.Delete() returned error: %v", err) + } +} + +func TestRepositories_List(t *testing.T) { + if !checkAuth("TestRepositories_List") { + return + } + + _, _, err := client.Repositories.List(context.Background(), "", nil) + if err != nil { + t.Fatalf("Repositories.List('') returned error: %v", err) + } + + _, _, err = client.Repositories.List(context.Background(), "google", nil) + if err != nil { + t.Fatalf("Repositories.List('google') returned error: %v", err) + } + + opt := github.RepositoryListOptions{Sort: "created"} + repos, _, err := client.Repositories.List(context.Background(), "google", &opt) + if err != nil { + t.Fatalf("Repositories.List('google') with Sort opt returned error: %v", err) + } + for i, repo := range repos { + if i > 0 && (*repos[i-1].CreatedAt).Time.Before((*repo.CreatedAt).Time) { + t.Fatalf("Repositories.List('google') with default descending Sort returned incorrect order") + } + } +} diff --git a/vendor/github.com/google/go-github/test/integration/users_test.go b/vendor/github.com/google/go-github/test/integration/users_test.go new file mode 100644 index 00000000..3c47c369 --- /dev/null +++ b/vendor/github.com/google/go-github/test/integration/users_test.go @@ -0,0 +1,244 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build integration + +package integration + +import ( + "context" + "fmt" + "math/rand" + "testing" + + "github.com/google/go-github/github" +) + +func TestUsers_Get(t *testing.T) { + // list all users + users, _, err := client.Users.ListAll(context.Background(), nil) + if err != nil { + t.Fatalf("Users.ListAll returned error: %v", err) + } + + if len(users) == 0 { + t.Errorf("Users.ListAll returned no users") + } + + // mojombo is user #1 + if want := "mojombo"; want != *users[0].Login { + t.Errorf("user[0].Login was %q, wanted %q", *users[0].Login, want) + } + + // get individual user + u, _, err := client.Users.Get(context.Background(), "octocat") + if err != nil { + t.Fatalf("Users.Get('octocat') returned error: %v", err) + } + + if want := "octocat"; want != *u.Login { + t.Errorf("user.Login was %q, wanted %q", *u.Login, want) + } + if want := "The Octocat"; want != *u.Name { + t.Errorf("user.Name was %q, wanted %q", *u.Name, want) + } +} + +func TestUsers_Update(t *testing.T) { + if !checkAuth("TestUsers_Get") { + return + } + + u, _, err := client.Users.Get(context.Background(), "") + if err != nil { + t.Fatalf("Users.Get('') returned error: %v", err) + } + + if *u.Login == "" { + t.Errorf("wanted non-empty values for user.Login") + } + + // save original location + var location string + if u.Location != nil { + location = *u.Location + } + + // update location to test value + testLoc := fmt.Sprintf("test-%d", rand.Int()) + u.Location = &testLoc + + _, _, err = client.Users.Edit(context.Background(), u) + if err != nil { + t.Fatalf("Users.Update returned error: %v", err) + } + + // refetch user and check location value + u, _, err = client.Users.Get(context.Background(), "") + if err != nil { + t.Fatalf("Users.Get('') returned error: %v", err) + } + + if testLoc != *u.Location { + t.Errorf("Users.Get('') has location: %v, want: %v", *u.Location, testLoc) + } + + // set location back to the original value + u.Location = &location + _, _, err = client.Users.Edit(context.Background(), u) + if err != nil { + t.Fatalf("Users.Edit returned error: %v", err) + } +} + +func TestUsers_Emails(t *testing.T) { + if !checkAuth("TestUsers_Emails") { + return + } + + emails, _, err := client.Users.ListEmails(context.Background(), nil) + if err != nil { + t.Fatalf("Users.ListEmails() returned error: %v", err) + } + + // create random address not currently in user's emails + var email string +EmailLoop: + for { + email = fmt.Sprintf("test-%d@example.com", rand.Int()) + for _, e := range emails { + if e.Email != nil && *e.Email == email { + continue EmailLoop + } + } + break + } + + // Add new address + _, _, err = client.Users.AddEmails(context.Background(), []string{email}) + if err != nil { + t.Fatalf("Users.AddEmails() returned error: %v", err) + } + + // List emails again and verify new email is present + emails, _, err = client.Users.ListEmails(context.Background(), nil) + if err != nil { + t.Fatalf("Users.ListEmails() returned error: %v", err) + } + + var found bool + for _, e := range emails { + if e.Email != nil && *e.Email == email { + found = true + break + } + } + + if !found { + t.Fatalf("Users.ListEmails() does not contain new address: %v", email) + } + + // Remove new address + _, err = client.Users.DeleteEmails(context.Background(), []string{email}) + if err != nil { + t.Fatalf("Users.DeleteEmails() returned error: %v", err) + } + + // List emails again and verify new email was removed + emails, _, err = client.Users.ListEmails(context.Background(), nil) + if err != nil { + t.Fatalf("Users.ListEmails() returned error: %v", err) + } + + for _, e := range emails { + if e.Email != nil && *e.Email == email { + t.Fatalf("Users.ListEmails() still contains address %v after removing it", email) + } + } +} + +func TestUsers_Keys(t *testing.T) { + keys, _, err := client.Users.ListKeys(context.Background(), "willnorris", nil) + if err != nil { + t.Fatalf("Users.ListKeys('willnorris') returned error: %v", err) + } + + if len(keys) == 0 { + t.Errorf("Users.ListKeys('willnorris') returned no keys") + } + + // the rest of the tests requires auth + if !checkAuth("TestUsers_Keys") { + return + } + + // TODO: make this integration test work for any authenticated user. + keys, _, err = client.Users.ListKeys(context.Background(), "", nil) + if err != nil { + t.Fatalf("Users.ListKeys('') returned error: %v", err) + } + + // ssh public key for testing (fingerprint: a7:22:ad:8c:36:9f:68:65:eb:ae:a1:e4:59:73:c1:76) + key := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCy/RIqaMFj2wjkOEjx9EAU0ReLAIhodga82/feo5nnT9UUkHLbL9xrIavfdLHx28lD3xYgPfAoSicUMaAeNwuQhmuerr2c2LFGxzrdXP8pVsQ+Ol7y7OdmFPfe0KrzoZaLJs9aSiZ4VKyY4z5Se/k2UgcJTdgQVlLfw/P96aqCx8yUu94BiWqkDqYEvgWKRNHrTiIo1EXeVBCCcfgNZe1suFfNJUJSUU2T3EG2bpwBbSOCjE3FyH8+Lz3K3BOGzm3df8E7Regj9j4YIcD8cWJYO86jLJoGgQ0L5MSOq+ishNaHQXech22Ix03D1lVMjCvDT7S/C94Z1LzhI2lhvyff" + for _, k := range keys { + if k.Key != nil && *k.Key == key { + t.Fatalf("Test key already exists for user. Please manually remove it first.") + } + } + + // Add new key + _, _, err = client.Users.CreateKey(context.Background(), &github.Key{ + Title: github.String("go-github test key"), + Key: github.String(key), + }) + if err != nil { + t.Fatalf("Users.CreateKey() returned error: %v", err) + } + + // List keys again and verify new key is present + keys, _, err = client.Users.ListKeys(context.Background(), "", nil) + if err != nil { + t.Fatalf("Users.ListKeys('') returned error: %v", err) + } + + var id int64 + for _, k := range keys { + if k.Key != nil && *k.Key == key { + id = *k.ID + break + } + } + + if id == 0 { + t.Fatalf("Users.ListKeys('') does not contain added test key") + } + + // Verify that fetching individual key works + k, _, err := client.Users.GetKey(context.Background(), id) + if err != nil { + t.Fatalf("Users.GetKey(%q) returned error: %v", id, err) + } + if *k.Key != key { + t.Fatalf("Users.GetKey(%q) returned key %v, want %v", id, *k.Key, key) + } + + // Remove test key + _, err = client.Users.DeleteKey(context.Background(), id) + if err != nil { + t.Fatalf("Users.DeleteKey(%d) returned error: %v", id, err) + } + + // List keys again and verify test key was removed + keys, _, err = client.Users.ListKeys(context.Background(), "", nil) + if err != nil { + t.Fatalf("Users.ListKeys('') returned error: %v", err) + } + + for _, k := range keys { + if k.Key != nil && *k.Key == key { + t.Fatalf("Users.ListKeys('') still contains test key after removing it") + } + } +} diff --git a/vendor/github.com/google/go-querystring/.gitignore b/vendor/github.com/google/go-querystring/.gitignore new file mode 100644 index 00000000..9ed3b07c --- /dev/null +++ b/vendor/github.com/google/go-querystring/.gitignore @@ -0,0 +1 @@ +*.test diff --git a/vendor/github.com/google/go-querystring/CONTRIBUTING.md b/vendor/github.com/google/go-querystring/CONTRIBUTING.md new file mode 100644 index 00000000..51cf5cd1 --- /dev/null +++ b/vendor/github.com/google/go-querystring/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# How to contribute # + +We'd love to accept your patches and contributions to this project. There are +a just a few small guidelines you need to follow. + + +## Contributor License Agreement ## + +Contributions to any Google project must be accompanied by a Contributor +License Agreement. This is not a copyright **assignment**, it simply gives +Google permission to use and redistribute your contributions as part of the +project. + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual + CLA][]. + + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA][]. + +You generally only need to submit a CLA once, so if you've already submitted +one (even if it was for a different project), you probably don't need to do it +again. + +[individual CLA]: https://developers.google.com/open-source/cla/individual +[corporate CLA]: https://developers.google.com/open-source/cla/corporate + + +## Submitting a patch ## + + 1. It's generally best to start by opening a new issue describing the bug or + feature you're intending to fix. Even if you think it's relatively minor, + it's helpful to know what people are working on. Mention in the initial + issue that you are planning to work on that bug or feature so that it can + be assigned to you. + + 1. Follow the normal process of [forking][] the project, and setup a new + branch to work in. It's important that each group of changes be done in + separate branches in order to ensure that a pull request only includes the + commits related to that bug or feature. + + 1. Go makes it very simple to ensure properly formatted code, so always run + `go fmt` on your code before committing it. You should also run + [golint][] over your code. As noted in the [golint readme][], it's not + strictly necessary that your code be completely "lint-free", but this will + help you find common style issues. + + 1. Any significant changes should almost always be accompanied by tests. The + project already has good test coverage, so look at some of the existing + tests if you're unsure how to go about it. [gocov][] and [gocov-html][] + are invaluable tools for seeing which parts of your code aren't being + exercised by your tests. + + 1. Do your best to have [well-formed commit messages][] for each change. + This provides consistency throughout the project, and ensures that commit + messages are able to be formatted properly by various git tools. + + 1. Finally, push the commits to your fork and submit a [pull request][]. + +[forking]: https://help.github.com/articles/fork-a-repo +[golint]: https://github.com/golang/lint +[golint readme]: https://github.com/golang/lint/blob/master/README +[gocov]: https://github.com/axw/gocov +[gocov-html]: https://github.com/matm/gocov-html +[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits +[pull request]: https://help.github.com/articles/creating-a-pull-request diff --git a/vendor/github.com/google/go-querystring/LICENSE b/vendor/github.com/google/go-querystring/LICENSE new file mode 100644 index 00000000..ae121a1e --- /dev/null +++ b/vendor/github.com/google/go-querystring/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013 Google. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/go-querystring/README.md b/vendor/github.com/google/go-querystring/README.md new file mode 100644 index 00000000..03e93703 --- /dev/null +++ b/vendor/github.com/google/go-querystring/README.md @@ -0,0 +1,39 @@ +# go-querystring # + +go-querystring is Go library for encoding structs into URL query parameters. + + +**Documentation:** +**Build Status:** [![Build Status](https://drone.io/github.com/google/go-querystring/status.png)](https://drone.io/github.com/google/go-querystring/latest) + +## Usage ## + +```go +import "github.com/google/go-querystring/query" +``` + +go-querystring is designed to assist in scenarios where you want to construct a +URL using a struct that represents the URL query parameters. You might do this +to enforce the type safety of your parameters, for example, as is done in the +[go-github][] library. + +The query package exports a single `Values()` function. A simple example: + +```go +type Options struct { + Query string `url:"q"` + ShowAll bool `url:"all"` + Page int `url:"page"` +} + +opt := Options{ "foo", true, 2 } +v, _ := query.Values(opt) +fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" +``` + +[go-github]: https://github.com/google/go-github/commit/994f6f8405f052a117d2d0b500054341048fbb08 + +## License ## + +This library is distributed under the BSD-style license found in the [LICENSE](./LICENSE) +file. diff --git a/vendor/github.com/google/go-querystring/query/encode.go b/vendor/github.com/google/go-querystring/query/encode.go new file mode 100644 index 00000000..37080b19 --- /dev/null +++ b/vendor/github.com/google/go-querystring/query/encode.go @@ -0,0 +1,320 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package query implements encoding of structs into URL query parameters. +// +// As a simple example: +// +// type Options struct { +// Query string `url:"q"` +// ShowAll bool `url:"all"` +// Page int `url:"page"` +// } +// +// opt := Options{ "foo", true, 2 } +// v, _ := query.Values(opt) +// fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" +// +// The exact mapping between Go values and url.Values is described in the +// documentation for the Values() function. +package query + +import ( + "bytes" + "fmt" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +var timeType = reflect.TypeOf(time.Time{}) + +var encoderType = reflect.TypeOf(new(Encoder)).Elem() + +// Encoder is an interface implemented by any type that wishes to encode +// itself into URL values in a non-standard way. +type Encoder interface { + EncodeValues(key string, v *url.Values) error +} + +// Values returns the url.Values encoding of v. +// +// Values expects to be passed a struct, and traverses it recursively using the +// following encoding rules. +// +// Each exported struct field is encoded as a URL parameter unless +// +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option +// +// The empty values are false, 0, any nil pointer or interface value, any array +// slice, map, or string of length zero, and any time.Time that returns true +// for IsZero(). +// +// The URL parameter name defaults to the struct field name but can be +// specified in the struct field's tag value. The "url" key in the struct +// field's tag value is the key name, followed by an optional comma and +// options. For example: +// +// // Field is ignored by this package. +// Field int `url:"-"` +// +// // Field appears as URL parameter "myName". +// Field int `url:"myName"` +// +// // Field appears as URL parameter "myName" and the field is omitted if +// // its value is empty +// Field int `url:"myName,omitempty"` +// +// // Field appears as URL parameter "Field" (the default), but the field +// // is skipped if empty. Note the leading comma. +// Field int `url:",omitempty"` +// +// For encoding individual field values, the following type-dependent rules +// apply: +// +// Boolean values default to encoding as the strings "true" or "false". +// Including the "int" option signals that the field should be encoded as the +// strings "1" or "0". +// +// time.Time values default to encoding as RFC3339 timestamps. Including the +// "unix" option signals that the field should be encoded as a Unix time (see +// time.Unix()) +// +// Slice and Array values default to encoding as multiple URL values of the +// same name. Including the "comma" option signals that the field should be +// encoded as a single comma-delimited value. Including the "space" option +// similarly encodes the value as a single space-delimited string. Including +// the "semicolon" option will encode the value as a semicolon-delimited string. +// Including the "brackets" option signals that the multiple URL values should +// have "[]" appended to the value name. "numbered" will append a number to +// the end of each incidence of the value name, example: +// name0=value0&name1=value1, etc. +// +// Anonymous struct fields are usually encoded as if their inner exported +// fields were fields in the outer struct, subject to the standard Go +// visibility rules. An anonymous struct field with a name given in its URL +// tag is treated as having that name, rather than being anonymous. +// +// Non-nil pointer values are encoded as the value pointed to. +// +// Nested structs are encoded including parent fields in value names for +// scoping. e.g: +// +// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO" +// +// All other values are encoded using their default string representation. +// +// Multiple fields that encode to the same URL parameter name will be included +// as multiple URL values of the same name. +func Values(v interface{}) (url.Values, error) { + values := make(url.Values) + val := reflect.ValueOf(v) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return values, nil + } + val = val.Elem() + } + + if v == nil { + return values, nil + } + + if val.Kind() != reflect.Struct { + return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) + } + + err := reflectValue(values, val, "") + return values, err +} + +// reflectValue populates the values parameter from the struct fields in val. +// Embedded structs are followed recursively (using the rules defined in the +// Values function documentation) breadth-first. +func reflectValue(values url.Values, val reflect.Value, scope string) error { + var embedded []reflect.Value + + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + sf := typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + + sv := val.Field(i) + tag := sf.Tag.Get("url") + if tag == "-" { + continue + } + name, opts := parseTag(tag) + if name == "" { + if sf.Anonymous && sv.Kind() == reflect.Struct { + // save embedded struct for later processing + embedded = append(embedded, sv) + continue + } + + name = sf.Name + } + + if scope != "" { + name = scope + "[" + name + "]" + } + + if opts.Contains("omitempty") && isEmptyValue(sv) { + continue + } + + if sv.Type().Implements(encoderType) { + if !reflect.Indirect(sv).IsValid() { + sv = reflect.New(sv.Type().Elem()) + } + + m := sv.Interface().(Encoder) + if err := m.EncodeValues(name, &values); err != nil { + return err + } + continue + } + + if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + var del byte + if opts.Contains("comma") { + del = ',' + } else if opts.Contains("space") { + del = ' ' + } else if opts.Contains("semicolon") { + del = ';' + } else if opts.Contains("brackets") { + name = name + "[]" + } + + if del != 0 { + s := new(bytes.Buffer) + first := true + for i := 0; i < sv.Len(); i++ { + if first { + first = false + } else { + s.WriteByte(del) + } + s.WriteString(valueString(sv.Index(i), opts)) + } + values.Add(name, s.String()) + } else { + for i := 0; i < sv.Len(); i++ { + k := name + if opts.Contains("numbered") { + k = fmt.Sprintf("%s%d", name, i) + } + values.Add(k, valueString(sv.Index(i), opts)) + } + } + continue + } + + for sv.Kind() == reflect.Ptr { + if sv.IsNil() { + break + } + sv = sv.Elem() + } + + if sv.Type() == timeType { + values.Add(name, valueString(sv, opts)) + continue + } + + if sv.Kind() == reflect.Struct { + reflectValue(values, sv, name) + continue + } + + values.Add(name, valueString(sv, opts)) + } + + for _, f := range embedded { + if err := reflectValue(values, f, scope); err != nil { + return err + } + } + + return nil +} + +// valueString returns the string representation of a value. +func valueString(v reflect.Value, opts tagOptions) string { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + v = v.Elem() + } + + if v.Kind() == reflect.Bool && opts.Contains("int") { + if v.Bool() { + return "1" + } + return "0" + } + + if v.Type() == timeType { + t := v.Interface().(time.Time) + if opts.Contains("unix") { + return strconv.FormatInt(t.Unix(), 10) + } + return t.Format(time.RFC3339) + } + + return fmt.Sprint(v.Interface()) +} + +// isEmptyValue checks if a value should be considered empty for the purposes +// of omitting fields with the "omitempty" option. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + + if v.Type() == timeType { + return v.Interface().(time.Time).IsZero() + } + + return false +} + +// tagOptions is the string following a comma in a struct field's "url" tag, or +// the empty string. It does not include the leading comma. +type tagOptions []string + +// parseTag splits a struct field's url tag into its name and comma-separated +// options. +func parseTag(tag string) (string, tagOptions) { + s := strings.Split(tag, ",") + return s[0], s[1:] +} + +// Contains checks whether the tagOptions contains the specified option. +func (o tagOptions) Contains(option string) bool { + for _, s := range o { + if s == option { + return true + } + } + return false +} diff --git a/vendor/github.com/google/go-querystring/query/encode_test.go b/vendor/github.com/google/go-querystring/query/encode_test.go new file mode 100644 index 00000000..0f26a775 --- /dev/null +++ b/vendor/github.com/google/go-querystring/query/encode_test.go @@ -0,0 +1,328 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package query + +import ( + "fmt" + "net/url" + "reflect" + "testing" + "time" +) + +type Nested struct { + A SubNested `url:"a"` + B *SubNested `url:"b"` + Ptr *SubNested `url:"ptr,omitempty"` +} + +type SubNested struct { + Value string `url:"value"` +} + +func TestValues_types(t *testing.T) { + str := "string" + strPtr := &str + timeVal := time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC) + + tests := []struct { + in interface{} + want url.Values + }{ + { + // basic primitives + struct { + A string + B int + C uint + D float32 + E bool + }{}, + url.Values{ + "A": {""}, + "B": {"0"}, + "C": {"0"}, + "D": {"0"}, + "E": {"false"}, + }, + }, + { + // pointers + struct { + A *string + B *int + C **string + D *time.Time + }{ + A: strPtr, + C: &strPtr, + D: &timeVal, + }, + url.Values{ + "A": {str}, + "B": {""}, + "C": {str}, + "D": {"2000-01-01T12:34:56Z"}, + }, + }, + { + // slices and arrays + struct { + A []string + B []string `url:",comma"` + C []string `url:",space"` + D [2]string + E [2]string `url:",comma"` + F [2]string `url:",space"` + G []*string `url:",space"` + H []bool `url:",int,space"` + I []string `url:",brackets"` + J []string `url:",semicolon"` + K []string `url:",numbered"` + }{ + A: []string{"a", "b"}, + B: []string{"a", "b"}, + C: []string{"a", "b"}, + D: [2]string{"a", "b"}, + E: [2]string{"a", "b"}, + F: [2]string{"a", "b"}, + G: []*string{&str, &str}, + H: []bool{true, false}, + I: []string{"a", "b"}, + J: []string{"a", "b"}, + K: []string{"a", "b"}, + }, + url.Values{ + "A": {"a", "b"}, + "B": {"a,b"}, + "C": {"a b"}, + "D": {"a", "b"}, + "E": {"a,b"}, + "F": {"a b"}, + "G": {"string string"}, + "H": {"1 0"}, + "I[]": {"a", "b"}, + "J": {"a;b"}, + "K0": {"a"}, + "K1": {"b"}, + }, + }, + { + // other types + struct { + A time.Time + B time.Time `url:",unix"` + C bool `url:",int"` + D bool `url:",int"` + }{ + A: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC), + B: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC), + C: true, + D: false, + }, + url.Values{ + "A": {"2000-01-01T12:34:56Z"}, + "B": {"946730096"}, + "C": {"1"}, + "D": {"0"}, + }, + }, + { + struct { + Nest Nested `url:"nest"` + }{ + Nested{ + A: SubNested{ + Value: "that", + }, + }, + }, + url.Values{ + "nest[a][value]": {"that"}, + "nest[b]": {""}, + }, + }, + { + struct { + Nest Nested `url:"nest"` + }{ + Nested{ + Ptr: &SubNested{ + Value: "that", + }, + }, + }, + url.Values{ + "nest[a][value]": {""}, + "nest[b]": {""}, + "nest[ptr][value]": {"that"}, + }, + }, + { + nil, + url.Values{}, + }, + } + + for i, tt := range tests { + v, err := Values(tt.in) + if err != nil { + t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err) + } + + if !reflect.DeepEqual(tt.want, v) { + t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want) + } + } +} + +func TestValues_omitEmpty(t *testing.T) { + str := "" + s := struct { + a string + A string + B string `url:",omitempty"` + C string `url:"-"` + D string `url:"omitempty"` // actually named omitempty, not an option + E *string `url:",omitempty"` + }{E: &str} + + v, err := Values(s) + if err != nil { + t.Errorf("Values(%q) returned error: %v", s, err) + } + + want := url.Values{ + "A": {""}, + "omitempty": {""}, + "E": {""}, // E is included because the pointer is not empty, even though the string being pointed to is + } + if !reflect.DeepEqual(want, v) { + t.Errorf("Values(%q) returned %v, want %v", s, v, want) + } +} + +type A struct { + B +} + +type B struct { + C string +} + +type D struct { + B + C string +} + +type e struct { + B + C string +} + +type F struct { + e +} + +func TestValues_embeddedStructs(t *testing.T) { + tests := []struct { + in interface{} + want url.Values + }{ + { + A{B{C: "foo"}}, + url.Values{"C": {"foo"}}, + }, + { + D{B: B{C: "bar"}, C: "foo"}, + url.Values{"C": {"foo", "bar"}}, + }, + { + F{e{B: B{C: "bar"}, C: "foo"}}, // With unexported embed + url.Values{"C": {"foo", "bar"}}, + }, + } + + for i, tt := range tests { + v, err := Values(tt.in) + if err != nil { + t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err) + } + + if !reflect.DeepEqual(tt.want, v) { + t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want) + } + } +} + +func TestValues_invalidInput(t *testing.T) { + _, err := Values("") + if err == nil { + t.Errorf("expected Values() to return an error on invalid input") + } +} + +type EncodedArgs []string + +func (m EncodedArgs) EncodeValues(key string, v *url.Values) error { + for i, arg := range m { + v.Set(fmt.Sprintf("%s.%d", key, i), arg) + } + return nil +} + +func TestValues_Marshaler(t *testing.T) { + s := struct { + Args EncodedArgs `url:"arg"` + }{[]string{"a", "b", "c"}} + v, err := Values(s) + if err != nil { + t.Errorf("Values(%q) returned error: %v", s, err) + } + + want := url.Values{ + "arg.0": {"a"}, + "arg.1": {"b"}, + "arg.2": {"c"}, + } + if !reflect.DeepEqual(want, v) { + t.Errorf("Values(%q) returned %v, want %v", s, v, want) + } +} + +func TestValues_MarshalerWithNilPointer(t *testing.T) { + s := struct { + Args *EncodedArgs `url:"arg"` + }{} + v, err := Values(s) + if err != nil { + t.Errorf("Values(%q) returned error: %v", s, err) + } + + want := url.Values{} + if !reflect.DeepEqual(want, v) { + t.Errorf("Values(%q) returned %v, want %v", s, v, want) + } +} + +func TestTagParsing(t *testing.T) { + name, opts := parseTag("field,foobar,foo") + if name != "field" { + t.Fatalf("name = %q, want field", name) + } + for _, tt := range []struct { + opt string + want bool + }{ + {"foobar", true}, + {"foo", true}, + {"bar", false}, + {"field", false}, + } { + if opts.Contains(tt.opt) != tt.want { + t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) + } + } +} diff --git a/vendor/github.com/google/gopacket/.gitignore b/vendor/github.com/google/gopacket/.gitignore new file mode 100644 index 00000000..149266fd --- /dev/null +++ b/vendor/github.com/google/gopacket/.gitignore @@ -0,0 +1,38 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +#* +*~ + +# examples binaries +examples/synscan/synscan +examples/pfdump/pfdump +examples/pcapdump/pcapdump +examples/httpassembly/httpassembly +examples/statsassembly/statsassembly +examples/arpscan/arpscan +examples/bidirectional/bidirectional +examples/bytediff/bytediff +examples/reassemblydump/reassemblydump +layers/gen +macs/gen +pcap/pcap_tester diff --git a/vendor/github.com/google/gopacket/.travis.gofmt.sh b/vendor/github.com/google/gopacket/.travis.gofmt.sh new file mode 100755 index 00000000..e341a1cb --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.gofmt.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd "$(dirname $0)" +if [ -n "$(go fmt ./...)" ]; then + echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2 + exit 1 +fi diff --git a/vendor/github.com/google/gopacket/.travis.golint.sh b/vendor/github.com/google/gopacket/.travis.golint.sh new file mode 100755 index 00000000..ed74c65a --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.golint.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +cd "$(dirname $0)" + +go get github.com/golang/lint/golint +DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing" +# Add subdirectories here as we clean up golint on each. +for subdir in $DIRS; do + pushd $subdir + if golint | + grep -v CannotSetRFMon | # pcap exported error name + grep -v DataLost | # tcpassembly/tcpreader exported error name + grep .; then + exit 1 + fi + popd +done + +pushd layers +for file in $(cat .linted); do + if golint $file | grep .; then + exit 1 + fi +done +popd diff --git a/vendor/github.com/google/gopacket/.travis.govet.sh b/vendor/github.com/google/gopacket/.travis.govet.sh new file mode 100755 index 00000000..52ad0841 --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.govet.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +cd "$(dirname $0)" +DIRS=". layers pcap pcapgo pfring tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs" +set -e +for subdir in $DIRS; do + pushd $subdir + go vet + popd +done diff --git a/vendor/github.com/google/gopacket/.travis.yml b/vendor/github.com/google/gopacket/.travis.yml new file mode 100644 index 00000000..7d731262 --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.yml @@ -0,0 +1,14 @@ +language: go +install: + - go get github.com/google/gopacket + - go get github.com/google/gopacket/layers + - go get github.com/google/gopacket/tcpassembly + - go get github.com/google/gopacket/reassembly +script: + - go test github.com/google/gopacket + - go test github.com/google/gopacket/layers + - go test github.com/google/gopacket/tcpassembly + - go test github.com/google/gopacket/reassembly + - ./.travis.gofmt.sh + - ./.travis.govet.sh + - ./.travis.golint.sh diff --git a/vendor/github.com/google/gopacket/AUTHORS b/vendor/github.com/google/gopacket/AUTHORS new file mode 100644 index 00000000..eba34f0f --- /dev/null +++ b/vendor/github.com/google/gopacket/AUTHORS @@ -0,0 +1,46 @@ +AUTHORS AND MAINTAINERS: + +MAIN DEVELOPERS: +Graeme Connell + +AUTHORS: +Nigel Tao +Cole Mickens +Ben Daglish +Luis Martinez +Remco Verhoef +Hiroaki Kawai +Lukas Lueg +Laurent Hausermann +Bill Green + +CONTRIBUTORS: +Attila Oláh +Vittus Mikiassen +Matthias Radestock +Matthew Sackman +Loic Prylli +Alexandre Fiori +Adrian Tam +Satoshi Matsumoto +David Stainton +Jesse Ward +Kane Mathers + +----------------------------------------------- +FORKED FROM github.com/akrennmair/gopcap +ALL THE FOLLOWING ARE FOR THAT PROJECT + +MAIN DEVELOPERS: +Andreas Krennmair + +CONTRIBUTORS: +Andrea Nall +Daniel Arndt +Dustin Sallings +Graeme Connell +Guillaume Savary +Mark Smith +Miek Gieben +Mike Bell +Trevor Strohman diff --git a/vendor/github.com/google/gopacket/CONTRIBUTING.md b/vendor/github.com/google/gopacket/CONTRIBUTING.md new file mode 100644 index 00000000..99ab7a2e --- /dev/null +++ b/vendor/github.com/google/gopacket/CONTRIBUTING.md @@ -0,0 +1,215 @@ +Contributing To gopacket +======================== + +So you've got some code and you'd like it to be part of gopacket... wonderful! +We're happy to accept contributions, whether they're fixes to old protocols, new +protocols entirely, or anything else you think would improve the gopacket +library. This document is designed to help you to do just that. + +The first section deals with the plumbing: how to actually get a change +submitted. + +The second section deals with coding style... Go is great in that it +has a uniform style implemented by 'go fmt', but there's still some decisions +we've made that go above and beyond, and if you follow them, they won't come up +in your code review. + +The third section deals with some of the implementation decisions we've made, +which may help you to understand the current code and which we may ask you to +conform to (or provide compelling reasons for ignoring). + +Overall, we hope this document will help you to understand our system and write +great code which fits in, and help us to turn around on your code review quickly +so the code can make it into the master branch as quickly as possible. + + +How To Submit Code +------------------ + +We use github.com's Pull Request feature to receive code contributions from +external contributors. See +https://help.github.com/articles/creating-a-pull-request/ for details on +how to create a request. + +Also, there's a local script `gc` in the base directory of GoPacket that +runs a local set of checks, which should give you relatively high confidence +that your pull won't fail github pull checks. + +```sh +go get github.com/google/gopacket +cd $GOROOT/src/pkg/github.com/google/gopacket +git checkout -b # create a new branch to work from +... code code code ... +./gc # Run this to do local commits, it performs a number of checks +``` + +To sum up: + +* DO + + Pull down the latest version. + + Make a feature-specific branch. + + Code using the style and methods discussed in the rest of this document. + + Use the ./gc command to do local commits or check correctness. + + Push your new feature branch up to github.com, as a pull request. + + Handle comments and requests from reviewers, pushing new commits up to + your feature branch as problems are addressed. + + Put interesting comments and discussions into commit comments. +* DON'T + + Push to someone else's branch without their permission. + + +Coding Style +------------ + +* Go code must be run through `go fmt`, `go vet`, and `golint` +* Follow http://golang.org/doc/effective_go.html as much as possible. + + In particular, http://golang.org/doc/effective_go.html#mixed-caps. Enums + should be be CamelCase, with acronyms capitalized (TCPSourcePort, vs. + TcpSourcePort or TCP_SOURCE_PORT). +* Bonus points for giving enum types a String() field. +* Any exported types or functions should have commentary + (http://golang.org/doc/effective_go.html#commentary) + + +Coding Methods And Implementation Notes +--------------------------------------- + +### Error Handling + +Many times, you'll be decoding a protocol and run across something bad, a packet +corruption or the like. How do you handle this? First off, ALWAYS report the +error. You can do this either by returning the error from the decode() function +(most common), or if you're up for it you can implement and add an ErrorLayer +through the packet builder (the first method is a simple shortcut that does +exactly this, then stops any future decoding). + +Often, you'll already have decode some part of your protocol by the time you hit +your error. Use your own discretion to determine whether the stuff you've +already decoded should be returned to the caller or not: + +```go +func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error { + prot := &MyProtocol{} + if len(data) < 10 { + // This error occurred before we did ANYTHING, so there's nothing in my + // protocol that the caller could possibly want. Just return the error. + return fmt.Errorf("Length %d less than 10", len(data)) + } + prot.ImportantField1 = data[:5] + prot.ImportantField2 = data[5:10] + // At this point, we've already got enough information in 'prot' to + // warrant returning it to the caller, so we'll add it now. + p.AddLayer(prot) + if len(data) < 15 { + // We encountered an error later in the packet, but the caller already + // has the important info we've gleaned so far. + return fmt.Errorf("Length %d less than 15", len(data)) + } + prot.ImportantField3 = data[10:15] + return nil // We've already added the layer, we can just return success. +} +``` + +In general, our code follows the approach of returning the first error it +encounters. In general, we don't trust any bytes after the first error we see. + +### What Is A Layer? + +The definition of a layer is up to the discretion of the coder. It should be +something important enough that it's actually useful to the caller (IE: every +TLV value should probably NOT be a layer). However, it can be more granular +than a single protocol... IPv6 and SCTP both implement many layers to handle the +various parts of the protocol. Use your best judgement, and prepare to defend +your decisions during code review. ;) + +### Performance + +We strive to make gopacket as fast as possible while still providing lots of +features. In general, this means: + +* Focus performance tuning on common protocols (IP4/6, TCP, etc), and optimize + others on an as-needed basis (tons of MPLS on your network? Time to optimize + MPLS!) +* Use fast operations. See the toplevel benchmark_test for benchmarks of some + of Go's underlying features and types. +* Test your performance changes! You should use the ./gc script's --benchmark + flag to submit any performance-related changes. Use pcap/gopacket_benchmark + to test your change against a PCAP file based on your traffic patterns. +* Don't be TOO hacky. Sometimes, removing an unused struct from a field causes + a huge performance hit, due to the way that Go currently handles its segmented + stack... don't be afraid to clean it up anyway. We'll trust the Go compiler + to get good enough over time to handle this. Also, this type of + compiler-specific optimization is very fragile; someone adding a field to an + entirely different struct elsewhere in the codebase could reverse any gains + you might achieve by aligning your allocations. +* Try to minimize memory allocations. If possible, use []byte to reference + pieces of the input, instead of using string, which requires copying the bytes + into a new memory allocation. +* Think hard about what should be evaluated lazily vs. not. In general, a + layer's struct should almost exactly mirror the layer's frame. Anything + that's more interesting should be a function. This may not always be + possible, but it's a good rule of thumb. +* Don't fear micro-optimizations. With the above in mind, we welcome + micro-optimizations that we think will have positive/neutral impacts on the + majority of workloads. A prime example of this is pre-allocating certain + structs within a larger one: + +```go +type MyProtocol struct { + // Most packets have 1-4 of VeryCommon, so we preallocate it here. + initialAllocation [4]uint32 + VeryCommon []uint32 +} + +func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error { + prot := &MyProtocol{} + prot.VeryCommon = proto.initialAllocation[:0] + for len(data) > 4 { + field := binary.BigEndian.Uint32(data[:4]) + data = data[4:] + // Since we're using the underlying initialAllocation, we won't need to + // allocate new memory for the following append unless we more than 16 + // bytes of data, which should be the uncommon case. + prot.VeryCommon = append(prot.VeryCommon, field) + } + p.AddLayer(prot) + if len(data) > 0 { + return fmt.Errorf("MyProtocol packet has %d bytes left after decoding", len(data)) + } + return nil +} +``` + +### Slices And Data + +If you're pulling a slice from the data you're decoding, don't copy it. Just +use the slice itself. + +```go +type MyProtocol struct { + A, B net.IP +} +func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error { + p.AddLayer(&MyProtocol{ + A: data[:4], + B: data[4:8], + }) + return nil +} +``` + +The caller has already agreed, by using this library, that they won't modify the +set of bytes they pass in to the decoder, or the library has already copied the +set of bytes to a read-only location. See DecodeOptions.NoCopy for more +information. + +### Enums/Types + +If a protocol has an integer field (uint8, uint16, etc) with a couple of known +values that mean something special, make it a type. This allows us to do really +nice things like adding a String() function to them, so we can more easily +display those to users. Check out layers/enums.go for one example, as well as +layers/icmp.go for layer-specific enums. + +When naming things, try for descriptiveness over suscinctness. For example, +choose DNSResponseRecord over DNSRR. diff --git a/vendor/github.com/google/gopacket/LICENSE b/vendor/github.com/google/gopacket/LICENSE new file mode 100644 index 00000000..2100d524 --- /dev/null +++ b/vendor/github.com/google/gopacket/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 Google, Inc. All rights reserved. +Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Andreas Krennmair, Google, nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/gopacket/README.md b/vendor/github.com/google/gopacket/README.md new file mode 100644 index 00000000..f71854c4 --- /dev/null +++ b/vendor/github.com/google/gopacket/README.md @@ -0,0 +1,10 @@ +# GoPacket + +This library provides packet decoding capabilities for Go. +See [godoc](https://godoc.org/github.com/google/gopacket) for more details. + +[![Build Status](https://travis-ci.org/google/gopacket.svg?branch=master)](https://travis-ci.org/google/gopacket) +[![GoDoc](https://godoc.org/github.com/google/gopacket?status.svg)](https://godoc.org/github.com/google/gopacket) + +Originally forked from the gopcap project written by Andreas +Krennmair (http://github.com/akrennmair/gopcap). diff --git a/vendor/github.com/google/gopacket/afpacket/afpacket.go b/vendor/github.com/google/gopacket/afpacket/afpacket.go new file mode 100644 index 00000000..13937c1d --- /dev/null +++ b/vendor/github.com/google/gopacket/afpacket/afpacket.go @@ -0,0 +1,476 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build linux + +// Package afpacket provides Go bindings for MMap'd AF_PACKET socket reading. +package afpacket + +// Couldn't have done this without: +// http://lxr.free-electrons.com/source/Documentation/networking/packet_mmap.txt +// http://codemonkeytips.blogspot.co.uk/2011/07/asynchronous-packet-socket-reading-with.html + +import ( + "errors" + "fmt" + "net" + "runtime" + "sync" + "sync/atomic" + "time" + "unsafe" + + "golang.org/x/net/bpf" + "golang.org/x/sys/unix" + + "github.com/google/gopacket" +) + +/* +#include // AF_PACKET, sockaddr_ll +#include // ETH_P_ALL +#include // socket() +#include // close() +#include // htons() +#include // mmap(), munmap() +#include // poll() +*/ +import "C" + +var pageSize = unix.Getpagesize() +var tpacketAlignment = uint(C.TPACKET_ALIGNMENT) + +// ErrPoll returned by poll +var ErrPoll = errors.New("packet poll failed") + +// ErrTimeout returned on poll timeout +var ErrTimeout = errors.New("packet poll timeout expired") + +func tpacketAlign(v int) int { + return int((uint(v) + tpacketAlignment - 1) & ((^tpacketAlignment) - 1)) +} + +// Stats is a set of counters detailing the work TPacket has done so far. +type Stats struct { + // Packets is the total number of packets returned to the caller. + Packets int64 + // Polls is the number of blocking syscalls made waiting for packets. + // This should always be <= Packets, since with TPacket one syscall + // can (and often does) return many results. + Polls int64 +} + +// SocketStats is a struct where socket stats are stored +type SocketStats C.struct_tpacket_stats + +// SocketStatsV3 is a struct where socket stats for TPacketV3 are stored +type SocketStatsV3 C.struct_tpacket_stats_v3 + +// TPacket implements packet receiving for Linux AF_PACKET versions 1, 2, and 3. +type TPacket struct { + // fd is the C file descriptor. + fd int + // ring points to the memory space of the ring buffer shared by tpacket and the kernel. + ring []byte + // rawring is the unsafe pointer that we use to poll for packets + rawring unsafe.Pointer + // opts contains read-only options for the TPacket object. + opts options + mu sync.Mutex // guards below + // offset is the offset into the ring of the current header. + offset int + // current is the current header. + current header + // pollset is used by TPacket for its poll() call. + pollset unix.PollFd + // shouldReleasePacket is set to true whenever we return packet data, to make sure we remember to release that data back to the kernel. + shouldReleasePacket bool + // headerNextNeeded is set to true when header need to move to the next packet. No need to move it case of poll error. + headerNextNeeded bool + // tpVersion is the version of TPacket actually in use, set by setRequestedTPacketVersion. + tpVersion OptTPacketVersion + // Hackity hack hack hack. We need to return a pointer to the header with + // getTPacketHeader, and we don't want to allocate a v3wrapper every time, + // so we leave it in the TPacket object and return a pointer to it. + v3 v3wrapper + + statsMu sync.Mutex // guards stats below + // stats is simple statistics on TPacket's run. + stats Stats + // socketStats contains stats from the socket + socketStats SocketStats + // same as socketStats, but with an extra field freeze_q_cnt + socketStatsV3 SocketStatsV3 +} + +var _ gopacket.ZeroCopyPacketDataSource = &TPacket{} + +// bindToInterface binds the TPacket socket to a particular named interface. +func (h *TPacket) bindToInterface(ifaceName string) error { + ifIndex := 0 + // An empty string here means to listen to all interfaces + if ifaceName != "" { + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + return fmt.Errorf("InterfaceByName: %v", err) + } + ifIndex = iface.Index + } + s := &unix.SockaddrLinklayer{ + Protocol: htons(uint16(unix.ETH_P_ALL)), + Ifindex: ifIndex, + } + return unix.Bind(h.fd, s) +} + +// setTPacketVersion asks the kernel to set TPacket to a particular version, and returns an error on failure. +func (h *TPacket) setTPacketVersion(version OptTPacketVersion) error { + if err := unix.SetsockoptInt(h.fd, unix.SOL_PACKET, unix.PACKET_VERSION, int(version)); err != nil { + return fmt.Errorf("setsockopt packet_version: %v", err) + } + return nil +} + +// setRequestedTPacketVersion tries to set TPacket to the requested version or versions. +func (h *TPacket) setRequestedTPacketVersion() error { + switch { + case (h.opts.version == TPacketVersionHighestAvailable || h.opts.version == TPacketVersion3) && h.setTPacketVersion(TPacketVersion3) == nil: + h.tpVersion = TPacketVersion3 + case (h.opts.version == TPacketVersionHighestAvailable || h.opts.version == TPacketVersion2) && h.setTPacketVersion(TPacketVersion2) == nil: + h.tpVersion = TPacketVersion2 + case (h.opts.version == TPacketVersionHighestAvailable || h.opts.version == TPacketVersion1) && h.setTPacketVersion(TPacketVersion1) == nil: + h.tpVersion = TPacketVersion1 + default: + return errors.New("no known tpacket versions work on this machine") + } + return nil +} + +// setUpRing sets up the shared-memory ring buffer between the user process and the kernel. +func (h *TPacket) setUpRing() (err error) { + totalSize := int(h.opts.framesPerBlock * h.opts.numBlocks * h.opts.frameSize) + switch h.tpVersion { + case TPacketVersion1, TPacketVersion2: + var tp C.struct_tpacket_req + tp.tp_block_size = C.uint(h.opts.blockSize) + tp.tp_block_nr = C.uint(h.opts.numBlocks) + tp.tp_frame_size = C.uint(h.opts.frameSize) + tp.tp_frame_nr = C.uint(h.opts.framesPerBlock * h.opts.numBlocks) + if err := setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, unsafe.Pointer(&tp), unsafe.Sizeof(tp)); err != nil { + return fmt.Errorf("setsockopt packet_rx_ring: %v", err) + } + case TPacketVersion3: + var tp C.struct_tpacket_req3 + tp.tp_block_size = C.uint(h.opts.blockSize) + tp.tp_block_nr = C.uint(h.opts.numBlocks) + tp.tp_frame_size = C.uint(h.opts.frameSize) + tp.tp_frame_nr = C.uint(h.opts.framesPerBlock * h.opts.numBlocks) + tp.tp_retire_blk_tov = C.uint(h.opts.blockTimeout / time.Millisecond) + if err := setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, unsafe.Pointer(&tp), unsafe.Sizeof(tp)); err != nil { + return fmt.Errorf("setsockopt packet_rx_ring v3: %v", err) + } + default: + return errors.New("invalid tpVersion") + } + h.ring, err = unix.Mmap(h.fd, 0, totalSize, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) + if err != nil { + return err + } + if h.ring == nil { + return errors.New("no ring") + } + h.rawring = unsafe.Pointer(&h.ring[0]) + return nil +} + +// Close cleans up the TPacket. It should not be used after the Close call. +func (h *TPacket) Close() { + if h.fd == -1 { + return // already closed. + } + if h.ring != nil { + unix.Munmap(h.ring) + } + h.ring = nil + unix.Close(h.fd) + h.fd = -1 + runtime.SetFinalizer(h, nil) +} + +// NewTPacket returns a new TPacket object for reading packets off the wire. +// Its behavior may be modified by passing in any/all of afpacket.Opt* to this +// function. +// If this function succeeds, the user should be sure to Close the returned +// TPacket when finished with it. +func NewTPacket(opts ...interface{}) (h *TPacket, err error) { + h = &TPacket{} + if h.opts, err = parseOptions(opts...); err != nil { + return nil, err + } + fd, err := unix.Socket(unix.AF_PACKET, int(h.opts.socktype), int(htons(unix.ETH_P_ALL))) + if err != nil { + return nil, err + } + h.fd = fd + if err = h.bindToInterface(h.opts.iface); err != nil { + goto errlbl + } + if err = h.setRequestedTPacketVersion(); err != nil { + goto errlbl + } + if err = h.setUpRing(); err != nil { + goto errlbl + } + // Clear stat counter from socket + if err = h.InitSocketStats(); err != nil { + goto errlbl + } + runtime.SetFinalizer(h, (*TPacket).Close) + return h, nil +errlbl: + h.Close() + return nil, err +} + +// SetBPF attaches a BPF filter to the underlying socket +func (h *TPacket) SetBPF(filter []bpf.RawInstruction) error { + var p unix.SockFprog + if len(filter) > int(^uint16(0)) { + return errors.New("filter too large") + } + p.Len = uint16(len(filter)) + p.Filter = (*unix.SockFilter)(unsafe.Pointer(&filter[0])) + + return setsockopt(h.fd, unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, unsafe.Pointer(&p), unix.SizeofSockFprog) +} + +func (h *TPacket) releaseCurrentPacket() error { + h.current.clearStatus() + h.offset++ + h.shouldReleasePacket = false + return nil +} + +// ZeroCopyReadPacketData reads the next packet off the wire, and returns its data. +// The slice returned by ZeroCopyReadPacketData points to bytes owned by the +// TPacket. Each call to ZeroCopyReadPacketData invalidates any data previously +// returned by ZeroCopyReadPacketData. Care must be taken not to keep pointers +// to old bytes when using ZeroCopyReadPacketData... if you need to keep data past +// the next time you call ZeroCopyReadPacketData, use ReadPacketData, which copies +// the bytes into a new buffer for you. +// tp, _ := NewTPacket(...) +// data1, _, _ := tp.ZeroCopyReadPacketData() +// // do everything you want with data1 here, copying bytes out of it if you'd like to keep them around. +// data2, _, _ := tp.ZeroCopyReadPacketData() // invalidates bytes in data1 +func (h *TPacket) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + h.mu.Lock() +retry: + if h.current == nil || !h.headerNextNeeded || !h.current.next() { + if h.shouldReleasePacket { + h.releaseCurrentPacket() + } + h.current = h.getTPacketHeader() + if err = h.pollForFirstPacket(h.current); err != nil { + h.headerNextNeeded = false + h.mu.Unlock() + return + } + // We received an empty block + if h.current.getLength() == 0 { + goto retry + } + } + data = h.current.getData() + ci.Timestamp = h.current.getTime() + ci.CaptureLength = len(data) + ci.Length = h.current.getLength() + ci.InterfaceIndex = h.current.getIfaceIndex() + atomic.AddInt64(&h.stats.Packets, 1) + h.headerNextNeeded = true + h.mu.Unlock() + + return +} + +// Stats returns statistics on the packets the TPacket has seen so far. +func (h *TPacket) Stats() (Stats, error) { + return Stats{ + Polls: atomic.LoadInt64(&h.stats.Polls), + Packets: atomic.LoadInt64(&h.stats.Packets), + }, nil +} + +// InitSocketStats clears socket counters and return empty stats. +func (h *TPacket) InitSocketStats() error { + if h.tpVersion == TPacketVersion3 { + socklen := unsafe.Sizeof(h.socketStatsV3) + slt := C.socklen_t(socklen) + var ssv3 SocketStatsV3 + + err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ssv3), uintptr(unsafe.Pointer(&slt))) + if err != nil { + return err + } + h.socketStatsV3 = SocketStatsV3{} + } else { + socklen := unsafe.Sizeof(h.socketStats) + slt := C.socklen_t(socklen) + var ss SocketStats + + err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ss), uintptr(unsafe.Pointer(&slt))) + if err != nil { + return err + } + h.socketStats = SocketStats{} + } + return nil +} + +// SocketStats saves stats from the socket to the TPacket instance. +func (h *TPacket) SocketStats() (SocketStats, SocketStatsV3, error) { + h.statsMu.Lock() + defer h.statsMu.Unlock() + // We need to save the counters since asking for the stats will clear them + if h.tpVersion == TPacketVersion3 { + socklen := unsafe.Sizeof(h.socketStatsV3) + slt := C.socklen_t(socklen) + var ssv3 SocketStatsV3 + + err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ssv3), uintptr(unsafe.Pointer(&slt))) + if err != nil { + return SocketStats{}, SocketStatsV3{}, err + } + + h.socketStatsV3.tp_packets += ssv3.tp_packets + h.socketStatsV3.tp_drops += ssv3.tp_drops + h.socketStatsV3.tp_freeze_q_cnt += ssv3.tp_freeze_q_cnt + return h.socketStats, h.socketStatsV3, nil + } + socklen := unsafe.Sizeof(h.socketStats) + slt := C.socklen_t(socklen) + var ss SocketStats + + err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ss), uintptr(unsafe.Pointer(&slt))) + if err != nil { + return SocketStats{}, SocketStatsV3{}, err + } + + h.socketStats.tp_packets += ss.tp_packets + h.socketStats.tp_drops += ss.tp_drops + return h.socketStats, h.socketStatsV3, nil +} + +// ReadPacketDataTo reads packet data into a user-supplied buffer. +// This function reads up to the length of the passed-in slice. +// The number of bytes read into data will be returned in ci.CaptureLength, +// which is the minimum of the size of the passed-in buffer and the size of +// the captured packet. +func (h *TPacket) ReadPacketDataTo(data []byte) (ci gopacket.CaptureInfo, err error) { + var d []byte + d, ci, err = h.ZeroCopyReadPacketData() + if err != nil { + return + } + ci.CaptureLength = copy(data, d) + return +} + +// ReadPacketData reads the next packet, copies it into a new buffer, and returns +// that buffer. Since the buffer is allocated by ReadPacketData, it is safe for long-term +// use. This implements gopacket.PacketDataSource. +func (h *TPacket) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + var d []byte + d, ci, err = h.ZeroCopyReadPacketData() + if err != nil { + return + } + data = make([]byte, len(d)) + copy(data, d) + return +} + +func (h *TPacket) getTPacketHeader() header { + switch h.tpVersion { + case TPacketVersion1: + if h.offset >= h.opts.framesPerBlock*h.opts.numBlocks { + h.offset = 0 + } + position := uintptr(h.rawring) + uintptr(h.opts.frameSize*h.offset) + return (*v1header)(unsafe.Pointer(position)) + case TPacketVersion2: + if h.offset >= h.opts.framesPerBlock*h.opts.numBlocks { + h.offset = 0 + } + position := uintptr(h.rawring) + uintptr(h.opts.frameSize*h.offset) + return (*v2header)(unsafe.Pointer(position)) + case TPacketVersion3: + // TPacket3 uses each block to return values, instead of each frame. Hence we need to rotate when we hit #blocks, not #frames. + if h.offset >= h.opts.numBlocks { + h.offset = 0 + } + position := uintptr(h.rawring) + uintptr(h.opts.frameSize*h.offset*h.opts.framesPerBlock) + h.v3 = initV3Wrapper(unsafe.Pointer(position)) + return &h.v3 + } + panic("handle tpacket version is invalid") +} + +func (h *TPacket) pollForFirstPacket(hdr header) error { + tm := int(h.opts.pollTimeout / time.Millisecond) + for hdr.getStatus()&C.TP_STATUS_USER == 0 { + h.pollset.Fd = int32(h.fd) + h.pollset.Events = unix.POLLIN + h.pollset.Revents = 0 + n, err := unix.Poll([]unix.PollFd{h.pollset}, tm) + if n == 0 { + return ErrTimeout + } + + atomic.AddInt64(&h.stats.Polls, 1) + if h.pollset.Revents&unix.POLLERR > 0 { + return ErrPoll + } + if err != nil { + return err + } + } + + h.shouldReleasePacket = true + return nil +} + +// FanoutType determines the type of fanout to use with a TPacket SetFanout call. +type FanoutType int + +// FanoutType values. +const ( + FanoutHash FanoutType = 0 + // It appears that defrag only works with FanoutHash, see: + // http://lxr.free-electrons.com/source/net/packet/af_packet.c#L1204 + FanoutHashWithDefrag FanoutType = 0x8000 + FanoutLoadBalance FanoutType = 1 + FanoutCPU FanoutType = 2 +) + +// SetFanout activates TPacket's fanout ability. +// Use of Fanout requires creating multiple TPacket objects and the same id/type to +// a SetFanout call on each. Note that this can be done cross-process, so if two +// different processes both call SetFanout with the same type/id, they'll share +// packets between them. The same should work for multiple TPacket objects within +// the same process. +func (h *TPacket) SetFanout(t FanoutType, id uint16) error { + h.mu.Lock() + defer h.mu.Unlock() + arg := C.int(t) << 16 + arg |= C.int(id) + return setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_FANOUT, unsafe.Pointer(&arg), unsafe.Sizeof(arg)) +} + +// WritePacketData transmits a raw packet. +func (h *TPacket) WritePacketData(pkt []byte) error { + _, err := unix.Write(h.fd, pkt) + return err +} diff --git a/vendor/github.com/google/gopacket/afpacket/afpacket_test.go b/vendor/github.com/google/gopacket/afpacket/afpacket_test.go new file mode 100644 index 00000000..57f6480d --- /dev/null +++ b/vendor/github.com/google/gopacket/afpacket/afpacket_test.go @@ -0,0 +1,40 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build linux + +package afpacket + +import ( + "reflect" + "testing" +) + +func TestParseOptions(t *testing.T) { + wanted1 := defaultOpts + wanted1.frameSize = 1 << 10 + wanted1.framesPerBlock = wanted1.blockSize / wanted1.frameSize + for i, test := range []struct { + opts []interface{} + want options + err bool + }{ + {opts: []interface{}{OptBlockSize(2)}, err: true}, + {opts: []interface{}{OptFrameSize(333)}, err: true}, + {opts: []interface{}{OptTPacketVersion(-3)}, err: true}, + {opts: []interface{}{OptTPacketVersion(5)}, err: true}, + {opts: []interface{}{OptFrameSize(1 << 10)}, want: wanted1}, + } { + got, err := parseOptions(test.opts...) + t.Logf("got: %#v\nerr: %v", got, err) + if test.err && err == nil || !test.err && err != nil { + t.Errorf("%d error mismatch, want error? %v. error: %v", i, test.err, err) + } + if !test.err && !reflect.DeepEqual(test.want, got) { + t.Errorf("%d opts mismatch, want\n%#v", i, test.want) + } + } +} diff --git a/vendor/github.com/google/gopacket/afpacket/header.go b/vendor/github.com/google/gopacket/afpacket/header.go new file mode 100644 index 00000000..0b9918ec --- /dev/null +++ b/vendor/github.com/google/gopacket/afpacket/header.go @@ -0,0 +1,158 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build linux + +package afpacket + +import ( + "reflect" + "time" + "unsafe" +) + +// #include +import "C" + +// Our model of handling all TPacket versions is a little hacky, to say the +// least. We use the header interface to handle interactions with the +// tpacket1/tpacket2 packet header AND the tpacket3 block header. The big +// difference is that tpacket3's block header implements the next() call to get +// the next packet within the block, while v1/v2 just always return false. + +type header interface { + // getStatus returns the TPacket status of the current header. + getStatus() int + // clearStatus clears the status of the current header, releasing its + // underlying data back to the kernel for future use with new packets. + // Using the header after calling clearStatus is an error. clearStatus + // should only be called after next() returns false. + clearStatus() + // getTime returns the timestamp for the current packet pointed to by + // the header. + getTime() time.Time + // getData returns the packet data pointed to by the current header. + getData() []byte + // getLength returns the total length of the packet. + getLength() int + // getIfaceIndex returns the index of the network interface + // where the packet was seen. The index can later be translated to a name. + getIfaceIndex() int + // next moves this header to point to the next packet it contains, + // returning true on success (in which case getTime and getData will + // return values for the new packet) or false if there are no more + // packets (in which case clearStatus should be called). + next() bool +} + +func tpAlign(x int) int { + return int((uint(x) + tpacketAlignment - 1) &^ (tpacketAlignment - 1)) +} + +type v1header C.struct_tpacket_hdr +type v2header C.struct_tpacket2_hdr + +func makeSlice(start uintptr, length int) (data []byte) { + slice := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + slice.Data = start + slice.Len = length + slice.Cap = length + return +} + +func (h *v1header) getStatus() int { + return int(h.tp_status) +} +func (h *v1header) clearStatus() { + h.tp_status = 0 +} +func (h *v1header) getTime() time.Time { + return time.Unix(int64(h.tp_sec), int64(h.tp_usec)*1000) +} +func (h *v1header) getData() []byte { + return makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.tp_mac), int(h.tp_snaplen)) +} +func (h *v1header) getLength() int { + return int(h.tp_len) +} +func (h *v1header) getIfaceIndex() int { + ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket_hdr))))) + return int(ll.sll_ifindex) +} +func (h *v1header) next() bool { + return false +} + +func (h *v2header) getStatus() int { + return int(h.tp_status) +} +func (h *v2header) clearStatus() { + h.tp_status = 0 +} +func (h *v2header) getTime() time.Time { + return time.Unix(int64(h.tp_sec), int64(h.tp_nsec)) +} +func (h *v2header) getData() []byte { + return makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.tp_mac), int(h.tp_snaplen)) +} +func (h *v2header) getLength() int { + return int(h.tp_len) +} +func (h *v2header) getIfaceIndex() int { + ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket2_hdr))))) + return int(ll.sll_ifindex) +} +func (h *v2header) next() bool { + return false +} + +type v3wrapper struct { + block *C.struct_tpacket_block_desc + blockhdr *C.struct_tpacket_hdr_v1 + packet *C.struct_tpacket3_hdr + used C.__u32 +} + +func initV3Wrapper(block unsafe.Pointer) (w v3wrapper) { + w.block = (*C.struct_tpacket_block_desc)(block) + w.blockhdr = (*C.struct_tpacket_hdr_v1)(unsafe.Pointer(&w.block.hdr[0])) + w.packet = (*C.struct_tpacket3_hdr)(unsafe.Pointer(uintptr(block) + uintptr(w.blockhdr.offset_to_first_pkt))) + return +} +func (w *v3wrapper) getStatus() int { + return int(w.blockhdr.block_status) +} +func (w *v3wrapper) clearStatus() { + w.blockhdr.block_status = 0 +} +func (w *v3wrapper) getTime() time.Time { + return time.Unix(int64(w.packet.tp_sec), int64(w.packet.tp_nsec)) +} +func (w *v3wrapper) getData() []byte { + return makeSlice(uintptr(unsafe.Pointer(w.packet))+uintptr(w.packet.tp_mac), int(w.packet.tp_snaplen)) +} +func (w *v3wrapper) getLength() int { + return int(w.packet.tp_len) +} +func (w *v3wrapper) getIfaceIndex() int { + ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(w.packet)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket3_hdr))))) + return int(ll.sll_ifindex) +} +func (w *v3wrapper) next() bool { + w.used++ + if w.used >= w.blockhdr.num_pkts { + return false + } + + next := uintptr(unsafe.Pointer(w.packet)) + if w.packet.tp_next_offset != 0 { + next += uintptr(w.packet.tp_next_offset) + } else { + next += uintptr(tpacketAlign(int(w.packet.tp_snaplen) + int(w.packet.tp_mac))) + } + w.packet = (*C.struct_tpacket3_hdr)(unsafe.Pointer(next)) + return true +} diff --git a/vendor/github.com/google/gopacket/afpacket/options.go b/vendor/github.com/google/gopacket/afpacket/options.go new file mode 100644 index 00000000..c5ab7719 --- /dev/null +++ b/vendor/github.com/google/gopacket/afpacket/options.go @@ -0,0 +1,171 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build linux + +package afpacket + +import ( + "errors" + "fmt" + "time" +) + +// #include +// #include +import "C" + +// OptTPacketVersion is the version of TPacket to use. +// It can be passed into NewTPacket. +type OptTPacketVersion int + +// String returns a string representation of the version, generally of the form V#. +func (t OptTPacketVersion) String() string { + switch t { + case TPacketVersion1: + return "V1" + case TPacketVersion2: + return "V2" + case TPacketVersion3: + return "V3" + case TPacketVersionHighestAvailable: + return "HighestAvailable" + } + return "InvalidVersion" +} + +// OptSocketType is the socket type used to open the TPacket socket. +type OptSocketType int + +func (t OptSocketType) String() string { + switch t { + case SocketRaw: + return "SOCK_RAW" + case SocketDgram: + return "SOCK_DGRAM" + } + return "UnknownSocketType" +} + +// TPacket version numbers for use with NewHandle. +const ( + // TPacketVersionHighestAvailable tells NewHandle to use the highest available version of tpacket the kernel has available. + // This is the default, should a version number not be given in NewHandle's options. + TPacketVersionHighestAvailable = OptTPacketVersion(-1) + TPacketVersion1 = OptTPacketVersion(C.TPACKET_V1) + TPacketVersion2 = OptTPacketVersion(C.TPACKET_V2) + TPacketVersion3 = OptTPacketVersion(C.TPACKET_V3) + tpacketVersionMax = TPacketVersion3 + tpacketVersionMin = -1 + // SocketRaw is the default socket type. It returns packet data + // including the link layer (ethernet headers, etc). + SocketRaw = OptSocketType(C.SOCK_RAW) + // SocketDgram strips off the link layer when reading packets, and adds + // the link layer back automatically on packet writes (coming soon...) + SocketDgram = OptSocketType(C.SOCK_DGRAM) +) + +// OptInterface is the specific interface to bind to. +// It can be passed into NewTPacket. +type OptInterface string + +// OptFrameSize is TPacket's tp_frame_size +// It can be passed into NewTPacket. +type OptFrameSize int + +// OptBlockSize is TPacket's tp_block_size +// It can be passed into NewTPacket. +type OptBlockSize int + +// OptNumBlocks is TPacket's tp_block_nr +// It can be passed into NewTPacket. +type OptNumBlocks int + +// OptBlockTimeout is TPacket v3's tp_retire_blk_tov. Note that it has only millisecond granularity, so must be >= 1 ms. +// It can be passed into NewTPacket. +type OptBlockTimeout time.Duration + +// OptPollTimeout is the number of milliseconds that poll() should block waiting for a file +// descriptor to become ready. Specifying a negative value in timeâ€out means an infinite timeout. +type OptPollTimeout time.Duration + +// Default constants used by options. +const ( + DefaultFrameSize = 4096 // Default value for OptFrameSize. + DefaultBlockSize = DefaultFrameSize * 128 // Default value for OptBlockSize. + DefaultNumBlocks = 128 // Default value for OptNumBlocks. + DefaultBlockTimeout = 64 * time.Millisecond // Default value for OptBlockTimeout. + DefaultPollTimeout = -1 * time.Millisecond // Default value for OptPollTimeout. This blocks forever. +) + +type options struct { + frameSize int + framesPerBlock int + blockSize int + numBlocks int + blockTimeout time.Duration + pollTimeout time.Duration + version OptTPacketVersion + socktype OptSocketType + iface string +} + +var defaultOpts = options{ + frameSize: DefaultFrameSize, + blockSize: DefaultBlockSize, + numBlocks: DefaultNumBlocks, + blockTimeout: DefaultBlockTimeout, + pollTimeout: DefaultPollTimeout, + version: TPacketVersionHighestAvailable, + socktype: SocketRaw, +} + +func parseOptions(opts ...interface{}) (ret options, err error) { + ret = defaultOpts + for _, opt := range opts { + switch v := opt.(type) { + case OptFrameSize: + ret.frameSize = int(v) + case OptBlockSize: + ret.blockSize = int(v) + case OptNumBlocks: + ret.numBlocks = int(v) + case OptBlockTimeout: + ret.blockTimeout = time.Duration(v) + case OptPollTimeout: + ret.pollTimeout = time.Duration(v) + case OptTPacketVersion: + ret.version = v + case OptInterface: + ret.iface = string(v) + case OptSocketType: + ret.socktype = v + default: + err = errors.New("unknown type in options") + return + } + } + if err = ret.check(); err != nil { + return + } + ret.framesPerBlock = ret.blockSize / ret.frameSize + return +} +func (o options) check() error { + switch { + case o.blockSize%pageSize != 0: + return fmt.Errorf("block size %d must be divisible by page size %d", o.blockSize, pageSize) + case o.blockSize%o.frameSize != 0: + return fmt.Errorf("block size %d must be divisible by frame size %d", o.blockSize, o.frameSize) + case o.numBlocks < 1: + return fmt.Errorf("num blocks %d must be >= 1", o.numBlocks) + case o.blockTimeout < time.Millisecond: + return fmt.Errorf("block timeout %v must be > %v", o.blockTimeout, time.Millisecond) + case o.version < tpacketVersionMin || o.version > tpacketVersionMax: + return fmt.Errorf("tpacket version %v is invalid", o.version) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/afpacket/sockopt_linux.go b/vendor/github.com/google/gopacket/afpacket/sockopt_linux.go new file mode 100644 index 00000000..c53e1cce --- /dev/null +++ b/vendor/github.com/google/gopacket/afpacket/sockopt_linux.go @@ -0,0 +1,58 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build linux + +package afpacket + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +// setsockopt provides access to the setsockopt syscall. +func setsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error { + _, _, errno := unix.Syscall6( + unix.SYS_SETSOCKOPT, + uintptr(fd), + uintptr(level), + uintptr(name), + uintptr(val), + vallen, + 0, + ) + if errno != 0 { + return error(errno) + } + + return nil +} + +// getsockopt provides access to the getsockopt syscall. +func getsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error { + _, _, errno := unix.Syscall6( + unix.SYS_GETSOCKOPT, + uintptr(fd), + uintptr(level), + uintptr(name), + uintptr(val), + vallen, + 0, + ) + if errno != 0 { + return error(errno) + } + + return nil +} + +// htons converts a short (uint16) from host-to-network byte order. +// Thanks to mikioh for this neat trick: +// https://github.com/mikioh/-stdyng/blob/master/afpacket.go +func htons(i uint16) uint16 { + return (i<<8)&0xff00 | i>>8 +} diff --git a/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.go b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.go new file mode 100644 index 00000000..8c3eb424 --- /dev/null +++ b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.go @@ -0,0 +1,57 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build linux,386 + +package afpacket + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + sysSETSOCKOPT = 0xe + sysGETSOCKOPT = 0xf +) + +func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, unix.Errno) + +// setsockopt provides access to the setsockopt syscall. +func setsockopt(fd, level, name int, v unsafe.Pointer, l uintptr) error { + _, errno := socketcall( + sysSETSOCKOPT, + uintptr(fd), + uintptr(level), + uintptr(name), + uintptr(v), + l, + 0, + ) + if errno != 0 { + return error(errno) + } + + return nil +} + +func getsockopt(fd, level, name int, v unsafe.Pointer, l uintptr) error { + _, errno := socketcall( + sysGETSOCKOPT, + uintptr(fd), + uintptr(level), + uintptr(name), + uintptr(v), + l, + 0, + ) + if errno != 0 { + return error(errno) + } + + return nil +} diff --git a/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.s b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.s new file mode 100644 index 00000000..7d0336a1 --- /dev/null +++ b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.s @@ -0,0 +1,8 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +TEXT ·socketcall(SB),4,$0-36 + JMP syscall·socketcall(SB) \ No newline at end of file diff --git a/vendor/github.com/google/gopacket/base.go b/vendor/github.com/google/gopacket/base.go new file mode 100644 index 00000000..797b55fb --- /dev/null +++ b/vendor/github.com/google/gopacket/base.go @@ -0,0 +1,178 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" +) + +// Layer represents a single decoded packet layer (using either the +// OSI or TCP/IP definition of a layer). When decoding, a packet's data is +// broken up into a number of layers. The caller may call LayerType() to +// figure out which type of layer they've received from the packet. Optionally, +// they may then use a type assertion to get the actual layer type for deep +// inspection of the data. +type Layer interface { + // LayerType is the gopacket type for this layer. + LayerType() LayerType + // LayerContents returns the set of bytes that make up this layer. + LayerContents() []byte + // LayerPayload returns the set of bytes contained within this layer, not + // including the layer itself. + LayerPayload() []byte +} + +// Payload is a Layer containing the payload of a packet. The definition of +// what constitutes the payload of a packet depends on previous layers; for +// TCP and UDP, we stop decoding above layer 4 and return the remaining +// bytes as a Payload. Payload is an ApplicationLayer. +type Payload []byte + +// LayerType returns LayerTypePayload +func (p Payload) LayerType() LayerType { return LayerTypePayload } + +// LayerContents returns the bytes making up this layer. +func (p Payload) LayerContents() []byte { return []byte(p) } + +// LayerPayload returns the payload within this layer. +func (p Payload) LayerPayload() []byte { return nil } + +// Payload returns this layer as bytes. +func (p Payload) Payload() []byte { return []byte(p) } + +// String implements fmt.Stringer. +func (p Payload) String() string { return fmt.Sprintf("%d byte(s)", len(p)) } + +// GoString implements fmt.GoStringer. +func (p Payload) GoString() string { return LongBytesGoString([]byte(p)) } + +// CanDecode implements DecodingLayer. +func (p Payload) CanDecode() LayerClass { return LayerTypePayload } + +// NextLayerType implements DecodingLayer. +func (p Payload) NextLayerType() LayerType { return LayerTypeZero } + +// DecodeFromBytes implements DecodingLayer. +func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error { + *p = Payload(data) + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error { + bytes, err := b.PrependBytes(len(p)) + if err != nil { + return err + } + copy(bytes, p) + return nil +} + +// decodePayload decodes data by returning it all in a Payload layer. +func decodePayload(data []byte, p PacketBuilder) error { + payload := &Payload{} + if err := payload.DecodeFromBytes(data, p); err != nil { + return nil + } + p.AddLayer(payload) + p.SetApplicationLayer(payload) + return nil +} + +// Fragment is a Layer containing a fragment of a larger frame, used by layers +// like IPv4 and IPv6 that allow for fragmentation of their payloads. +type Fragment []byte + +// LayerType returns LayerTypeFragment +func (p *Fragment) LayerType() LayerType { return LayerTypeFragment } + +// LayerContents implements Layer. +func (p *Fragment) LayerContents() []byte { return []byte(*p) } + +// LayerPayload implements Layer. +func (p *Fragment) LayerPayload() []byte { return nil } + +// Payload returns this layer as a byte slice. +func (p *Fragment) Payload() []byte { return []byte(*p) } + +// String implements fmt.Stringer. +func (p *Fragment) String() string { return fmt.Sprintf("%d byte(s)", len(*p)) } + +// CanDecode implements DecodingLayer. +func (p *Fragment) CanDecode() LayerClass { return LayerTypeFragment } + +// NextLayerType implements DecodingLayer. +func (p *Fragment) NextLayerType() LayerType { return LayerTypeZero } + +// DecodeFromBytes implements DecodingLayer. +func (p *Fragment) DecodeFromBytes(data []byte, df DecodeFeedback) error { + *p = Fragment(data) + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error { + bytes, err := b.PrependBytes(len(*p)) + if err != nil { + return err + } + copy(bytes, *p) + return nil +} + +// decodeFragment decodes data by returning it all in a Fragment layer. +func decodeFragment(data []byte, p PacketBuilder) error { + payload := &Fragment{} + if err := payload.DecodeFromBytes(data, p); err != nil { + return nil + } + p.AddLayer(payload) + p.SetApplicationLayer(payload) + return nil +} + +// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their +// corresponding OSI layers, as best as possible. + +// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2) +type LinkLayer interface { + Layer + LinkFlow() Flow +} + +// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI +// layer 3) +type NetworkLayer interface { + Layer + NetworkFlow() Flow +} + +// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI +// layer 4) +type TransportLayer interface { + Layer + TransportFlow() Flow +} + +// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI +// layer 7), also known as the packet payload. +type ApplicationLayer interface { + Layer + Payload() []byte +} + +// ErrorLayer is a packet layer created when decoding of the packet has failed. +// Its payload is all the bytes that we were unable to decode, and the returned +// error details why the decoding failed. +type ErrorLayer interface { + Layer + Error() error +} diff --git a/vendor/github.com/google/gopacket/benchmark_test.go b/vendor/github.com/google/gopacket/benchmark_test.go new file mode 100644 index 00000000..74a1d28d --- /dev/null +++ b/vendor/github.com/google/gopacket/benchmark_test.go @@ -0,0 +1,194 @@ +// Copyright 2012, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "runtime" + "testing" +) + +// A few benchmarks for figuring out exactly how fast some underlying Go +// things are. + +type testError struct{} + +func (t *testError) Error() string { return "abc" } + +func BenchmarkTypeAssertion(b *testing.B) { + var e error = &testError{} + for i := 0; i < b.N; i++ { + _, _ = e.(*testError) + } +} + +func BenchmarkMapLookup(b *testing.B) { + m := map[LayerType]bool{ + LayerTypePayload: true, + } + for i := 0; i < b.N; i++ { + _ = m[LayerTypePayload] + } +} + +func BenchmarkNilMapLookup(b *testing.B) { + var m map[LayerType]bool + for i := 0; i < b.N; i++ { + _ = m[LayerTypePayload] + } +} + +func BenchmarkNilMapLookupWithNilCheck(b *testing.B) { + var m map[LayerType]bool + for i := 0; i < b.N; i++ { + if m != nil { + _ = m[LayerTypePayload] + } + } +} + +func BenchmarkArrayLookup(b *testing.B) { + m := make([]bool, 100) + for i := 0; i < b.N; i++ { + _ = m[LayerTypePayload] + } +} + +var testError1 = &testError{} +var testError2 error = testError1 + +func BenchmarkTypeToInterface1(b *testing.B) { + var e error + for i := 0; i < b.N; i++ { + e = testError1 + } + // Have to do someting with 'e' or the compiler complains about an unused + // variable. + testError2 = e +} +func BenchmarkTypeToInterface2(b *testing.B) { + var e error + for i := 0; i < b.N; i++ { + e = testError2 + } + // Have to do someting with 'e' or the compiler complains about an unused + // variable. + testError2 = e +} + +var decodeOpts DecodeOptions + +func decodeOptsByValue(_ DecodeOptions) {} +func decodeOptsByPointer(_ *DecodeOptions) {} +func BenchmarkPassDecodeOptionsByValue(b *testing.B) { + for i := 0; i < b.N; i++ { + decodeOptsByValue(decodeOpts) + } +} +func BenchmarkPassDecodeOptionsByPointer(b *testing.B) { + for i := 0; i < b.N; i++ { + decodeOptsByPointer(&decodeOpts) + } +} + +func BenchmarkLockOSThread(b *testing.B) { + for i := 0; i < b.N; i++ { + runtime.LockOSThread() + } +} +func BenchmarkUnlockOSThread(b *testing.B) { + for i := 0; i < b.N; i++ { + runtime.UnlockOSThread() + } +} +func lockUnlock() { + runtime.LockOSThread() + runtime.UnlockOSThread() +} +func lockDeferUnlock() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() +} +func BenchmarkLockUnlockOSThread(b *testing.B) { + for i := 0; i < b.N; i++ { + lockUnlock() + } +} +func BenchmarkLockDeferUnlockOSThread(b *testing.B) { + for i := 0; i < b.N; i++ { + lockDeferUnlock() + } +} + +func BenchmarkUnbufferedChannel(b *testing.B) { + ca := make(chan bool) + cb := make(chan bool) + defer close(ca) + go func() { + defer close(cb) + for _ = range ca { + cb <- true + } + }() + for i := 0; i < b.N; i++ { + ca <- true + <-cb + } +} +func BenchmarkSmallBufferedChannel(b *testing.B) { + ca := make(chan bool, 1) + cb := make(chan bool, 1) + defer close(ca) + go func() { + defer close(cb) + for _ = range ca { + cb <- true + } + }() + for i := 0; i < b.N; i++ { + ca <- true + <-cb + } +} +func BenchmarkLargeBufferedChannel(b *testing.B) { + ca := make(chan bool, 1000) + cb := make(chan bool, 1000) + defer close(ca) + go func() { + defer close(cb) + for _ = range ca { + cb <- true + } + }() + for i := 0; i < b.N; i++ { + ca <- true + <-cb + } +} +func BenchmarkEndpointFastHashShort(b *testing.B) { + e := Endpoint{typ: 1, len: 2} + for i := 0; i < b.N; i++ { + e.FastHash() + } +} +func BenchmarkEndpointFastHashLong(b *testing.B) { + e := Endpoint{typ: 1, len: 16} + for i := 0; i < b.N; i++ { + e.FastHash() + } +} +func BenchmarkFlowFastHashShort(b *testing.B) { + e := Flow{typ: 1, slen: 2, dlen: 2} + for i := 0; i < b.N; i++ { + e.FastHash() + } +} +func BenchmarkFlowFastHashLong(b *testing.B) { + e := Flow{typ: 1, slen: 16, dlen: 16} + for i := 0; i < b.N; i++ { + e.FastHash() + } +} diff --git a/vendor/github.com/google/gopacket/bsdbpf/bsd_bpf_sniffer.go b/vendor/github.com/google/gopacket/bsdbpf/bsd_bpf_sniffer.go new file mode 100644 index 00000000..3e1da0b9 --- /dev/null +++ b/vendor/github.com/google/gopacket/bsdbpf/bsd_bpf_sniffer.go @@ -0,0 +1,215 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build darwin dragonfly freebsd netbsd openbsd + +package bsdbpf + +import ( + "errors" + "fmt" + "syscall" + "time" + "unsafe" + + "github.com/google/gopacket" + "golang.org/x/sys/unix" +) + +const wordSize = int(unsafe.Sizeof(uintptr(0))) + +func bpfWordAlign(x int) int { + return (((x) + (wordSize - 1)) &^ (wordSize - 1)) +} + +// Options is used to configure various properties of the BPF sniffer. +// Default values are used when a nil Options pointer is passed to NewBPFSniffer. +type Options struct { + // BPFDeviceName is name of the bpf device to use for sniffing + // the network device. The default value of BPFDeviceName is empty string + // which causes the first available BPF device file /dev/bpfX to be used. + BPFDeviceName string + // ReadBufLen specifies the size of the buffer used to read packets + // off the wire such that multiple packets are buffered with each read syscall. + // Note that an individual packet larger than the buffer size is necessarily truncated. + // A larger buffer should increase performance because fewer read syscalls would be made. + // If zero is used, the system's default buffer length will be used which depending on the + // system may default to 4096 bytes which is not big enough to accomodate some link layers + // such as WLAN (802.11). + // ReadBufLen defaults to 32767... however typical BSD manual pages for BPF indicate that + // if the requested buffer size cannot be accommodated, the closest allowable size will be + // set and returned... hence our GetReadBufLen method. + ReadBufLen int + // Timeout is the length of time to wait before timing out on a read request. + // Timeout defaults to nil which means no timeout is used. + Timeout *syscall.Timeval + // Promisc is set to true for promiscuous mode ethernet sniffing. + // Promisc defaults to true. + Promisc bool + // Immediate is set to true to make our read requests return as soon as a packet becomes available. + // Otherwise, a read will block until either the kernel buffer becomes full or a timeout occurs. + // The default is true. + Immediate bool + // PreserveLinkAddr is set to false if the link level source address should be filled in automatically + // by the interface output routine. Set to true if the link level source address will be written, + // as provided, to the wire. + // The default is true. + PreserveLinkAddr bool +} + +var defaultOptions = Options{ + BPFDeviceName: "", + ReadBufLen: 32767, + Timeout: nil, + Promisc: true, + Immediate: true, + PreserveLinkAddr: true, +} + +// BPFSniffer is a struct used to track state of a BSD BPF ethernet sniffer +// such that gopacket's PacketDataSource interface is implemented. +type BPFSniffer struct { + options *Options + sniffDeviceName string + fd int + readBuffer []byte + lastReadLen int + readBytesConsumed int +} + +// NewBPFSniffer is used to create BSD-only BPF ethernet sniffer +// iface is the network interface device name that you wish to sniff +// options can set to nil in order to utilize default values for everything. +// Each field of Options also have a default setting if left unspecified by +// the user's custome Options struct. +func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) { + var err error + enable := 1 + sniffer := BPFSniffer{ + sniffDeviceName: iface, + } + if options == nil { + sniffer.options = &defaultOptions + } else { + sniffer.options = options + } + + if sniffer.options.BPFDeviceName == "" { + sniffer.pickBpfDevice() + } + + // setup our read buffer + if sniffer.options.ReadBufLen == 0 { + sniffer.options.ReadBufLen, err = syscall.BpfBuflen(sniffer.fd) + if err != nil { + return nil, err + } + } else { + sniffer.options.ReadBufLen, err = syscall.SetBpfBuflen(sniffer.fd, sniffer.options.ReadBufLen) + if err != nil { + return nil, err + } + } + sniffer.readBuffer = make([]byte, sniffer.options.ReadBufLen) + + err = syscall.SetBpfInterface(sniffer.fd, sniffer.sniffDeviceName) + if err != nil { + return nil, err + } + + if sniffer.options.Immediate { + // turn immediate mode on. This makes the snffer non-blocking. + err = syscall.SetBpfImmediate(sniffer.fd, enable) + if err != nil { + return nil, err + } + } + + // the above call to syscall.SetBpfImmediate needs to be made + // before setting a timer otherwise the reads will block for the + // entire timer duration even if there are packets to return. + if sniffer.options.Timeout != nil { + err = syscall.SetBpfTimeout(sniffer.fd, sniffer.options.Timeout) + if err != nil { + return nil, err + } + } + + if sniffer.options.PreserveLinkAddr { + // preserves the link level source address... + // higher level protocol analyzers will not need this + err = syscall.SetBpfHeadercmpl(sniffer.fd, enable) + if err != nil { + return nil, err + } + } + + if sniffer.options.Promisc { + // forces the interface into promiscuous mode + err = syscall.SetBpfPromisc(sniffer.fd, enable) + if err != nil { + return nil, err + } + } + + return &sniffer, nil +} + +// Close is used to close the file-descriptor of the BPF device file. +func (b *BPFSniffer) Close() error { + return syscall.Close(b.fd) +} + +func (b *BPFSniffer) pickBpfDevice() { + var err error + b.options.BPFDeviceName = "" + for i := 0; i < 99; i++ { + b.options.BPFDeviceName = fmt.Sprintf("/dev/bpf%d", i) + b.fd, err = syscall.Open(b.options.BPFDeviceName, syscall.O_RDWR, 0) + if err == nil { + return + } + } + panic("failed to acquire a BPF device for read-write access") +} + +func (b *BPFSniffer) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) { + var err error + if b.readBytesConsumed >= b.lastReadLen { + b.readBytesConsumed = 0 + b.readBuffer = make([]byte, b.options.ReadBufLen) + b.lastReadLen, err = syscall.Read(b.fd, b.readBuffer) + if err != nil { + b.lastReadLen = 0 + return nil, gopacket.CaptureInfo{}, err + } + } + hdr := (*unix.BpfHdr)(unsafe.Pointer(&b.readBuffer[b.readBytesConsumed])) + frameStart := b.readBytesConsumed + int(hdr.Hdrlen) + b.readBytesConsumed += bpfWordAlign(int(hdr.Hdrlen) + int(hdr.Caplen)) + + if frameStart+int(hdr.Caplen) > len(b.readBuffer) { + captureInfo := gopacket.CaptureInfo{ + Timestamp: time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000), + CaptureLength: 0, + Length: 0, + } + return nil, captureInfo, errors.New("BPF captured frame received with corrupted BpfHdr struct.") + } + + rawFrame := b.readBuffer[frameStart : frameStart+int(hdr.Caplen)] + captureInfo := gopacket.CaptureInfo{ + Timestamp: time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000), + CaptureLength: len(rawFrame), + Length: len(rawFrame), + } + return rawFrame, captureInfo, nil +} + +// GetReadBufLen returns the BPF read buffer length +func (b *BPFSniffer) GetReadBufLen() int { + return b.options.ReadBufLen +} diff --git a/vendor/github.com/google/gopacket/bytediff/bytediff.go b/vendor/github.com/google/gopacket/bytediff/bytediff.go new file mode 100644 index 00000000..63addd94 --- /dev/null +++ b/vendor/github.com/google/gopacket/bytediff/bytediff.go @@ -0,0 +1,217 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package bytediff provides a simple diff utility for looking at differences in byte +// slices. It's slow, clunky, and not particularly good by any measure, but +// it does provide very useful visualizations for diffs between small byte +// slices. +// +// Our diff algorithm uses a dynamic programming implementation of longest common +// substring to find matching parts of slices, then recursively calls itself on +// the prefix/suffix of that matching part for each packet. This is a Bad Idea +// (tm) for normal (especially large) input, but for packets where large portions +// repeat frequently and we expect minor changes between results, it's actually +// quite useful. +package bytediff + +import ( + "bytes" + "fmt" +) + +// OutputFormat tells a Differences.String call how to format the set of +// differences into a human-readable string. Its internals are currently +// unexported because we may want to change them drastically in the future. For +// the moment, please just use one of the provided OutputFormats that comes with +// this library. +type OutputFormat struct { + start, finish, add, remove, change, reset string +} + +var ( + // BashOutput uses bash escape sequences to color output. + BashOutput = &OutputFormat{ + reset: "\033[0m", + remove: "\033[32m", + add: "\033[31m", + change: "\033[33m", + } + // HTMLOutput uses a
 to wrap output, and s to color it.
+	// HTMLOutput is pretty experimental, so use at your own risk ;)
+	HTMLOutput = &OutputFormat{
+		start:  "
",
+		finish: "
", + reset: "
", + remove: "", + add: "", + change: "", + } +) + +// longestCommonSubstring uses a O(MN) dynamic programming approach to find the +// longest common substring in a set of slices. It returns the index in each +// slice at which the substring begins, plus the length of the commonality. +func longestCommonSubstring(strA, strB []byte) (indexA, indexB, length int) { + lenA, lenB := len(strA), len(strB) + if lenA == 0 || lenB == 0 { + return 0, 0, 0 + } + arr := make([][]int, lenA) + for i := 0; i < lenA; i++ { + arr[i] = make([]int, lenB) + } + var maxLength int + var maxA, maxB int + for a := 0; a < lenA; a++ { + for b := 0; b < lenB; b++ { + if strA[a] == strB[b] { + length := 1 + if a > 0 && b > 0 { + length = arr[a-1][b-1] + 1 + } + arr[a][b] = length + if length > maxLength { + maxLength = length + maxA = a + maxB = b + } + } + } + } + a, b := maxA, maxB + for a >= 0 && b >= 0 && strA[a] == strB[b] { + indexA = a + indexB = b + a-- + b-- + length++ + } + return +} + +func intMax(a, b int) int { + if a > b { + return a + } + return b +} + +// Difference represents a single part of the data being diffed, containing +// information about both the original and new values. +// From and To are the sets of bytes in the original and the new byte slice. +// !Replace implies From == To (no change) +// len(To) == 0 implies From is being deleted +// len(From) == 0 implies To is being inserted +// else implies From is being replaced by To +type Difference struct { + Replace bool + From, To []byte +} + +// color returns the bash color for a given difference. +func (c *OutputFormat) color(d Difference) string { + switch { + case !d.Replace: + return "" + case len(d.From) == 0: + return c.remove + case len(d.To) == 0: + return c.add + default: + return c.change + } +} + +// Diff diffs strA and strB, returning a list of differences which +// can be used to construct either the original or new string. +// +// Diff is optimized for comparing VERY SHORT slices. It's meant for comparing +// things like packets off the wire, not large files or the like. +// As such, its runtime can be catastrophic if large inputs are passed in. +// You've been warned. +func Diff(strA, strB []byte) Differences { + if len(strA) == 0 && len(strB) == 0 { + return nil + } + ia, ib, l := longestCommonSubstring(strA, strB) + if l == 0 { + return Differences{ + Difference{true, strA, strB}, + } + } + beforeA, match, afterA := strA[:ia], strA[ia:ia+l], strA[ia+l:] + beforeB, afterB := strB[:ib], strB[ib+l:] + var diffs Differences + diffs = append(diffs, Diff(beforeA, beforeB)...) + diffs = append(diffs, Difference{false, match, match}) + diffs = append(diffs, Diff(afterA, afterB)...) + return diffs +} + +// Differences is a set of differences for a given diff'd pair of byte slices. +type Differences []Difference + +// String outputs a previously diff'd set of strings, showing differences +// between them, highlighted by colors. +// +// The output format of this function is NOT guaranteed consistent, and may be +// changed at any time by the library authors. It's meant solely for human +// consumption. +func (c *OutputFormat) String(diffs Differences) string { + var buf bytes.Buffer + count := 0 + fmt.Fprintf(&buf, "%s", c.start) + fmt.Fprintf(&buf, "00000000 ") + for i := 0; i < len(diffs); i++ { + diff := diffs[i] + color := c.color(diff) + reset := "" + if color != "" { + reset = c.reset + } + fmt.Fprint(&buf, color) + for _, b := range diff.From { + fmt.Fprintf(&buf, " %02x", b) + count++ + switch count % 16 { + case 0: + fmt.Fprintf(&buf, "%v\n%08x%v ", reset, count, color) + case 8: + fmt.Fprintf(&buf, " ") + } + } + fmt.Fprint(&buf, reset) + } + fmt.Fprintf(&buf, "\n\n00000000 ") + count = 0 + for i := 0; i < len(diffs); i++ { + diff := diffs[i] + str := diff.From + if diff.Replace { + str = diff.To + } + color := c.color(diff) + reset := "" + if color != "" { + reset = c.reset + } + fmt.Fprint(&buf, color) + for _, b := range str { + fmt.Fprintf(&buf, " %02x", b) + count++ + switch count % 16 { + case 0: + fmt.Fprintf(&buf, "%v\n%08x%v ", reset, count, color) + case 8: + fmt.Fprintf(&buf, " ") + } + } + fmt.Fprint(&buf, reset) + } + fmt.Fprint(&buf, "\n") + fmt.Fprintf(&buf, "%s", c.finish) + return string(buf.Bytes()) +} diff --git a/vendor/github.com/google/gopacket/bytediff/bytediff_test.go b/vendor/github.com/google/gopacket/bytediff/bytediff_test.go new file mode 100644 index 00000000..022ad4bc --- /dev/null +++ b/vendor/github.com/google/gopacket/bytediff/bytediff_test.go @@ -0,0 +1,53 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package bytediff + +import ( + "reflect" + "testing" +) + +func TestLCS(t *testing.T) { + for i, test := range []struct { + a, b []byte + indexA, indexB, length int + }{ + {[]byte{1, 2, 3}, []byte{1, 2, 3}, 0, 0, 3}, + {[]byte{0, 1, 2, 3}, []byte{1, 2, 3, 4}, 1, 0, 3}, + {[]byte{0, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3}, []byte{1, 2, 3, 4}, 4, 0, 4}, + {[]byte{1, 2, 2, 3, 4}, []byte{1, 2, 3, 4}, 2, 1, 3}, + {[]byte{0, 1, 2, 3, 4}, []byte{1, 1, 2, 2, 3, 4}, 2, 3, 3}, + } { + ia, ib, l := longestCommonSubstring(test.a, test.b) + if ia != test.indexA || ib != test.indexB || l != test.length { + t.Errorf("%d: want (%d %d %d) got (%d %d %d)", i, test.indexA, test.indexB, test.length, ia, ib, l) + } + } +} + +func TestDiff(t *testing.T) { + for i, test := range []struct { + a, b []byte + d Differences + }{ + { + []byte{0, 1, 2, 3, 4}, + []byte{1, 1, 2, 2, 3, 4}, + Differences{ + Difference{true, []byte{0}, []byte{}}, + Difference{false, []byte{1}, []byte{1}}, + Difference{true, []byte{}, []byte{1, 2}}, + Difference{false, []byte{2, 3, 4}, []byte{2, 3, 4}}, + }, + }, + } { + diffs := Diff(test.a, test.b) + if !reflect.DeepEqual(diffs, test.d) { + t.Errorf("%d want %v got %v", i, test.d, diffs) + } + } +} diff --git a/vendor/github.com/google/gopacket/decode.go b/vendor/github.com/google/gopacket/decode.go new file mode 100644 index 00000000..2633f848 --- /dev/null +++ b/vendor/github.com/google/gopacket/decode.go @@ -0,0 +1,157 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "errors" +) + +// DecodeFeedback is used by DecodingLayer layers to provide decoding metadata. +type DecodeFeedback interface { + // SetTruncated should be called if during decoding you notice that a packet + // is shorter than internal layer variables (HeaderLength, or the like) say it + // should be. It sets packet.Metadata().Truncated. + SetTruncated() +} + +type nilDecodeFeedback struct{} + +func (nilDecodeFeedback) SetTruncated() {} + +// NilDecodeFeedback implements DecodeFeedback by doing nothing. +var NilDecodeFeedback DecodeFeedback = nilDecodeFeedback{} + +// PacketBuilder is used by layer decoders to store the layers they've decoded, +// and to defer future decoding via NextDecoder. +// Typically, the pattern for use is: +// func (m *myDecoder) Decode(data []byte, p PacketBuilder) error { +// if myLayer, err := myDecodingLogic(data); err != nil { +// return err +// } else { +// p.AddLayer(myLayer) +// } +// // maybe do this, if myLayer is a LinkLayer +// p.SetLinkLayer(myLayer) +// return p.NextDecoder(nextDecoder) +// } +type PacketBuilder interface { + DecodeFeedback + // AddLayer should be called by a decoder immediately upon successful + // decoding of a layer. + AddLayer(l Layer) + // The following functions set the various specific layers in the final + // packet. Note that if many layers call SetX, the first call is kept and all + // other calls are ignored. + SetLinkLayer(LinkLayer) + SetNetworkLayer(NetworkLayer) + SetTransportLayer(TransportLayer) + SetApplicationLayer(ApplicationLayer) + SetErrorLayer(ErrorLayer) + // NextDecoder should be called by a decoder when they're done decoding a + // packet layer but not done with decoding the entire packet. The next + // decoder will be called to decode the last AddLayer's LayerPayload. + // Because of this, NextDecoder must only be called once all other + // PacketBuilder calls have been made. Set*Layer and AddLayer calls after + // NextDecoder calls will behave incorrectly. + NextDecoder(next Decoder) error + // DumpPacketData is used solely for decoding. If you come across an error + // you need to diagnose while processing a packet, call this and your packet's + // data will be dumped to stderr so you can create a test. This should never + // be called from a production decoder. + DumpPacketData() + // DecodeOptions returns the decode options + DecodeOptions() *DecodeOptions +} + +// Decoder is an interface for logic to decode a packet layer. Users may +// implement a Decoder to handle their own strange packet types, or may use one +// of the many decoders available in the 'layers' subpackage to decode things +// for them. +type Decoder interface { + // Decode decodes the bytes of a packet, sending decoded values and other + // information to PacketBuilder, and returning an error if unsuccessful. See + // the PacketBuilder documentation for more details. + Decode([]byte, PacketBuilder) error +} + +// DecodeFunc wraps a function to make it a Decoder. +type DecodeFunc func([]byte, PacketBuilder) error + +// Decode implements Decoder by calling itself. +func (d DecodeFunc) Decode(data []byte, p PacketBuilder) error { + // function, call thyself. + return d(data, p) +} + +// DecodePayload is a Decoder that returns a Payload layer containing all +// remaining bytes. +var DecodePayload Decoder = DecodeFunc(decodePayload) + +// DecodeUnknown is a Decoder that returns an Unknown layer containing all +// remaining bytes, useful if you run up against a layer that you're unable to +// decode yet. This layer is considered an ErrorLayer. +var DecodeUnknown Decoder = DecodeFunc(decodeUnknown) + +// DecodeFragment is a Decoder that returns a Fragment layer containing all +// remaining bytes. +var DecodeFragment Decoder = DecodeFunc(decodeFragment) + +// LayerTypeZero is an invalid layer type, but can be used to determine whether +// layer type has actually been set correctly. +var LayerTypeZero = RegisterLayerType(0, LayerTypeMetadata{Name: "Unknown", Decoder: DecodeUnknown}) + +// LayerTypeDecodeFailure is the layer type for the default error layer. +var LayerTypeDecodeFailure = RegisterLayerType(1, LayerTypeMetadata{Name: "DecodeFailure", Decoder: DecodeUnknown}) + +// LayerTypePayload is the layer type for a payload that we don't try to decode +// but treat as a success, IE: an application-level payload. +var LayerTypePayload = RegisterLayerType(2, LayerTypeMetadata{Name: "Payload", Decoder: DecodePayload}) + +// LayerTypeFragment is the layer type for a fragment of a layer transported +// by an underlying layer that supports fragmentation. +var LayerTypeFragment = RegisterLayerType(3, LayerTypeMetadata{Name: "Fragment", Decoder: DecodeFragment}) + +// DecodeFailure is a packet layer created if decoding of the packet data failed +// for some reason. It implements ErrorLayer. LayerContents will be the entire +// set of bytes that failed to parse, and Error will return the reason parsing +// failed. +type DecodeFailure struct { + data []byte + err error + stack []byte +} + +// Error returns the error encountered during decoding. +func (d *DecodeFailure) Error() error { return d.err } + +// LayerContents implements Layer. +func (d *DecodeFailure) LayerContents() []byte { return d.data } + +// LayerPayload implements Layer. +func (d *DecodeFailure) LayerPayload() []byte { return nil } + +// String implements fmt.Stringer. +func (d *DecodeFailure) String() string { + return "Packet decoding error: " + d.Error().Error() +} + +// Dump implements Dumper. +func (d *DecodeFailure) Dump() (s string) { + if d.stack != nil { + s = string(d.stack) + } + return +} + +// LayerType returns LayerTypeDecodeFailure +func (d *DecodeFailure) LayerType() LayerType { return LayerTypeDecodeFailure } + +// decodeUnknown "decodes" unsupported data types by returning an error. +// This decoder will thus always return a DecodeFailure layer. +func decodeUnknown(data []byte, p PacketBuilder) error { + return errors.New("Layer type not currently supported") +} diff --git a/vendor/github.com/google/gopacket/doc.go b/vendor/github.com/google/gopacket/doc.go new file mode 100644 index 00000000..61989407 --- /dev/null +++ b/vendor/github.com/google/gopacket/doc.go @@ -0,0 +1,365 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/* +Package gopacket provides packet decoding for the Go language. + +gopacket contains many sub-packages with additional functionality you may find +useful, including: + + * layers: You'll probably use this every time. This contains of the logic + built into gopacket for decoding packet protocols. Note that all example + code below assumes that you have imported both gopacket and + gopacket/layers. + * pcap: C bindings to use libpcap to read packets off the wire. + * pfring: C bindings to use PF_RING to read packets off the wire. + * afpacket: C bindings for Linux's AF_PACKET to read packets off the wire. + * tcpassembly: TCP stream reassembly + +Also, if you're looking to dive right into code, see the examples subdirectory +for numerous simple binaries built using gopacket libraries. + +Basic Usage + +gopacket takes in packet data as a []byte and decodes it into a packet with +a non-zero number of "layers". Each layer corresponds to a protocol +within the bytes. Once a packet has been decoded, the layers of the packet +can be requested from the packet. + + // Decode a packet + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default) + // Get the TCP layer from this packet + if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { + fmt.Println("This is a TCP packet!") + // Get actual TCP data from this layer + tcp, _ := tcpLayer.(*layers.TCP) + fmt.Printf("From src port %d to dst port %d\n", tcp.SrcPort, tcp.DstPort) + } + // Iterate over all layers, printing out each layer type + for _, layer := range packet.Layers() { + fmt.Println("PACKET LAYER:", layer.LayerType()) + } + +Packets can be decoded from a number of starting points. Many of our base +types implement Decoder, which allow us to decode packets for which +we don't have full data. + + // Decode an ethernet packet + ethP := gopacket.NewPacket(p1, layers.LayerTypeEthernet, gopacket.Default) + // Decode an IPv6 header and everything it contains + ipP := gopacket.NewPacket(p2, layers.LayerTypeIPv6, gopacket.Default) + // Decode a TCP header and its payload + tcpP := gopacket.NewPacket(p3, layers.LayerTypeTCP, gopacket.Default) + + +Reading Packets From A Source + +Most of the time, you won't just have a []byte of packet data lying around. +Instead, you'll want to read packets in from somewhere (file, interface, etc) +and process them. To do that, you'll want to build a PacketSource. + +First, you'll need to construct an object that implements the PacketDataSource +interface. There are implementations of this interface bundled with gopacket +in the gopacket/pcap and gopacket/pfring subpackages... see their documentation +for more information on their usage. Once you have a PacketDataSource, you can +pass it into NewPacketSource, along with a Decoder of your choice, to create +a PacketSource. + +Once you have a PacketSource, you can read packets from it in multiple ways. +See the docs for PacketSource for more details. The easiest method is the +Packets function, which returns a channel, then asynchronously writes new +packets into that channel, closing the channel if the packetSource hits an +end-of-file. + + packetSource := ... // construct using pcap or pfring + for packet := range packetSource.Packets() { + handlePacket(packet) // do something with each packet + } + +You can change the decoding options of the packetSource by setting fields in +packetSource.DecodeOptions... see the following sections for more details. + + +Lazy Decoding + +gopacket optionally decodes packet data lazily, meaning it +only decodes a packet layer when it needs to handle a function call. + + // Create a packet, but don't actually decode anything yet + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) + // Now, decode the packet up to the first IPv4 layer found but no further. + // If no IPv4 layer was found, the whole packet will be decoded looking for + // it. + ip4 := packet.Layer(layers.LayerTypeIPv4) + // Decode all layers and return them. The layers up to the first IPv4 layer + // are already decoded, and will not require decoding a second time. + layers := packet.Layers() + +Lazily-decoded packets are not concurrency-safe. Since layers have not all been +decoded, each call to Layer() or Layers() has the potential to mutate the packet +in order to decode the next layer. If a packet is used +in multiple goroutines concurrently, don't use gopacket.Lazy. Then gopacket +will decode the packet fully, and all future function calls won't mutate the +object. + + +NoCopy Decoding + +By default, gopacket will copy the slice passed to NewPacket and store the +copy within the packet, so future mutations to the bytes underlying the slice +don't affect the packet and its layers. If you can guarantee that the +underlying slice bytes won't be changed, you can use NoCopy to tell +gopacket.NewPacket, and it'll use the passed-in slice itself. + + // This channel returns new byte slices, each of which points to a new + // memory location that's guaranteed immutable for the duration of the + // packet. + for data := range myByteSliceChannel { + p := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) + doSomethingWithPacket(p) + } + +The fastest method of decoding is to use both Lazy and NoCopy, but note from +the many caveats above that for some implementations either or both may be +dangerous. + + +Pointers To Known Layers + +During decoding, certain layers are stored in the packet as well-known +layer types. For example, IPv4 and IPv6 are both considered NetworkLayer +layers, while TCP and UDP are both TransportLayer layers. We support 4 +layers, corresponding to the 4 layers of the TCP/IP layering scheme (roughly +anagalous to layers 2, 3, 4, and 7 of the OSI model). To access these, +you can use the packet.LinkLayer, packet.NetworkLayer, +packet.TransportLayer, and packet.ApplicationLayer functions. Each of +these functions returns a corresponding interface +(gopacket.{Link,Network,Transport,Application}Layer). The first three +provide methods for getting src/dst addresses for that particular layer, +while the final layer provides a Payload function to get payload data. +This is helpful, for example, to get payloads for all packets regardless +of their underlying data type: + + // Get packets from some source + for packet := range someSource { + if app := packet.ApplicationLayer(); app != nil { + if strings.Contains(string(app.Payload()), "magic string") { + fmt.Println("Found magic string in a packet!") + } + } + } + +A particularly useful layer is ErrorLayer, which is set whenever there's +an error parsing part of the packet. + + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default) + if err := packet.ErrorLayer(); err != nil { + fmt.Println("Error decoding some part of the packet:", err) + } + +Note that we don't return an error from NewPacket because we may have decoded +a number of layers successfully before running into our erroneous layer. You +may still be able to get your Ethernet and IPv4 layers correctly, even if +your TCP layer is malformed. + + +Flow And Endpoint + +gopacket has two useful objects, Flow and Endpoint, for communicating in a protocol +independent manner the fact that a packet is coming from A and going to B. +The general layer types LinkLayer, NetworkLayer, and TransportLayer all provide +methods for extracting their flow information, without worrying about the type +of the underlying Layer. + +A Flow is a simple object made up of a set of two Endpoints, one source and one +destination. It details the sender and receiver of the Layer of the Packet. + +An Endpoint is a hashable representation of a source or destination. For +example, for LayerTypeIPv4, an Endpoint contains the IP address bytes for a v4 +IP packet. A Flow can be broken into Endpoints, and Endpoints can be combined +into Flows: + + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) + netFlow := packet.NetworkLayer().NetworkFlow() + src, dst := netFlow.Endpoints() + reverseFlow := gopacket.NewFlow(dst, src) + +Both Endpoint and Flow objects can be used as map keys, and the equality +operator can compare them, so you can easily group together all packets +based on endpoint criteria: + + flows := map[gopacket.Endpoint]chan gopacket.Packet + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) + // Send all TCP packets to channels based on their destination port. + if tcp := packet.Layer(layers.LayerTypeTCP); tcp != nil { + flows[tcp.TransportFlow().Dst()] <- packet + } + // Look for all packets with the same source and destination network address + if net := packet.NetworkLayer(); net != nil { + src, dst := net.NetworkFlow().Endpoints() + if src == dst { + fmt.Println("Fishy packet has same network source and dst: %s", src) + } + } + // Find all packets coming from UDP port 1000 to UDP port 500 + interestingFlow := gopacket.NewFlow(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500)) + if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow { + fmt.Println("Found that UDP flow I was looking for!") + } + +For load-balancing purposes, both Flow and Endpoint have FastHash() functions, +which provide quick, non-cryptographic hashes of their contents. Of particular +importance is the fact that Flow FastHash() is symmetric: A->B will have the same +hash as B->A. An example usage could be: + + channels := [8]chan gopacket.Packet + for i := 0; i < 8; i++ { + channels[i] = make(chan gopacket.Packet) + go packetHandler(channels[i]) + } + for packet := range getPackets() { + if net := packet.NetworkLayer(); net != nil { + channels[int(net.NetworkFlow().FastHash()) & 0x7] <- packet + } + } + +This allows us to split up a packet stream while still making sure that each +stream sees all packets for a flow (and its bidirectional opposite). + + +Implementing Your Own Decoder + +If your network has some strange encapsulation, you can implement your own +decoder. In this example, we handle Ethernet packets which are encapsulated +in a 4-byte header. + + // Create a layer type, should be unique and high, so it doesn't conflict, + // giving it a name and a decoder to use. + var MyLayerType = gopacket.RegisterLayerType(12345, gopacket.LayerTypeMetadata{Name: "MyLayerType", Decoder: gopacket.DecodeFunc(decodeMyLayer)}) + + // Implement my layer + type MyLayer struct { + StrangeHeader []byte + payload []byte + } + func (m MyLayer) LayerType() gopacket.LayerType { return MyLayerType } + func (m MyLayer) LayerContents() []byte { return m.StrangeHeader } + func (m MyLayer) LayerPayload() []byte { return m.payload } + + // Now implement a decoder... this one strips off the first 4 bytes of the + // packet. + func decodeMyLayer(data []byte, p gopacket.PacketBuilder) error { + // Create my layer + p.AddLayer(&MyLayer{data[:4], data[4:]}) + // Determine how to handle the rest of the packet + return p.NextDecoder(layers.LayerTypeEthernet) + } + + // Finally, decode your packets: + p := gopacket.NewPacket(data, MyLayerType, gopacket.Lazy) + +See the docs for Decoder and PacketBuilder for more details on how coding +decoders works, or look at RegisterLayerType and RegisterEndpointType to see how +to add layer/endpoint types to gopacket. + + +Fast Decoding With DecodingLayerParser + +TLDR: DecodingLayerParser takes about 10% of the time as NewPacket to decode +packet data, but only for known packet stacks. + +Basic decoding using gopacket.NewPacket or PacketSource.Packets is somewhat slow +due to its need to allocate a new packet and every respective layer. It's very +versatile and can handle all known layer types, but sometimes you really only +care about a specific set of layers regardless, so that versatility is wasted. + +DecodingLayerParser avoids memory allocation altogether by decoding packet +layers directly into preallocated objects, which you can then reference to get +the packet's information. A quick example: + + func main() { + var eth layers.Ethernet + var ip4 layers.IPv4 + var ip6 layers.IPv6 + var tcp layers.TCP + parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp) + decoded := []gopacket.LayerType{} + for packetData := range somehowGetPacketData() { + err := parser.DecodeLayers(packetData, &decoded) + for _, layerType := range decoded { + switch layerType { + case layers.LayerTypeIPv6: + fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP) + case layers.LayerTypeIPv4: + fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP) + } + } + } + } + +The important thing to note here is that the parser is modifying the passed in +layers (eth, ip4, ip6, tcp) instead of allocating new ones, thus greatly +speeding up the decoding process. It's even branching based on layer type... +it'll handle an (eth, ip4, tcp) or (eth, ip6, tcp) stack. However, it won't +handle any other type... since no other decoders were passed in, an (eth, ip4, +udp) stack will stop decoding after ip4, and only pass back [LayerTypeEthernet, +LayerTypeIPv4] through the 'decoded' slice (along with an error saying it can't +decode a UDP packet). + +Unfortunately, not all layers can be used by DecodingLayerParser... only those +implementing the DecodingLayer interface are usable. Also, it's possible to +create DecodingLayers that are not themselves Layers... see +layers.IPv6ExtensionSkipper for an example of this. + + +Creating Packet Data + +As well as offering the ability to decode packet data, gopacket will allow you +to create packets from scratch, as well. A number of gopacket layers implement +the SerializableLayer interface; these layers can be serialized to a []byte in +the following manner: + + ip := &layers.IPv4{ + SrcIP: net.IP{1, 2, 3, 4}, + DstIP: net.IP{5, 6, 7, 8}, + // etc... + } + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} // See SerializeOptions for more details. + err := ip.SerializeTo(&buf, opts) + if err != nil { panic(err) } + fmt.Println(buf.Bytes()) // prints out a byte slice containing the serialized IPv4 layer. + +SerializeTo PREPENDS the given layer onto the SerializeBuffer, and they treat +the current buffer's Bytes() slice as the payload of the serializing layer. +Therefore, you can serialize an entire packet by serializing a set of layers in +reverse order (Payload, then TCP, then IP, then Ethernet, for example). The +SerializeBuffer's SerializeLayers function is a helper that does exactly that. + +To generate a (empty and useless, because no fields are set) +Ethernet(IPv4(TCP(Payload))) packet, for example, you can run: + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} + gopacket.SerializeLayers(buf, opts, + &layers.Ethernet{}, + &layers.IPv4{}, + &layers.TCP{}, + gopacket.Payload([]byte{1, 2, 3, 4})) + packetData := buf.Bytes() + +A Final Note + +If you use gopacket, you'll almost definitely want to make sure gopacket/layers +is imported, since when imported it sets all the LayerType variables and fills +in a lot of interesting variables/maps (DecodersByLayerName, etc). Therefore, +it's recommended that even if you don't use any layers functions directly, you still import with: + + import ( + _ "github.com/google/gopacket/layers" + ) +*/ +package gopacket diff --git a/vendor/github.com/google/gopacket/dumpcommand/tcpdump.go b/vendor/github.com/google/gopacket/dumpcommand/tcpdump.go new file mode 100644 index 00000000..2d357220 --- /dev/null +++ b/vendor/github.com/google/gopacket/dumpcommand/tcpdump.go @@ -0,0 +1,119 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package dumpcommand implements a run function for pfdump and pcapdump +// with many similar flags/features to tcpdump. This code is split out seperate +// from data sources (pcap/pfring) so it can be used by both. +package dumpcommand + +import ( + "flag" + "fmt" + "log" + "os" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/ip4defrag" + "github.com/google/gopacket/layers" // pulls in all layers decoders +) + +var ( + print = flag.Bool("print", true, "Print out packets, if false only prints out statistics") + maxcount = flag.Int("c", -1, "Only grab this many packets, then exit") + decoder = flag.String("decoder", "Ethernet", "Name of the decoder to use") + dump = flag.Bool("X", false, "If true, dump very verbose info on each packet") + statsevery = flag.Int("stats", 1000, "Output statistics every N packets") + printErrors = flag.Bool("errors", false, "Print out packet dumps of decode errors, useful for checking decoders against live traffic") + lazy = flag.Bool("lazy", false, "If true, do lazy decoding") + defrag = flag.Bool("defrag", false, "If true, do IPv4 defrag") +) + +func Run(src gopacket.PacketDataSource) { + if !flag.Parsed() { + log.Fatalln("Run called without flags.Parse() being called") + } + var dec gopacket.Decoder + var ok bool + if dec, ok = gopacket.DecodersByLayerName[*decoder]; !ok { + log.Fatalln("No decoder named", *decoder) + } + source := gopacket.NewPacketSource(src, dec) + source.Lazy = *lazy + source.NoCopy = true + source.DecodeStreamsAsDatagrams = true + fmt.Fprintln(os.Stderr, "Starting to read packets") + count := 0 + bytes := int64(0) + start := time.Now() + errors := 0 + truncated := 0 + layertypes := map[gopacket.LayerType]int{} + defragger := ip4defrag.NewIPv4Defragmenter() + + for packet := range source.Packets() { + count++ + bytes += int64(len(packet.Data())) + + // defrag the IPv4 packet if required + if *defrag { + ip4Layer := packet.Layer(layers.LayerTypeIPv4) + if ip4Layer == nil { + continue + } + ip4 := ip4Layer.(*layers.IPv4) + l := ip4.Length + + newip4, err := defragger.DefragIPv4(ip4) + if err != nil { + log.Fatalln("Error while de-fragmenting", err) + } else if newip4 == nil { + continue // packet fragment, we don't have whole packet yet. + } + if newip4.Length != l { + fmt.Printf("Decoding re-assembled packet: %s\n", newip4.NextLayerType()) + pb, ok := packet.(gopacket.PacketBuilder) + if !ok { + panic("Not a PacketBuilder") + } + nextDecoder := newip4.NextLayerType() + nextDecoder.Decode(newip4.Payload, pb) + } + } + + if *dump { + fmt.Println(packet.Dump()) + } else if *print { + fmt.Println(packet) + } + if !*lazy || *print || *dump { // if we've already decoded all layers... + for _, layer := range packet.Layers() { + layertypes[layer.LayerType()]++ + } + if packet.Metadata().Truncated { + truncated++ + } + if errLayer := packet.ErrorLayer(); errLayer != nil { + errors++ + if *printErrors { + fmt.Println("Error:", errLayer.Error()) + fmt.Println("--- Packet ---") + fmt.Println(packet.Dump()) + } + } + } + done := *maxcount > 0 && count >= *maxcount + if count%*statsevery == 0 || done { + fmt.Fprintf(os.Stderr, "Processed %v packets (%v bytes) in %v, %v errors and %v truncated packets\n", count, bytes, time.Since(start), errors, truncated) + if len(layertypes) > 0 { + fmt.Fprintf(os.Stderr, "Layer types seen: %+v\n", layertypes) + } + } + if done { + break + } + } +} diff --git a/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go b/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go new file mode 100644 index 00000000..1a0e33e5 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go @@ -0,0 +1,188 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// arpscan implements ARP scanning of all interfaces' local networks using +// gopacket and its subpackages. This example shows, among other things: +// * Generating and sending packet data +// * Reading in packet data and interpreting it +// * Use of the 'pcap' subpackage for reading/writing +package main + +import ( + "bytes" + "encoding/binary" + "errors" + "log" + "net" + "sync" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" +) + +func main() { + // Get a list of all interfaces. + ifaces, err := net.Interfaces() + if err != nil { + panic(err) + } + + var wg sync.WaitGroup + for _, iface := range ifaces { + wg.Add(1) + // Start up a scan on each interface. + go func(iface net.Interface) { + defer wg.Done() + if err := scan(&iface); err != nil { + log.Printf("interface %v: %v", iface.Name, err) + } + }(iface) + } + // Wait for all interfaces' scans to complete. They'll try to run + // forever, but will stop on an error, so if we get past this Wait + // it means all attempts to write have failed. + wg.Wait() +} + +// scan scans an individual interface's local network for machines using ARP requests/replies. +// +// scan loops forever, sending packets out regularly. It returns an error if +// it's ever unable to write a packet. +func scan(iface *net.Interface) error { + // We just look for IPv4 addresses, so try to find if the interface has one. + var addr *net.IPNet + if addrs, err := iface.Addrs(); err != nil { + return err + } else { + for _, a := range addrs { + if ipnet, ok := a.(*net.IPNet); ok { + if ip4 := ipnet.IP.To4(); ip4 != nil { + addr = &net.IPNet{ + IP: ip4, + Mask: ipnet.Mask[len(ipnet.Mask)-4:], + } + break + } + } + } + } + // Sanity-check that the interface has a good address. + if addr == nil { + return errors.New("no good IP network found") + } else if addr.IP[0] == 127 { + return errors.New("skipping localhost") + } else if addr.Mask[0] != 0xff || addr.Mask[1] != 0xff { + return errors.New("mask means network is too large") + } + log.Printf("Using network range %v for interface %v", addr, iface.Name) + + // Open up a pcap handle for packet reads/writes. + handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever) + if err != nil { + return err + } + defer handle.Close() + + // Start up a goroutine to read in packet data. + stop := make(chan struct{}) + go readARP(handle, iface, stop) + defer close(stop) + for { + // Write our scan packets out to the handle. + if err := writeARP(handle, iface, addr); err != nil { + log.Printf("error writing packets on %v: %v", iface.Name, err) + return err + } + // We don't know exactly how long it'll take for packets to be + // sent back to us, but 10 seconds should be more than enough + // time ;) + time.Sleep(10 * time.Second) + } +} + +// readARP watches a handle for incoming ARP responses we might care about, and prints them. +// +// readARP loops until 'stop' is closed. +func readARP(handle *pcap.Handle, iface *net.Interface, stop chan struct{}) { + src := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet) + in := src.Packets() + for { + var packet gopacket.Packet + select { + case <-stop: + return + case packet = <-in: + arpLayer := packet.Layer(layers.LayerTypeARP) + if arpLayer == nil { + continue + } + arp := arpLayer.(*layers.ARP) + if arp.Operation != layers.ARPReply || bytes.Equal([]byte(iface.HardwareAddr), arp.SourceHwAddress) { + // This is a packet I sent. + continue + } + // Note: we might get some packets here that aren't responses to ones we've sent, + // if for example someone else sends US an ARP request. Doesn't much matter, though... + // all information is good information :) + log.Printf("IP %v is at %v", net.IP(arp.SourceProtAddress), net.HardwareAddr(arp.SourceHwAddress)) + } + } +} + +// writeARP writes an ARP request for each address on our local network to the +// pcap handle. +func writeARP(handle *pcap.Handle, iface *net.Interface, addr *net.IPNet) error { + // Set up all the layers' fields we can. + eth := layers.Ethernet{ + SrcMAC: iface.HardwareAddr, + DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + EthernetType: layers.EthernetTypeARP, + } + arp := layers.ARP{ + AddrType: layers.LinkTypeEthernet, + Protocol: layers.EthernetTypeIPv4, + HwAddressSize: 6, + ProtAddressSize: 4, + Operation: layers.ARPRequest, + SourceHwAddress: []byte(iface.HardwareAddr), + SourceProtAddress: []byte(addr.IP), + DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, + } + // Set up buffer and options for serialization. + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + // Send one packet for every address. + for _, ip := range ips(addr) { + arp.DstProtAddress = []byte(ip) + gopacket.SerializeLayers(buf, opts, ð, &arp) + if err := handle.WritePacketData(buf.Bytes()); err != nil { + return err + } + } + return nil +} + +// ips is a simple and not very good method for getting all IPv4 addresses from a +// net.IPNet. It returns all IPs it can over the channel it sends back, closing +// the channel when done. +func ips(n *net.IPNet) (out []net.IP) { + num := binary.BigEndian.Uint32([]byte(n.IP)) + mask := binary.BigEndian.Uint32([]byte(n.Mask)) + num &= mask + for mask < 0xffffffff { + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], num) + out = append(out, net.IP(buf[:])) + mask += 1 + num += 1 + } + return +} diff --git a/vendor/github.com/google/gopacket/examples/bidirectional/main.go b/vendor/github.com/google/gopacket/examples/bidirectional/main.go new file mode 100644 index 00000000..4b0b240d --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/bidirectional/main.go @@ -0,0 +1,192 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary provides an example of connecting up bidirectional streams from +// the unidirectional streams provided by gopacket/tcpassembly. +package main + +import ( + "flag" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/tcpassembly" + "log" + "time" +) + +var iface = flag.String("i", "eth0", "Interface to get packets from") +var snaplen = flag.Int("s", 16<<10, "SnapLen for pcap packet capture") +var filter = flag.String("f", "tcp", "BPF filter for pcap") +var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail") + +// key is used to map bidirectional streams to each other. +type key struct { + net, transport gopacket.Flow +} + +// String prints out the key in a human-readable fashion. +func (k key) String() string { + return fmt.Sprintf("%v:%v", k.net, k.transport) +} + +// timeout is the length of time to wait befor flushing connections and +// bidirectional stream pairs. +const timeout time.Duration = time.Minute * 5 + +// myStream implements tcpassembly.Stream +type myStream struct { + bytes int64 // total bytes seen on this stream. + bidi *bidi // maps to my bidirectional twin. + done bool // if true, we've seen the last packet we're going to for this stream. +} + +// bidi stores each unidirectional side of a bidirectional stream. +// +// When a new stream comes in, if we don't have an opposite stream, a bidi is +// created with 'a' set to the new stream. If we DO have an opposite stream, +// 'b' is set to the new stream. +type bidi struct { + key key // Key of the first stream, mostly for logging. + a, b *myStream // the two bidirectional streams. + lastPacketSeen time.Time // last time we saw a packet from either stream. +} + +// myFactory implements tcpassmebly.StreamFactory +type myFactory struct { + // bidiMap maps keys to bidirectional stream pairs. + bidiMap map[key]*bidi +} + +// New handles creating a new tcpassembly.Stream. +func (f *myFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream { + // Create a new stream. + s := &myStream{} + + // Find the bidi bidirectional struct for this stream, creating a new one if + // one doesn't already exist in the map. + k := key{netFlow, tcpFlow} + bd := f.bidiMap[k] + if bd == nil { + bd = &bidi{a: s, key: k} + log.Printf("[%v] created first side of bidirectional stream", bd.key) + // Register bidirectional with the reverse key, so the matching stream going + // the other direction will find it. + f.bidiMap[key{netFlow.Reverse(), tcpFlow.Reverse()}] = bd + } else { + log.Printf("[%v] found second side of bidirectional stream", bd.key) + bd.b = s + // Clear out the bidi we're using from the map, just in case. + delete(f.bidiMap, k) + } + s.bidi = bd + return s +} + +// emptyStream is used to finish bidi that only have one stream, in +// collectOldStreams. +var emptyStream = &myStream{done: true} + +// collectOldStreams finds any streams that haven't received a packet within +// 'timeout', and sets/finishes the 'b' stream inside them. The 'a' stream may +// still receive packets after this. +func (f *myFactory) collectOldStreams() { + cutoff := time.Now().Add(-timeout) + for k, bd := range f.bidiMap { + if bd.lastPacketSeen.Before(cutoff) { + log.Printf("[%v] timing out old stream", bd.key) + bd.b = emptyStream // stub out b with an empty stream. + delete(f.bidiMap, k) // remove it from our map. + bd.maybeFinish() // if b was the last stream we were waiting for, finish up. + } + } +} + +// Reassembled handles reassembled TCP stream data. +func (s *myStream) Reassembled(rs []tcpassembly.Reassembly) { + for _, r := range rs { + // For now, we'll simply count the bytes on each side of the TCP stream. + s.bytes += int64(len(r.Bytes)) + if r.Skip > 0 { + s.bytes += int64(r.Skip) + } + // Mark that we've received new packet data. + // We could just use time.Now, but by using r.Seen we handle the case + // where packets are being read from a file and could be very old. + if s.bidi.lastPacketSeen.After(r.Seen) { + s.bidi.lastPacketSeen = r.Seen + } + } +} + +// ReassemblyComplete marks this stream as finished. +func (s *myStream) ReassemblyComplete() { + s.done = true + s.bidi.maybeFinish() +} + +// maybeFinish will wait until both directions are complete, then print out +// stats. +func (bd *bidi) maybeFinish() { + switch { + case bd.a == nil: + log.Fatalf("[%v] a should always be non-nil, since it's set when bidis are created", bd.key) + case !bd.a.done: + log.Printf("[%v] still waiting on first stream", bd.key) + case bd.b == nil: + log.Printf("[%v] no second stream yet", bd.key) + case !bd.b.done: + log.Printf("[%v] still waiting on second stream", bd.key) + default: + log.Printf("[%v] FINISHED, bytes: %d tx, %d rx", bd.key, bd.a.bytes, bd.b.bytes) + } +} + +func main() { + defer util.Run()() + log.Printf("starting capture on interface %q", *iface) + // Set up pcap packet capture + handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever) + if err != nil { + panic(err) + } + if err := handle.SetBPFFilter(*filter); err != nil { + panic(err) + } + + // Set up assembly + streamFactory := &myFactory{bidiMap: make(map[key]*bidi)} + streamPool := tcpassembly.NewStreamPool(streamFactory) + assembler := tcpassembly.NewAssembler(streamPool) + + log.Println("reading in packets") + // Read in packets, pass to assembler. + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + packets := packetSource.Packets() + ticker := time.Tick(timeout / 4) + for { + select { + case packet := <-packets: + if *logAllPackets { + log.Println(packet) + } + if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP { + log.Println("Unusable packet") + continue + } + tcp := packet.TransportLayer().(*layers.TCP) + assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp) + + case <-ticker: + // Every minute, flush connections that haven't seen activity in the past minute. + log.Println("---- FLUSHING ----") + assembler.FlushOlderThan(time.Now().Add(-timeout)) + streamFactory.collectOldStreams() + } + } +} diff --git a/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png b/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png new file mode 100644 index 00000000..5aa3c8a4 Binary files /dev/null and b/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png differ diff --git a/vendor/github.com/google/gopacket/examples/bytediff/main.go b/vendor/github.com/google/gopacket/examples/bytediff/main.go new file mode 100644 index 00000000..2a4c11b9 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/bytediff/main.go @@ -0,0 +1,96 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary shows how to display byte differences to users via the bytediff +// library. +package main + +import ( + "fmt" + "github.com/google/gopacket/bytediff" +) + +var sliceA = []byte{ + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, + 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06, + 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7, + 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18, + 0x00, 0x73, 0x9a, 0x8f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77, + 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, + 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, + 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61, + 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, + 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20, + 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, + 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, + 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54, + 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, + 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31, + 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20, + 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x2e, 0x31, + 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, + 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, + 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, + 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70, + 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, + 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55, + 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73, + 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, + 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30, + 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a, + 0x0d, 0x0a, +} +var sliceB = []byte{ + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, + 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06, + 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7, + 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18, + 0x00, 0x73, 0x9a, 0x8f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77, + 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, + 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, + 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, + 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20, + 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, + 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, + 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54, + 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, + 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31, + 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20, + 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, + 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, + 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, + 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, + 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70, + 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, + 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55, + 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73, + 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2e, 0x39, 0x55, 0x35, 0x39, + 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30, + 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a, + 0x0d, 0x0a, +} + +func main() { + fmt.Println(bytediff.BashOutput.String(bytediff.Diff(sliceA, sliceB))) +} diff --git a/vendor/github.com/google/gopacket/examples/httpassembly/main.go b/vendor/github.com/google/gopacket/examples/httpassembly/main.go new file mode 100644 index 00000000..02af21e0 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/httpassembly/main.go @@ -0,0 +1,127 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary provides sample code for using the gopacket TCP assembler and TCP +// stream reader. It reads packets off the wire and reconstructs HTTP requests +// it sees, logging them. +package main + +import ( + "bufio" + "flag" + "io" + "log" + "net/http" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/tcpassembly" + "github.com/google/gopacket/tcpassembly/tcpreader" +) + +var iface = flag.String("i", "eth0", "Interface to get packets from") +var fname = flag.String("r", "", "Filename to read from, overrides -i") +var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture") +var filter = flag.String("f", "tcp and dst port 80", "BPF filter for pcap") +var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail") + +// Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces + +// httpStreamFactory implements tcpassembly.StreamFactory +type httpStreamFactory struct{} + +// httpStream will handle the actual decoding of http requests. +type httpStream struct { + net, transport gopacket.Flow + r tcpreader.ReaderStream +} + +func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream { + hstream := &httpStream{ + net: net, + transport: transport, + r: tcpreader.NewReaderStream(), + } + go hstream.run() // Important... we must guarantee that data from the reader stream is read. + + // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it. + return &hstream.r +} + +func (h *httpStream) run() { + buf := bufio.NewReader(&h.r) + for { + req, err := http.ReadRequest(buf) + if err == io.EOF { + // We must read until we see an EOF... very important! + return + } else if err != nil { + log.Println("Error reading stream", h.net, h.transport, ":", err) + } else { + bodyBytes := tcpreader.DiscardBytesToEOF(req.Body) + req.Body.Close() + log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body") + } + } +} + +func main() { + defer util.Run()() + var handle *pcap.Handle + var err error + + // Set up pcap packet capture + if *fname != "" { + log.Printf("Reading from pcap dump %q", *fname) + handle, err = pcap.OpenOffline(*fname) + } else { + log.Printf("Starting capture on interface %q", *iface) + handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever) + } + if err != nil { + log.Fatal(err) + } + + if err := handle.SetBPFFilter(*filter); err != nil { + log.Fatal(err) + } + + // Set up assembly + streamFactory := &httpStreamFactory{} + streamPool := tcpassembly.NewStreamPool(streamFactory) + assembler := tcpassembly.NewAssembler(streamPool) + + log.Println("reading in packets") + // Read in packets, pass to assembler. + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + packets := packetSource.Packets() + ticker := time.Tick(time.Minute) + for { + select { + case packet := <-packets: + // A nil packet indicates the end of a pcap file. + if packet == nil { + return + } + if *logAllPackets { + log.Println(packet) + } + if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP { + log.Println("Unusable packet") + continue + } + tcp := packet.TransportLayer().(*layers.TCP) + assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp) + + case <-ticker: + // Every minute, flush connections that haven't seen activity in the past 2 minutes. + assembler.FlushOlderThan(time.Now().Add(time.Minute * -2)) + } + } +} diff --git a/vendor/github.com/google/gopacket/examples/pcapdump/main.go b/vendor/github.com/google/gopacket/examples/pcapdump/main.go new file mode 100644 index 00000000..373dee29 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/pcapdump/main.go @@ -0,0 +1,73 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pcapdump binary implements a tcpdump-like command line tool with gopacket +// using pcap as a backend data collection mechanism. +package main + +import ( + "flag" + "fmt" + "github.com/google/gopacket/dumpcommand" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/pcap" + "log" + "os" + "strings" + "time" +) + +var iface = flag.String("i", "eth0", "Interface to read packets from") +var fname = flag.String("r", "", "Filename to read from, overrides -i") +var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet") +var tstype = flag.String("timestamp_type", "", "Type of timestamps to use") +var promisc = flag.Bool("promisc", true, "Set promiscuous mode") + +func main() { + defer util.Run()() + var handle *pcap.Handle + var err error + if *fname != "" { + if handle, err = pcap.OpenOffline(*fname); err != nil { + log.Fatal("PCAP OpenOffline error:", err) + } + } else { + // This is a little complicated because we want to allow all possible options + // for creating the packet capture handle... instead of all this you can + // just call pcap.OpenLive if you want a simple handle. + inactive, err := pcap.NewInactiveHandle(*iface) + if err != nil { + log.Fatalf("could not create: %v", err) + } + defer inactive.CleanUp() + if err = inactive.SetSnapLen(*snaplen); err != nil { + log.Fatalf("could not set snap length: %v", err) + } else if err = inactive.SetPromisc(*promisc); err != nil { + log.Fatalf("could not set promisc mode: %v", err) + } else if err = inactive.SetTimeout(time.Second); err != nil { + log.Fatalf("could not set timeout: %v", err) + } + if *tstype != "" { + if t, err := pcap.TimestampSourceFromString(*tstype); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } else if err := inactive.SetTimestampSource(t); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } + } + if handle, err = inactive.Activate(); err != nil { + log.Fatal("PCAP Activate error:", err) + } + defer handle.Close() + } + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter) + if err = handle.SetBPFFilter(bpffilter); err != nil { + log.Fatal("BPF filter error:", err) + } + } + dumpcommand.Run(handle) +} diff --git a/vendor/github.com/google/gopacket/examples/pcaplay/main.go b/vendor/github.com/google/gopacket/examples/pcaplay/main.go new file mode 100644 index 00000000..d36d860d --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/pcaplay/main.go @@ -0,0 +1,163 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pcaplay binary load an offline capture (pcap file) and replay +// it on the select interface, with an emphasis on packet timing +package main + +import ( + "flag" + "fmt" + "io" + "log" + "os" + "strings" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/pcap" +) + +var iface = flag.String("i", "eth0", "Interface to write packets to") +var fname = flag.String("r", "", "Filename to read from") +var fast = flag.Bool("f", false, "Send each packets as fast as possible") + +var lastTS time.Time +var lastSend time.Time + +var start time.Time +var bytesSent int + +func writePacketDelayed(handle *pcap.Handle, buf []byte, ci gopacket.CaptureInfo) { + if ci.CaptureLength != ci.Length { + // do not write truncated packets + return + } + + intervalInCapture := ci.Timestamp.Sub(lastTS) + elapsedTime := time.Since(lastSend) + + if (intervalInCapture > elapsedTime) && !lastSend.IsZero() { + time.Sleep(intervalInCapture - elapsedTime) + } + + lastSend = time.Now() + writePacket(handle, buf) + lastTS = ci.Timestamp +} + +func writePacket(handle *pcap.Handle, buf []byte) error { + if err := handle.WritePacketData(buf); err != nil { + log.Printf("Failed to send packet: %s\n", err) + return err + } + return nil +} + +func pcapInfo(filename string) (start time.Time, end time.Time, packets int, size int) { + handleRead, err := pcap.OpenOffline(*fname) + if err != nil { + log.Fatal("PCAP OpenOffline error (handle to read packet):", err) + } + + var previousTs time.Time + var deltaTotal time.Duration + + for { + data, ci, err := handleRead.ReadPacketData() + if err != nil && err != io.EOF { + log.Fatal(err) + } else if err == io.EOF { + break + } else { + + if start.IsZero() { + start = ci.Timestamp + } + end = ci.Timestamp + packets++ + size += len(data) + + if previousTs.IsZero() { + previousTs = ci.Timestamp + } else { + deltaTotal += ci.Timestamp.Sub(previousTs) + previousTs = ci.Timestamp + } + } + } + sec := int(deltaTotal.Seconds()) + if sec == 0 { + sec = 1 + } + fmt.Printf("Avg packet rate %d/s\n", packets/sec) + return start, end, packets, size +} + +func main() { + defer util.Run()() + + // Sanity checks + if *fname == "" { + log.Fatal("Need a input file") + } + + // Open PCAP file + handle potential BPF Filter + handleRead, err := pcap.OpenOffline(*fname) + if err != nil { + log.Fatal("PCAP OpenOffline error (handle to read packet):", err) + } + defer handleRead.Close() + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter) + if err = handleRead.SetBPFFilter(bpffilter); err != nil { + log.Fatal("BPF filter error:", err) + } + } + // Open up a second pcap handle for packet writes. + handleWrite, err := pcap.OpenLive(*iface, 65536, true, pcap.BlockForever) + if err != nil { + log.Fatal("PCAP OpenLive error (handle to write packet):", err) + } + defer handleWrite.Close() + + start = time.Now() + pkt := 0 + tsStart, tsEnd, packets, size := pcapInfo(*fname) + + // Loop over packets and write them + for { + data, ci, err := handleRead.ReadPacketData() + switch { + case err == io.EOF: + fmt.Printf("\nFinished in %s", time.Since(start)) + return + case err != nil: + log.Printf("Failed to read packet %d: %s\n", pkt, err) + default: + if *fast { + writePacket(handleWrite, data) + } else { + writePacketDelayed(handleWrite, data, ci) + } + + bytesSent += len(data) + duration := time.Since(start) + pkt++ + + if duration > time.Second { + rate := bytesSent / int(duration.Seconds()) + remainingTime := tsEnd.Sub(tsStart) - duration + fmt.Printf("\rrate %d kB/sec - sent %d/%d kB - %d/%d packets - remaining time %s", + rate/1000, bytesSent/1000, size/1000, + pkt, packets, remainingTime) + } + } + } + +} diff --git a/vendor/github.com/google/gopacket/examples/pfdump/main.go b/vendor/github.com/google/gopacket/examples/pfdump/main.go new file mode 100644 index 00000000..4b3ace6d --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/pfdump/main.go @@ -0,0 +1,52 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pfdump binary implements a tcpdump-like command line tool with gopacket +// using pfring as a backend data collection mechanism. +package main + +import ( + "flag" + "fmt" + "github.com/google/gopacket/dumpcommand" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/pfring" + "log" + "os" + "strings" +) + +var iface = flag.String("i", "eth0", "Interface to read packets from") +var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet") +var cluster = flag.Int("cluster", -1, "If >= 0, sets the pfring cluster to this value") +var clustertype = flag.Int("clustertype", int(pfring.ClusterPerFlow), "Cluster type") + +func main() { + defer util.Run()() + var ring *pfring.Ring + var err error + if ring, err = pfring.NewRing(*iface, uint32(*snaplen), pfring.FlagPromisc); err != nil { + log.Fatalln("pfring ring creation error:", err) + } + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter) + if err = ring.SetBPFFilter(bpffilter); err != nil { + log.Fatalln("BPF filter error:", err) + } + } + if *cluster >= 0 { + if err = ring.SetCluster(*cluster, pfring.ClusterType(*clustertype)); err != nil { + log.Fatalln("pfring SetCluster error:", err) + } + } + if err = ring.SetSocketMode(pfring.ReadOnly); err != nil { + log.Fatalln("pfring SetSocketMode error:", err) + } else if err = ring.Enable(); err != nil { + log.Fatalln("pfring Enable error:", err) + } + dumpcommand.Run(ring) +} diff --git a/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh b/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh new file mode 100755 index 00000000..671d29f4 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# Limitations: if the number extracted files in too big, finding identical +# files might fail due to '*' in cmdline +# This would require to split sha256sum symlinks in xx/yyyyy + +usage() +{ + echo "Usage: $0 " + echo "Compares tcpreassembly against tcpflow" + echo "" + echo "$@" + exit 1 +} + +debug() { + return # comment me for debug + echo "$@" +} + +die() +{ + ( + echo "$@" + echo + ) >&2 + exit 1 +} + +rename() +{ + local path="$1" + local filter="$2" + find "$path" -type f -name "$filter" -print0 | + while IFS= read -r -d $'\0' f; do + local sha256="$(sha256sum "$f" | cut -d ' ' -f 1)" + local target="$(dirname $f)/../sha256/$sha256" + debug "$target → $f" + mkdir -p "$(dirname "$target")" || return 1 + if [ ! -f "$target" ]; then + ln -sr "$f" "$target" || return 1 + fi + done + return $? +} + +main() +{ + local src="$1" + local out="$2" + + # TODO: make options + local extra="" + extra="$extra -debug" + extra="$extra -cpuprofile "$out/gopacket/cpu.prof"" + extra="$extra -memprofile "$out/gopacket/mem.prof"" + + [ ! -f "$src" ] && usage "Missing pcap" + [ ! -d "$out" ] && ( mkdir "$out" || die "Failed to create $out" ) + + mkdir -p "$out/gopacket/all" || die "Failed to create $out/gopacket/all" + mkdir -p "$out/tcpflow/all" || die "Faield to create $out/tcpflow/all" + + echo " * Running go reassembly" + time ./reassemblydump -r "$src" $debug -output "$out/gopacket/all" $extra -writeincomplete -ignorefsmerr -nooptcheck -allowmissinginit port 80 &> "$out/gopacket.txt" || die "Failed to run reassmbly. Check $out/gopacket.txt" + echo " * Running tcpflow" + time tcpflow -e http -r "$src" -o "$out/tcpflow/all" port 80 &> "$out/tcpflow.txt" || die "Failed to run tcpflow. Check $out/tcpflow.txt" + + echo " * Creating sha256sum symlinks for gopacket" + rename "$out/gopacket/all" '*' || die "Failed to rename in $out/gopacket" + echo " * Creating sha256sum symlinks for tcpflow" + rename "$out/tcpflow/all" '*HTTPBODY*' || die "Failed to rename in $out/tcpflow" + + # Remove identical files + echo " * Finding identical files" + local nb=0 + mkdir -p "$out/gopacket/sha256-equal" + mkdir -p "$out/tcpflow/sha256-equal" + for f in "$out/gopacket/sha256/"*; do + local f="$(basename "$f")" + [ -f "$out/tcpflow/sha256/$f" ] && { + debug " $f" + mv "$out/gopacket/sha256/$f" "$out/gopacket/sha256-equal" + mv "$out/tcpflow/sha256/$f" "$out/tcpflow/sha256-equal" + nb=$((nb+1)) + } + done + echo " → found $nb files" + + echo " * Diffing {gopacket,tcpflow}/sha256" + local rc=0 + for p in "gopacket" "tcpflow"; do + local nb=$(ls -1 "$out/$p/sha256/" | wc -l) + if [ $nb -ne 0 ]; then + rc=$((rc+1)) + echo " → $nb files in $out/$p/sha256" + fi + done + return $rc +} + +main "$@" +exit $? diff --git a/vendor/github.com/google/gopacket/examples/reassemblydump/main.go b/vendor/github.com/google/gopacket/examples/reassemblydump/main.go new file mode 100644 index 00000000..9fc37915 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/reassemblydump/main.go @@ -0,0 +1,650 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pcapdump binary implements a tcpdump-like command line tool with gopacket +// using pcap as a backend data collection mechanism. +package main + +import ( + "bufio" + "bytes" + "compress/gzip" + "encoding/binary" + "encoding/hex" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/url" + "os" + "os/signal" + "path" + "runtime/pprof" + "strings" + "sync" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/ip4defrag" + "github.com/google/gopacket/layers" // pulls in all layers decoders + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/reassembly" +) + +var maxcount = flag.Int("c", -1, "Only grab this many packets, then exit") +var decoder = flag.String("decoder", "", "Name of the decoder to use (default: guess from capture)") +var statsevery = flag.Int("stats", 1000, "Output statistics every N packets") +var lazy = flag.Bool("lazy", false, "If true, do lazy decoding") +var nodefrag = flag.Bool("nodefrag", false, "If true, do not do IPv4 defrag") +var checksum = flag.Bool("checksum", false, "Check TCP checksum") +var nooptcheck = flag.Bool("nooptcheck", false, "Do not check TCP options (useful to ignore MSS on captures with TSO)") +var ignorefsmerr = flag.Bool("ignorefsmerr", false, "Ignore TCP FSM errors") +var allowmissinginit = flag.Bool("allowmissinginit", false, "Support streams without SYN/SYN+ACK/ACK sequence") +var verbose = flag.Bool("verbose", false, "Be verbose") +var debug = flag.Bool("debug", false, "Display debug information") +var quiet = flag.Bool("quiet", false, "Be quiet regarding errors") + +// http +var nohttp = flag.Bool("nohttp", false, "Disable HTTP parsing") +var output = flag.String("output", "", "Path to create file for HTTP 200 OK responses") +var writeincomplete = flag.Bool("writeincomplete", false, "Write incomplete response") + +var hexdump = flag.Bool("dump", false, "Dump HTTP request/response as hex") +var hexdumppkt = flag.Bool("dumppkt", false, "Dump packet as hex") + +// capture +var iface = flag.String("i", "eth0", "Interface to read packets from") +var fname = flag.String("r", "", "Filename to read from, overrides -i") +var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet") +var tstype = flag.String("timestamp_type", "", "Type of timestamps to use") +var promisc = flag.Bool("promisc", true, "Set promiscuous mode") + +var memprofile = flag.String("memprofile", "", "Write memory profile") + +var stats struct { + ipdefrag int + missedBytes int + pkt int + sz int + totalsz int + rejectFsm int + rejectOpt int + rejectConnFsm int + reassembled int + outOfOrderBytes int + outOfOrderPackets int + biggestChunkBytes int + biggestChunkPackets int + overlapBytes int + overlapPackets int +} + +const closeTimeout time.Duration = time.Hour * 24 // Closing inactive: TODO: from CLI +const timeout time.Duration = time.Minute * 5 // Pending bytes: TODO: from CLI + +/* + * HTTP part + */ + +type httpReader struct { + ident string + isClient bool + bytes chan []byte + data []byte + hexdump bool + parent *tcpStream +} + +func (h *httpReader) Read(p []byte) (int, error) { + ok := true + for ok && len(h.data) == 0 { + h.data, ok = <-h.bytes + } + if !ok || len(h.data) == 0 { + return 0, io.EOF + } + + l := copy(p, h.data) + h.data = h.data[l:] + return l, nil +} + +var outputLevel int +var errorsMap map[string]uint +var errors uint + +// Too bad for perf that a... is evaluated +func Error(t string, s string, a ...interface{}) { + errors++ + nb, _ := errorsMap[t] + errorsMap[t] = nb + 1 + if outputLevel >= 0 { + fmt.Printf(s, a...) + } +} +func Info(s string, a ...interface{}) { + if outputLevel >= 1 { + fmt.Printf(s, a...) + } +} +func Debug(s string, a ...interface{}) { + if outputLevel >= 2 { + fmt.Printf(s, a...) + } +} + +func (h *httpReader) run(wg *sync.WaitGroup) { + defer wg.Done() + b := bufio.NewReader(h) + for true { + if h.isClient { + req, err := http.ReadRequest(b) + if err == io.EOF || err == io.ErrUnexpectedEOF { + break + } else if err != nil { + Error("HTTP-request", "HTTP/%s Request error: %s (%v,%+v)\n", h.ident, err, err, err) + continue + } + body, err := ioutil.ReadAll(req.Body) + s := len(body) + if err != nil { + Error("HTTP-request-body", "Got body err: %s\n", err) + } else if h.hexdump { + Info("Body(%d/0x%x)\n%s\n", len(body), len(body), hex.Dump(body)) + } + req.Body.Close() + Info("HTTP/%s Request: %s %s (body:%d)\n", h.ident, req.Method, req.URL, s) + h.parent.urls = append(h.parent.urls, req.URL.String()) + } else { + res, err := http.ReadResponse(b, nil) + var req string + if len(h.parent.urls) == 0 { + req = fmt.Sprintf("") + } else { + req, h.parent.urls = h.parent.urls[0], h.parent.urls[1:] + } + if err == io.EOF || err == io.ErrUnexpectedEOF { + break + } else if err != nil { + Error("HTTP-response", "HTTP/%s Response error: %s (%v,%+v)\n", h.ident, err, err, err) + continue + } + body, err := ioutil.ReadAll(res.Body) + s := len(body) + if err != nil { + Error("HTTP-response-body", "HTTP/%s: failed to get body(parsed len:%d): %s\n", h.ident, s, err) + } + if h.hexdump { + Info("Body(%d/0x%x)\n%s\n", len(body), len(body), hex.Dump(body)) + } + res.Body.Close() + sym := "," + if res.ContentLength > 0 && res.ContentLength != int64(s) { + sym = "!=" + } + contentType, ok := res.Header["Content-Type"] + if !ok { + contentType = []string{http.DetectContentType(body)} + } + encoding := res.Header["Content-Encoding"] + Info("HTTP/%s Response: %s URL:%s (%d%s%d%s) -> %s\n", h.ident, res.Status, req, res.ContentLength, sym, s, contentType, encoding) + if (err == nil || *writeincomplete) && *output != "" { + base := url.QueryEscape(path.Base(req)) + if err != nil { + base = "incomplete-" + base + } + base = path.Join(*output, base) + if len(base) > 250 { + base = base[:250] + "..." + } + if base == *output { + base = path.Join(*output, "noname") + } + target := base + n := 0 + for true { + _, err := os.Stat(target) + //if os.IsNotExist(err) != nil { + if err != nil { + break + } + target = fmt.Sprintf("%s-%d", base, n) + n++ + } + f, err := os.Create(target) + if err != nil { + Error("HTTP-create", "Cannot create %s: %s\n", target, err) + continue + } + var r io.Reader + r = bytes.NewBuffer(body) + if len(encoding) > 0 && (encoding[0] == "gzip" || encoding[0] == "deflate") { + r, err = gzip.NewReader(r) + if err != nil { + Error("HTTP-gunzip", "Failed to gzip decode: %s", err) + } + } + if err == nil { + w, err := io.Copy(f, r) + if _, ok := r.(*gzip.Reader); ok { + r.(*gzip.Reader).Close() + } + f.Close() + if err != nil { + Error("HTTP-save", "%s: failed to save %s (l:%d): %s\n", h.ident, target, w, err) + } else { + Info("%s: Saved %s (l:%d)\n", h.ident, target, w) + } + } + } + } + } +} + +/* + * The TCP factory: returns a new Stream + */ +type tcpStreamFactory struct { + wg sync.WaitGroup + doHTTP bool +} + +func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.TCP, ac reassembly.AssemblerContext) reassembly.Stream { + Debug("* NEW: %s %s\n", net, transport) + fsmOptions := reassembly.TCPSimpleFSMOptions{ + SupportMissingEstablishment: *allowmissinginit, + } + stream := &tcpStream{ + net: net, + transport: transport, + isDNS: tcp.SrcPort == 53 || tcp.DstPort == 53, + isHTTP: (tcp.SrcPort == 80 || tcp.DstPort == 80) && factory.doHTTP, + reversed: tcp.SrcPort == 80, + tcpstate: reassembly.NewTCPSimpleFSM(fsmOptions), + ident: fmt.Sprintf("%s:%s", net, transport), + optchecker: reassembly.NewTCPOptionCheck(), + } + if stream.isHTTP { + stream.client = httpReader{ + bytes: make(chan []byte), + ident: fmt.Sprintf("%s %s", net, transport), + hexdump: *hexdump, + parent: stream, + isClient: true, + } + stream.server = httpReader{ + bytes: make(chan []byte), + ident: fmt.Sprintf("%s %s", net.Reverse(), transport.Reverse()), + hexdump: *hexdump, + parent: stream, + } + factory.wg.Add(2) + go stream.client.run(&factory.wg) + go stream.server.run(&factory.wg) + } + return stream +} + +func (factory *tcpStreamFactory) WaitGoRoutines() { + factory.wg.Wait() +} + +/* + * The assembler context + */ +type Context struct { + CaptureInfo gopacket.CaptureInfo +} + +func (c *Context) GetCaptureInfo() gopacket.CaptureInfo { + return c.CaptureInfo +} + +/* + * TCP stream + */ + +/* It's a connection (bidirectional) */ +type tcpStream struct { + tcpstate *reassembly.TCPSimpleFSM + fsmerr bool + optchecker reassembly.TCPOptionCheck + net, transport gopacket.Flow + isDNS bool + isHTTP bool + reversed bool + client httpReader + server httpReader + urls []string + ident string +} + +func (t *tcpStream) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassembly.TCPFlowDirection, acked reassembly.Sequence, start *bool, ac reassembly.AssemblerContext) bool { + // FSM + if !t.tcpstate.CheckState(tcp, dir) { + Error("FSM", "%s: Packet rejected by FSM (state:%s)\n", t.ident, t.tcpstate.String()) + stats.rejectFsm++ + if !t.fsmerr { + t.fsmerr = true + stats.rejectConnFsm++ + } + if !*ignorefsmerr { + return false + } + } + // Options + err := t.optchecker.Accept(tcp, ci, dir, acked, start) + if err != nil { + Error("OptionChecker", "%s: Packet rejected by OptionChecker: %s\n", t.ident, err) + stats.rejectOpt++ + if !*nooptcheck { + return false + } + } + // Checksum + accept := true + if *checksum { + c, err := tcp.ComputeChecksum() + if err != nil { + Error("ChecksumCompute", "%s: Got error computing checksum: %s\n", t.ident, err) + accept = false + } else if c != 0x0 { + Error("Checksum", "%s: Invalid checksum: 0x%x\n", t.ident, c) + accept = false + } + } + if !accept { + stats.rejectOpt++ + } + return accept +} + +func (t *tcpStream) ReassembledSG(sg reassembly.ScatterGather, ac reassembly.AssemblerContext) { + dir, start, end, skip := sg.Info() + length, saved := sg.Lengths() + // update stats + sgStats := sg.Stats() + if skip > 0 { + stats.missedBytes += skip + } + stats.sz += length - saved + stats.pkt += sgStats.Packets + if sgStats.Chunks > 1 { + stats.reassembled++ + } + stats.outOfOrderPackets += sgStats.QueuedPackets + stats.outOfOrderBytes += sgStats.QueuedBytes + if length > stats.biggestChunkBytes { + stats.biggestChunkBytes = length + } + if sgStats.Packets > stats.biggestChunkPackets { + stats.biggestChunkPackets = sgStats.Packets + } + if sgStats.OverlapBytes != 0 && sgStats.OverlapPackets == 0 { + fmt.Printf("bytes:%d, pkts:%d\n", sgStats.OverlapBytes, sgStats.OverlapPackets) + panic("Invalid overlap") + } + stats.overlapBytes += sgStats.OverlapBytes + stats.overlapPackets += sgStats.OverlapPackets + + var ident string + if dir == reassembly.TCPDirClientToServer { + ident = fmt.Sprintf("%v %v(%s): ", t.net, t.transport, dir) + } else { + ident = fmt.Sprintf("%v %v(%s): ", t.net.Reverse(), t.transport.Reverse(), dir) + } + Debug("%s: SG reassembled packet with %d bytes (start:%v,end:%v,skip:%d,saved:%d,nb:%d,%d,overlap:%d,%d)\n", ident, length, start, end, skip, saved, sgStats.Packets, sgStats.Chunks, sgStats.OverlapBytes, sgStats.OverlapPackets) + if skip == -1 && *allowmissinginit { + // this is allowed + } else if skip != 0 { + // Missing bytes in stream: do not even try to parse it + return + } + data := sg.Fetch(length) + if t.isDNS { + dns := &layers.DNS{} + var decoded []gopacket.LayerType + if len(data) < 2 { + if len(data) > 0 { + sg.KeepFrom(0) + } + return + } + dnsSize := binary.BigEndian.Uint16(data[:2]) + missing := int(dnsSize) - len(data[2:]) + Debug("dnsSize: %d, missing: %d\n", dnsSize, missing) + if missing > 0 { + Info("Missing some bytes: %d\n", missing) + sg.KeepFrom(0) + return + } + p := gopacket.NewDecodingLayerParser(layers.LayerTypeDNS, dns) + err := p.DecodeLayers(data[2:], &decoded) + if err != nil { + Error("DNS-parser", "Failed to decode DNS: %v\n", err) + } else { + Debug("DNS: %s\n", gopacket.LayerDump(dns)) + } + if len(data) > 2+int(dnsSize) { + sg.KeepFrom(2 + int(dnsSize)) + } + } else if t.isHTTP { + if length > 0 { + if *hexdump { + Debug("Feeding http with:\n%s", hex.Dump(data)) + } + if dir == reassembly.TCPDirClientToServer && !t.reversed { + t.client.bytes <- data + } else { + t.server.bytes <- data + } + } + } +} + +func (t *tcpStream) ReassemblyComplete(ac reassembly.AssemblerContext) bool { + Debug("%s: Connection closed\n", t.ident) + if t.isHTTP { + close(t.client.bytes) + close(t.server.bytes) + } + // do not remove the connection to allow last ACK + return false +} + +func main() { + defer util.Run()() + var handle *pcap.Handle + var err error + if *debug { + outputLevel = 2 + } else if *verbose { + outputLevel = 1 + } else if *quiet { + outputLevel = -1 + } + errorsMap = make(map[string]uint) + if *fname != "" { + if handle, err = pcap.OpenOffline(*fname); err != nil { + log.Fatal("PCAP OpenOffline error:", err) + } + } else { + // This is a little complicated because we want to allow all possible options + // for creating the packet capture handle... instead of all this you can + // just call pcap.OpenLive if you want a simple handle. + inactive, err := pcap.NewInactiveHandle(*iface) + if err != nil { + log.Fatal("could not create: %v", err) + } + defer inactive.CleanUp() + if err = inactive.SetSnapLen(*snaplen); err != nil { + log.Fatal("could not set snap length: %v", err) + } else if err = inactive.SetPromisc(*promisc); err != nil { + log.Fatal("could not set promisc mode: %v", err) + } else if err = inactive.SetTimeout(time.Second); err != nil { + log.Fatal("could not set timeout: %v", err) + } + if *tstype != "" { + if t, err := pcap.TimestampSourceFromString(*tstype); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } else if err := inactive.SetTimestampSource(t); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } + } + if handle, err = inactive.Activate(); err != nil { + log.Fatal("PCAP Activate error:", err) + } + defer handle.Close() + } + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + Info("Using BPF filter %q\n", bpffilter) + if err = handle.SetBPFFilter(bpffilter); err != nil { + log.Fatal("BPF filter error:", err) + } + } + + var dec gopacket.Decoder + var ok bool + decoder_name := *decoder + if decoder_name == "" { + decoder_name = fmt.Sprintf("%s", handle.LinkType()) + } + if dec, ok = gopacket.DecodersByLayerName[decoder_name]; !ok { + log.Fatalln("No decoder named", decoder_name) + } + source := gopacket.NewPacketSource(handle, dec) + source.Lazy = *lazy + source.NoCopy = true + Info("Starting to read packets\n") + count := 0 + bytes := int64(0) + start := time.Now() + defragger := ip4defrag.NewIPv4Defragmenter() + + streamFactory := &tcpStreamFactory{doHTTP: !*nohttp} + streamPool := reassembly.NewStreamPool(streamFactory) + assembler := reassembly.NewAssembler(streamPool) + + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt) + + for packet := range source.Packets() { + count++ + Debug("PACKET #%d\n", count) + data := packet.Data() + bytes += int64(len(data)) + if *hexdumppkt { + Debug("Packet content (%d/0x%x)\n%s\n", len(data), len(data), hex.Dump(data)) + } + + // defrag the IPv4 packet if required + if !*nodefrag { + ip4Layer := packet.Layer(layers.LayerTypeIPv4) + if ip4Layer == nil { + continue + } + ip4 := ip4Layer.(*layers.IPv4) + l := ip4.Length + newip4, err := defragger.DefragIPv4(ip4) + if err != nil { + log.Fatalln("Error while de-fragmenting", err) + } else if newip4 == nil { + Debug("Fragment...\n") + continue // packet fragment, we don't have whole packet yet. + } + if newip4.Length != l { + stats.ipdefrag++ + Debug("Decoding re-assembled packet: %s\n", newip4.NextLayerType()) + pb, ok := packet.(gopacket.PacketBuilder) + if !ok { + panic("Not a PacketBuilder") + } + nextDecoder := newip4.NextLayerType() + nextDecoder.Decode(newip4.Payload, pb) + } + } + + tcp := packet.Layer(layers.LayerTypeTCP) + if tcp != nil { + tcp := tcp.(*layers.TCP) + if *checksum { + err := tcp.SetNetworkLayerForChecksum(packet.NetworkLayer()) + if err != nil { + log.Fatalf("Failed to set network layer for checksum: %s\n", err) + } + } + c := Context{ + CaptureInfo: packet.Metadata().CaptureInfo, + } + stats.totalsz += len(tcp.Payload) + assembler.AssembleWithContext(packet.NetworkLayer().NetworkFlow(), tcp, &c) + } + if count%*statsevery == 0 { + ref := packet.Metadata().CaptureInfo.Timestamp + flushed, closed := assembler.FlushWithOptions(reassembly.FlushOptions{T: ref.Add(-timeout), TC: ref.Add(-closeTimeout)}) + Debug("Forced flush: %d flushed, %d closed (%s)", flushed, closed, ref) + } + + done := *maxcount > 0 && count >= *maxcount + if count%*statsevery == 0 || done { + fmt.Fprintf(os.Stderr, "Processed %v packets (%v bytes) in %v (errors: %v, type:%v)\n", count, bytes, time.Since(start), errors, len(errorsMap)) + } + select { + case <-signalChan: + fmt.Fprintf(os.Stderr, "\nCaught SIGINT: aborting\n") + done = true + default: + // NOP: continue + } + if done { + break + } + } + + closed := assembler.FlushAll() + Debug("Final flush: %d closed", closed) + if outputLevel >= 2 { + streamPool.Dump() + } + + if *memprofile != "" { + f, err := os.Create(*memprofile) + if err != nil { + log.Fatal(err) + } + pprof.WriteHeapProfile(f) + f.Close() + } + + streamFactory.WaitGoRoutines() + Debug("%s\n", assembler.Dump()) + if !*nodefrag { + fmt.Printf("IPdefrag:\t\t%d\n", stats.ipdefrag) + } + fmt.Printf("TCP stats:\n") + fmt.Printf(" missed bytes:\t\t%d\n", stats.missedBytes) + fmt.Printf(" total packets:\t\t%d\n", stats.pkt) + fmt.Printf(" rejected FSM:\t\t%d\n", stats.rejectFsm) + fmt.Printf(" rejected Options:\t%d\n", stats.rejectOpt) + fmt.Printf(" reassembled bytes:\t%d\n", stats.sz) + fmt.Printf(" total TCP bytes:\t%d\n", stats.totalsz) + fmt.Printf(" conn rejected FSM:\t%d\n", stats.rejectConnFsm) + fmt.Printf(" reassembled chunks:\t%d\n", stats.reassembled) + fmt.Printf(" out-of-order packets:\t%d\n", stats.outOfOrderPackets) + fmt.Printf(" out-of-order bytes:\t%d\n", stats.outOfOrderBytes) + fmt.Printf(" biggest-chunk packets:\t%d\n", stats.biggestChunkPackets) + fmt.Printf(" biggest-chunk bytes:\t%d\n", stats.biggestChunkBytes) + fmt.Printf(" overlap packets:\t%d\n", stats.overlapPackets) + fmt.Printf(" overlap bytes:\t\t%d\n", stats.overlapBytes) + fmt.Printf("Errors: %d\n", errors) + for e, _ := range errorsMap { + fmt.Printf(" %s:\t\t%d\n", e, errorsMap[e]) + } +} diff --git a/vendor/github.com/google/gopacket/examples/statsassembly/main.go b/vendor/github.com/google/gopacket/examples/statsassembly/main.go new file mode 100644 index 00000000..36da0117 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/statsassembly/main.go @@ -0,0 +1,211 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary provides sample code for using the gopacket TCP assembler raw, +// without the help of the tcpreader library. It watches TCP streams and +// reports statistics on completed streams. +// +// It also uses gopacket.DecodingLayerParser instead of the normal +// gopacket.PacketSource, to highlight the methods, pros, and cons of this +// approach. +package main + +import ( + "flag" + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/tcpassembly" + "log" + "time" +) + +var iface = flag.String("i", "eth0", "Interface to get packets from") +var snaplen = flag.Int("s", 65536, "SnapLen for pcap packet capture") +var filter = flag.String("f", "tcp", "BPF filter for pcap") +var logAllPackets = flag.Bool("v", false, "Log whenever we see a packet") +var bufferedPerConnection = flag.Int("connection_max_buffer", 0, ` +Max packets to buffer for a single connection before skipping over a gap in data +and continuing to stream the connection after the buffer. If zero or less, this +is infinite.`) +var bufferedTotal = flag.Int("total_max_buffer", 0, ` +Max packets to buffer total before skipping over gaps in connections and +continuing to stream connection data. If zero or less, this is infinite`) +var flushAfter = flag.String("flush_after", "2m", ` +Connections which have buffered packets (they've gotten packets out of order and +are waiting for old packets to fill the gaps) are flushed after they're this old +(their oldest gap is skipped). Any string parsed by time.ParseDuration is +acceptable here`) +var packetCount = flag.Int("c", -1, ` +Quit after processing this many packets, flushing all currently buffered +connections. If negative, this is infinite`) + +// simpleStreamFactory implements tcpassembly.StreamFactory +type statsStreamFactory struct{} + +// statsStream will handle the actual decoding of stats requests. +type statsStream struct { + net, transport gopacket.Flow + bytes, packets, outOfOrder, skipped int64 + start, end time.Time + sawStart, sawEnd bool +} + +// New creates a new stream. It's called whenever the assembler sees a stream +// it isn't currently following. +func (factory *statsStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream { + log.Printf("new stream %v:%v started", net, transport) + s := &statsStream{ + net: net, + transport: transport, + start: time.Now(), + } + s.end = s.start + // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it. + return s +} + +// Reassembled is called whenever new packet data is available for reading. +// Reassembly objects contain stream data IN ORDER. +func (s *statsStream) Reassembled(reassemblies []tcpassembly.Reassembly) { + for _, reassembly := range reassemblies { + if reassembly.Seen.Before(s.end) { + s.outOfOrder++ + } else { + s.end = reassembly.Seen + } + s.bytes += int64(len(reassembly.Bytes)) + s.packets += 1 + if reassembly.Skip > 0 { + s.skipped += int64(reassembly.Skip) + } + s.sawStart = s.sawStart || reassembly.Start + s.sawEnd = s.sawEnd || reassembly.End + } +} + +// ReassemblyComplete is called when the TCP assembler believes a stream has +// finished. +func (s *statsStream) ReassemblyComplete() { + diffSecs := float64(s.end.Sub(s.start)) / float64(time.Second) + log.Printf("Reassembly of stream %v:%v complete - start:%v end:%v bytes:%v packets:%v ooo:%v bps:%v pps:%v skipped:%v", + s.net, s.transport, s.start, s.end, s.bytes, s.packets, s.outOfOrder, + float64(s.bytes)/diffSecs, float64(s.packets)/diffSecs, s.skipped) +} + +func main() { + defer util.Run()() + + flushDuration, err := time.ParseDuration(*flushAfter) + if err != nil { + log.Fatal("invalid flush duration: ", *flushAfter) + } + + log.Printf("starting capture on interface %q", *iface) + // Set up pcap packet capture + handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, flushDuration/2) + if err != nil { + log.Fatal("error opening pcap handle: ", err) + } + if err := handle.SetBPFFilter(*filter); err != nil { + log.Fatal("error setting BPF filter: ", err) + } + + // Set up assembly + streamFactory := &statsStreamFactory{} + streamPool := tcpassembly.NewStreamPool(streamFactory) + assembler := tcpassembly.NewAssembler(streamPool) + assembler.MaxBufferedPagesPerConnection = *bufferedPerConnection + assembler.MaxBufferedPagesTotal = *bufferedTotal + + log.Println("reading in packets") + + // We use a DecodingLayerParser here instead of a simpler PacketSource. + // This approach should be measurably faster, but is also more rigid. + // PacketSource will handle any known type of packet safely and easily, + // but DecodingLayerParser will only handle those packet types we + // specifically pass in. This trade-off can be quite useful, though, in + // high-throughput situations. + var eth layers.Ethernet + var dot1q layers.Dot1Q + var ip4 layers.IPv4 + var ip6 layers.IPv6 + var ip6extensions layers.IPv6ExtensionSkipper + var tcp layers.TCP + var payload gopacket.Payload + parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, + ð, &dot1q, &ip4, &ip6, &ip6extensions, &tcp, &payload) + decoded := make([]gopacket.LayerType, 0, 4) + + nextFlush := time.Now().Add(flushDuration / 2) + + var byteCount int64 + start := time.Now() + +loop: + for ; *packetCount != 0; *packetCount-- { + // Check to see if we should flush the streams we have + // that haven't seen any new data in a while. Note we set a + // timeout on our PCAP handle, so this should happen even if we + // never see packet data. + if time.Now().After(nextFlush) { + stats, _ := handle.Stats() + log.Printf("flushing all streams that haven't seen packets in the last 2 minutes, pcap stats: %+v", stats) + assembler.FlushOlderThan(time.Now().Add(flushDuration)) + nextFlush = time.Now().Add(flushDuration / 2) + } + + // To speed things up, we're also using the ZeroCopy method for + // reading packet data. This method is faster than the normal + // ReadPacketData, but the returned bytes in 'data' are + // invalidated by any subsequent ZeroCopyReadPacketData call. + // Note that tcpassembly is entirely compatible with this packet + // reading method. This is another trade-off which might be + // appropriate for high-throughput sniffing: it avoids a packet + // copy, but its cost is much more careful handling of the + // resulting byte slice. + data, ci, err := handle.ZeroCopyReadPacketData() + + if err != nil { + log.Printf("error getting packet: %v", err) + continue + } + err = parser.DecodeLayers(data, &decoded) + if err != nil { + log.Printf("error decoding packet: %v", err) + continue + } + if *logAllPackets { + log.Printf("decoded the following layers: %v", decoded) + } + byteCount += int64(len(data)) + // Find either the IPv4 or IPv6 address to use as our network + // layer. + foundNetLayer := false + var netFlow gopacket.Flow + for _, typ := range decoded { + switch typ { + case layers.LayerTypeIPv4: + netFlow = ip4.NetworkFlow() + foundNetLayer = true + case layers.LayerTypeIPv6: + netFlow = ip6.NetworkFlow() + foundNetLayer = true + case layers.LayerTypeTCP: + if foundNetLayer { + assembler.AssembleWithTimestamp(netFlow, &tcp, ci.Timestamp) + } else { + log.Println("could not find IPv4 or IPv6 layer, inoring") + } + continue loop + } + } + log.Println("could not find TCP layer") + } + assembler.FlushAll() + log.Printf("processed %d bytes in %v", byteCount, time.Since(start)) +} diff --git a/vendor/github.com/google/gopacket/examples/synscan/main.go b/vendor/github.com/google/gopacket/examples/synscan/main.go new file mode 100644 index 00000000..7a2345f8 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/synscan/main.go @@ -0,0 +1,259 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// synscan implements a TCP syn scanner on top of pcap. +// It's more complicated than arpscan, since it has to handle sending packets +// outside the local network, requiring some routing and ARP work. +// +// Since this is just an example program, it aims for simplicity over +// performance. It doesn't handle sending packets very quickly, it scans IPs +// serially instead of in parallel, and uses gopacket.Packet instead of +// gopacket.DecodingLayerParser for packet processing. We also make use of very +// simple timeout logic with time.Since. +// +// Making it blazingly fast is left as an exercise to the reader. +package main + +import ( + "errors" + "flag" + "log" + "net" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/routing" +) + +// scanner handles scanning a single IP address. +type scanner struct { + // iface is the interface to send packets on. + iface *net.Interface + // destination, gateway (if applicable), and source IP addresses to use. + dst, gw, src net.IP + + handle *pcap.Handle + + // opts and buf allow us to easily serialize packets in the send() + // method. + opts gopacket.SerializeOptions + buf gopacket.SerializeBuffer +} + +// newScanner creates a new scanner for a given destination IP address, using +// router to determine how to route packets to that IP. +func newScanner(ip net.IP, router routing.Router) (*scanner, error) { + s := &scanner{ + dst: ip, + opts: gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + }, + buf: gopacket.NewSerializeBuffer(), + } + // Figure out the route to the IP. + iface, gw, src, err := router.Route(ip) + if err != nil { + return nil, err + } + log.Printf("scanning ip %v with interface %v, gateway %v, src %v", ip, iface.Name, gw, src) + s.gw, s.src, s.iface = gw, src, iface + + // Open the handle for reading/writing. + // Note we could very easily add some BPF filtering here to greatly + // decrease the number of packets we have to look at when getting back + // scan results. + handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever) + if err != nil { + return nil, err + } + s.handle = handle + return s, nil +} + +// close cleans up the handle. +func (s *scanner) close() { + s.handle.Close() +} + +// getHwAddr is a hacky but effective way to get the destination hardware +// address for our packets. It does an ARP request for our gateway (if there is +// one) or destination IP (if no gateway is necessary), then waits for an ARP +// reply. This is pretty slow right now, since it blocks on the ARP +// request/reply. +func (s *scanner) getHwAddr() (net.HardwareAddr, error) { + start := time.Now() + arpDst := s.dst + if s.gw != nil { + arpDst = s.gw + } + // Prepare the layers to send for an ARP request. + eth := layers.Ethernet{ + SrcMAC: s.iface.HardwareAddr, + DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + EthernetType: layers.EthernetTypeARP, + } + arp := layers.ARP{ + AddrType: layers.LinkTypeEthernet, + Protocol: layers.EthernetTypeIPv4, + HwAddressSize: 6, + ProtAddressSize: 4, + Operation: layers.ARPRequest, + SourceHwAddress: []byte(s.iface.HardwareAddr), + SourceProtAddress: []byte(s.src), + DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, + DstProtAddress: []byte(arpDst), + } + // Send a single ARP request packet (we never retry a send, since this + // is just an example ;) + if err := s.send(ð, &arp); err != nil { + return nil, err + } + // Wait 3 seconds for an ARP reply. + for { + if time.Since(start) > time.Second*3 { + return nil, errors.New("timeout getting ARP reply") + } + data, _, err := s.handle.ReadPacketData() + if err == pcap.NextErrorTimeoutExpired { + continue + } else if err != nil { + return nil, err + } + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) + if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil { + arp := arpLayer.(*layers.ARP) + if net.IP(arp.SourceProtAddress).Equal(net.IP(arpDst)) { + return net.HardwareAddr(arp.SourceHwAddress), nil + } + } + } +} + +// scan scans the dst IP address of this scanner. +func (s *scanner) scan() error { + // First off, get the MAC address we should be sending packets to. + hwaddr, err := s.getHwAddr() + if err != nil { + return err + } + // Construct all the network layers we need. + eth := layers.Ethernet{ + SrcMAC: s.iface.HardwareAddr, + DstMAC: hwaddr, + EthernetType: layers.EthernetTypeIPv4, + } + ip4 := layers.IPv4{ + SrcIP: s.src, + DstIP: s.dst, + Version: 4, + TTL: 64, + Protocol: layers.IPProtocolTCP, + } + tcp := layers.TCP{ + SrcPort: 54321, + DstPort: 0, // will be incremented during the scan + SYN: true, + } + tcp.SetNetworkLayerForChecksum(&ip4) + + // Create the flow we expect returning packets to have, so we can check + // against it and discard useless packets. + ipFlow := gopacket.NewFlow(layers.EndpointIPv4, s.dst, s.src) + start := time.Now() + for { + // Send one packet per loop iteration until we've sent packets + // to all of ports [1, 65535]. + if tcp.DstPort < 65535 { + start = time.Now() + tcp.DstPort++ + if err := s.send(ð, &ip4, &tcp); err != nil { + log.Printf("error sending to port %v: %v", tcp.DstPort, err) + } + } + // Time out 5 seconds after the last packet we sent. + if time.Since(start) > time.Second*5 { + log.Printf("timed out for %v, assuming we've seen all we can", s.dst) + return nil + } + + // Read in the next packet. + data, _, err := s.handle.ReadPacketData() + if err == pcap.NextErrorTimeoutExpired { + continue + } else if err != nil { + log.Printf("error reading packet: %v", err) + continue + } + + // Parse the packet. We'd use DecodingLayerParser here if we + // wanted to be really fast. + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) + + // Find the packets we care about, and print out logging + // information about them. All others are ignored. + if net := packet.NetworkLayer(); net == nil { + // log.Printf("packet has no network layer") + } else if net.NetworkFlow() != ipFlow { + // log.Printf("packet does not match our ip src/dst") + } else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer == nil { + // log.Printf("packet has not tcp layer") + } else if tcp, ok := tcpLayer.(*layers.TCP); !ok { + // We panic here because this is guaranteed to never + // happen. + panic("tcp layer is not tcp layer :-/") + } else if tcp.DstPort != 54321 { + // log.Printf("dst port %v does not match", tcp.DstPort) + } else if tcp.RST { + log.Printf(" port %v closed", tcp.SrcPort) + } else if tcp.SYN && tcp.ACK { + log.Printf(" port %v open", tcp.SrcPort) + } else { + // log.Printf("ignoring useless packet") + } + } +} + +// send sends the given layers as a single packet on the network. +func (s *scanner) send(l ...gopacket.SerializableLayer) error { + if err := gopacket.SerializeLayers(s.buf, s.opts, l...); err != nil { + return err + } + return s.handle.WritePacketData(s.buf.Bytes()) +} + +func main() { + defer util.Run()() + router, err := routing.New() + if err != nil { + log.Fatal("routing error:", err) + } + for _, arg := range flag.Args() { + var ip net.IP + if ip = net.ParseIP(arg); ip == nil { + log.Printf("non-ip target: %q", arg) + continue + } else if ip = ip.To4(); ip == nil { + log.Printf("non-ipv4 target: %q", arg) + continue + } + // Note: newScanner creates and closes a pcap Handle once for + // every scan target. We could do much better, were this not an + // example ;) + s, err := newScanner(ip, router) + if err != nil { + log.Printf("unable to create scanner for %v: %v", ip, err) + continue + } + if err := s.scan(); err != nil { + log.Printf("unable to scan %v: %v", ip, err) + } + s.close() + } +} diff --git a/vendor/github.com/google/gopacket/examples/util/util.go b/vendor/github.com/google/gopacket/examples/util/util.go new file mode 100644 index 00000000..0f698fb9 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/util/util.go @@ -0,0 +1,40 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package util provides shared utilities for all gopacket examples. +package util + +import ( + "flag" + "log" + "os" + "runtime/pprof" +) + +var cpuprofile = flag.String("cpuprofile", "", "Where to write CPU profile") + +// Run starts up stuff at the beginning of a main function, and returns a +// function to defer until the function completes. It should be used like this: +// +// func main() { +// defer util.Run()() +// ... stuff ... +// } +func Run() func() { + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatalf("could not open cpu profile file %q", *cpuprofile) + } + pprof.StartCPUProfile(f) + return func() { + pprof.StopCPUProfile() + f.Close() + } + } + return func() {} +} diff --git a/vendor/github.com/google/gopacket/flows.go b/vendor/github.com/google/gopacket/flows.go new file mode 100644 index 00000000..7203ead0 --- /dev/null +++ b/vendor/github.com/google/gopacket/flows.go @@ -0,0 +1,236 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "bytes" + "fmt" + "strconv" +) + +// MaxEndpointSize determines the maximum size in bytes of an endpoint address. +// +// Endpoints/Flows have a problem: They need to be hashable. Therefore, they +// can't use a byte slice. The two obvious choices are to use a string or a +// byte array. Strings work great, but string creation requires memory +// allocation, which can be slow. Arrays work great, but have a fixed size. We +// originally used the former, now we've switched to the latter. Use of a fixed +// byte-array doubles the speed of constructing a flow (due to not needing to +// allocate). This is a huge increase... too much for us to pass up. +// +// The end result of this, though, is that an endpoint/flow can't be created +// using more than MaxEndpointSize bytes per address. +const MaxEndpointSize = 16 + +// Endpoint is the set of bytes used to address packets at various layers. +// See LinkLayer, NetworkLayer, and TransportLayer specifications. +// Endpoints are usable as map keys. +type Endpoint struct { + typ EndpointType + len int + raw [MaxEndpointSize]byte +} + +// EndpointType returns the endpoint type associated with this endpoint. +func (a Endpoint) EndpointType() EndpointType { return a.typ } + +// Raw returns the raw bytes of this endpoint. These aren't human-readable +// most of the time, but they are faster than calling String. +func (a Endpoint) Raw() []byte { return a.raw[:a.len] } + +// LessThan provides a stable ordering for all endpoints. It sorts first based +// on the EndpointType of an endpoint, then based on the raw bytes of that +// endpoint. +// +// For some endpoints, the actual comparison may not make sense, however this +// ordering does provide useful information for most Endpoint types. +// Ordering is based first on endpoint type, then on raw endpoint bytes. +// Endpoint bytes are sorted lexigraphically. +func (a Endpoint) LessThan(b Endpoint) bool { + return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0) +} + +// fnvHash is used by our FastHash functions, and implements the FNV hash +// created by Glenn Fowler, Landon Curt Noll, and Phong Vo. +// See http://isthe.com/chongo/tech/comp/fnv/. +func fnvHash(s []byte) (h uint64) { + h = fnvBasis + for i := 0; i < len(s); i++ { + h ^= uint64(s[i]) + h *= fnvPrime + } + return +} + +const fnvBasis = 14695981039346656037 +const fnvPrime = 1099511628211 + +// FastHash provides a quick hashing function for an endpoint, useful if you'd +// like to split up endpoints by modulos or other load-balancing techniques. +// It uses a variant of Fowler-Noll-Vo hashing. +// +// The output of FastHash is not guaranteed to remain the same through future +// code revisions, so should not be used to key values in persistent storage. +func (a Endpoint) FastHash() (h uint64) { + h = fnvHash(a.raw[:a.len]) + h ^= uint64(a.typ) + h *= fnvPrime + return +} + +// NewEndpoint creates a new Endpoint object. +// +// The size of raw must be less than MaxEndpointSize, otherwise this function +// will panic. +func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) { + e.len = len(raw) + if e.len > MaxEndpointSize { + panic("raw byte length greater than MaxEndpointSize") + } + e.typ = typ + copy(e.raw[:], raw) + return +} + +// EndpointTypeMetadata is used to register a new endpoint type. +type EndpointTypeMetadata struct { + // Name is the string returned by an EndpointType's String function. + Name string + // Formatter is called from an Endpoint's String function to format the raw + // bytes in an Endpoint into a human-readable string. + Formatter func([]byte) string +} + +// EndpointType is the type of a gopacket Endpoint. This type determines how +// the bytes stored in the endpoint should be interpreted. +type EndpointType int64 + +var endpointTypes = map[EndpointType]EndpointTypeMetadata{} + +// RegisterEndpointType creates a new EndpointType and registers it globally. +// It MUST be passed a unique number, or it will panic. Numbers 0-999 are +// reserved for gopacket's use. +func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType { + t := EndpointType(num) + if _, ok := endpointTypes[t]; ok { + panic("Endpoint type number already in use") + } + endpointTypes[t] = meta + return t +} + +func (e EndpointType) String() string { + if t, ok := endpointTypes[e]; ok { + return t.Name + } + return strconv.Itoa(int(e)) +} + +func (a Endpoint) String() string { + if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil { + return t.Formatter(a.raw[:a.len]) + } + return fmt.Sprintf("%v:%v", a.typ, a.raw) +} + +// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint. +// Flows are usable as map keys. +type Flow struct { + typ EndpointType + slen, dlen int + src, dst [MaxEndpointSize]byte +} + +// FlowFromEndpoints creates a new flow by pasting together two endpoints. +// The endpoints must have the same EndpointType, or this function will return +// an error. +func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) { + if src.typ != dst.typ { + err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ) + return + } + return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil +} + +// FastHash provides a quick hashing function for a flow, useful if you'd +// like to split up flows by modulos or other load-balancing techniques. +// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide +// with its reverse flow. IE: the flow A->B will have the same hash as the flow +// B->A. +// +// The output of FastHash is not guaranteed to remain the same through future +// code revisions, so should not be used to key values in persistent storage. +func (f Flow) FastHash() (h uint64) { + // This combination must be commutative. We don't use ^, since that would + // give the same hash for all A->A flows. + h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen]) + h ^= uint64(f.typ) + h *= fnvPrime + return +} + +// String returns a human-readable representation of this flow, in the form +// "Src->Dst" +func (f Flow) String() string { + s, d := f.Endpoints() + return fmt.Sprintf("%v->%v", s, d) +} + +// EndpointType returns the EndpointType for this Flow. +func (f Flow) EndpointType() EndpointType { + return f.typ +} + +// Endpoints returns the two Endpoints for this flow. +func (f Flow) Endpoints() (src, dst Endpoint) { + return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst} +} + +// Src returns the source Endpoint for this flow. +func (f Flow) Src() (src Endpoint) { + src, _ = f.Endpoints() + return +} + +// Dst returns the destination Endpoint for this flow. +func (f Flow) Dst() (dst Endpoint) { + _, dst = f.Endpoints() + return +} + +// Reverse returns a new flow with endpoints reversed. +func (f Flow) Reverse() Flow { + return Flow{f.typ, f.dlen, f.slen, f.dst, f.src} +} + +// NewFlow creates a new flow. +// +// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will +// panic. +func NewFlow(t EndpointType, src, dst []byte) (f Flow) { + f.slen = len(src) + f.dlen = len(dst) + if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize { + panic("flow raw byte length greater than MaxEndpointSize") + } + f.typ = t + copy(f.src[:], src) + copy(f.dst[:], dst) + return +} + +// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints +// that are specified incorrectly during creation. +var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string { + return fmt.Sprintf("%v", b) +}}) + +// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid. +var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil) + +// InvalidFlow is a singleton Flow of type EndpointInvalid. +var InvalidFlow = NewFlow(EndpointInvalid, nil, nil) diff --git a/vendor/github.com/google/gopacket/gc b/vendor/github.com/google/gopacket/gc new file mode 100755 index 00000000..57bcdee2 --- /dev/null +++ b/vendor/github.com/google/gopacket/gc @@ -0,0 +1,278 @@ +#!/bin/bash +# Copyright 2012 Google, Inc. All rights reserved. + +# This script provides a simple way to run benchmarks against previous code and +# keep a log of how benchmarks change over time. When used with the --benchmark +# flag, it runs benchmarks from the current code and from the last commit run +# with --benchmark, then stores the results in the git commit description. We +# rerun the old benchmarks along with the new ones, since there's no guarantee +# that git commits will happen on the same machine, so machine differences could +# cause wildly inaccurate results. +# +# If you're making changes to 'gopacket' which could cause performance changes, +# you may be requested to use this commit script to make sure your changes don't +# have large detrimental effects (or to show off how awesome your performance +# improvements are). +# +# If not run with the --benchmark flag, this script is still very useful... it +# makes sure all the correct go formatting, building, and testing work as +# expected. + +function Usage { + cat < + +--benchmark: Run benchmark comparisons against last benchmark'd commit +--root: Run tests that require root priviledges +--gen: Generate code for MACs/ports by pulling down external data + +Note, some 'git commit' flags are necessary, if all else fails, pass in -a +EOF + exit 1 +} + +BENCH="" +GEN="" +ROOT="" +while [ ! -z "$1" ]; do + case "$1" in + "--benchmark") + BENCH="$2" + shift + shift + ;; + "--gen") + GEN="yes" + shift + ;; + "--root") + ROOT="yes" + shift + ;; + "--help") + Usage + ;; + "-h") + Usage + ;; + "help") + Usage + ;; + *) + break + ;; + esac +done + +function Root { + if [ ! -z "$ROOT" ]; then + local exec="$1" + # Some folks (like me) keep source code in places inaccessible by root (like + # NFS), so to make sure things run smoothly we copy them to a /tmp location. + local tmpfile="$(mktemp -t gopacket_XXXXXXXX)" + echo "Running root test executable $exec as $tmpfile" + cp "$exec" "$tmpfile" + chmod a+x "$tmpfile" + shift + sudo "$tmpfile" "$@" + fi +} + +if [ "$#" -eq "0" ]; then + Usage +fi + +cd $(dirname $0) + +# Check for copyright notices. +for filename in $(find ./ -type f -name '*.go'); do + if ! head -n 1 "$filename" | grep -q Copyright; then + echo "File '$filename' may not have copyright notice" + exit 1 + fi +done + +set -e +set -x + +if [ ! -z "$ROOT" ]; then + echo "Running SUDO to get root priviledges for root tests" + sudo echo "have root" +fi + +if [ ! -z "$GEN" ]; then + pushd macs + go run gen.go | gofmt > valid_mac_prefixes.go + popd + pushd layers + go run gen.go | gofmt > iana_ports.go + popd +fi + +# Make sure everything is formatted, compiles, and tests pass. +go fmt ./... +go test -i ./... 2>/dev/null >/dev/null || true +go test +go build +pushd examples/bytediff +go build +popd +if [ -f /usr/include/pcap.h ]; then + pushd pcap + go test ./... + go build ./... + go build pcap_tester.go + Root pcap_tester --mode=basic + Root pcap_tester --mode=filtered + Root pcap_tester --mode=timestamp || echo "You might not support timestamp sources" + popd + pushd examples/pcapdump + go build + popd + pushd examples/arpscan + go build + popd + pushd examples/bidirectional + go build + popd + pushd examples/synscan + go build + popd + pushd examples/httpassembly + go build + popd + pushd examples/statsassembly + go build + popd +fi +pushd macs +go test ./... +gofmt -w gen.go +go build gen.go +popd +pushd tcpassembly +go test ./... +popd +pushd reassembly +go test ./... +popd +pushd layers +gofmt -w gen.go +go build gen.go +go test ./... +popd +pushd pcapgo +go test ./... +go build ./... +popd +if [ -f /usr/include/linux/if_packet.h ]; then + if grep -q TPACKET_V3 /usr/include/linux/if_packet.h; then + pushd afpacket + go build ./... + go test ./... + popd + fi +fi +if [ -f /usr/include/pfring.h ]; then + pushd pfring + go test ./... + go build ./... + popd + pushd examples/pfdump + go build + popd +fi + +for travis_script in `ls .travis.*.sh`; do + ./$travis_script +done + +# Run our initial commit +git commit "$@" + +if [ -z "$BENCH" ]; then + set +x + echo "We're not benchmarking and we've committed... we're done!" + exit +fi + +### If we get here, we want to run benchmarks from current commit, and compare +### then to benchmarks from the last --benchmark commit. + +# Get our current branch. +BRANCH="$(git branch | grep '^*' | awk '{print $2}')" + +# File we're going to build our commit description in. +COMMIT_FILE="$(mktemp /tmp/tmp.XXXXXXXX)" + +# Add the word "BENCH" to the start of the git commit. +echo -n "BENCH " > $COMMIT_FILE + +# Get the current description... there must be an easier way. +git log -n 1 | grep '^ ' | sed 's/^ //' >> $COMMIT_FILE + +# Get the commit sha for the last benchmark commit +PREV=$(git log -n 1 --grep='BENCHMARK_MARKER_DO_NOT_CHANGE' | head -n 1 | awk '{print $2}') + +## Run current benchmarks + +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +pushd layers +go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE +popd +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +fi + + + +## Reset to last benchmark commit, run benchmarks + +git checkout $PREV + +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +pushd layers +go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE +popd +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +fi + + + +## Reset back to the most recent commit, edit the commit message by appending +## benchmark results. +git checkout $BRANCH +git commit --amend -F $COMMIT_FILE diff --git a/vendor/github.com/google/gopacket/ip4defrag/defrag.go b/vendor/github.com/google/gopacket/ip4defrag/defrag.go new file mode 100644 index 00000000..9d3862fa --- /dev/null +++ b/vendor/github.com/google/gopacket/ip4defrag/defrag.go @@ -0,0 +1,350 @@ +// Copyright 2013 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package ip4defrag implements a IPv4 defragmenter +package ip4defrag + +import ( + "container/list" + "errors" + "fmt" + "log" + "sync" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +// Quick and Easy to use debug code to trace +// how defrag works. +var debug debugging = false // or flip to true +type debugging bool + +func (d debugging) Printf(format string, args ...interface{}) { + if d { + log.Printf(format, args...) + } +} + +// Constants determining how to handle fragments. +const ( + IPv4MinimumFragmentSize = 576 // Minimum size of a single fragment + IPv4MaximumSize = 65535 // Maximum size of a fragment (2^16) + IPv4MaximumFragmentOffset = 8189 // Maximum offset of a fragment + IPv4MaximumFragmentListLen = 8 // Back out if we get more than this many fragments +) + +// DefragIPv4 takes in an IPv4 packet with a fragment payload. +// +// It do not modify the IPv4 layer in place, 'in' remains untouched +// It returns a ready-to be used IPv4 layer. +// +// If the passed-in IPv4 layer is NOT fragmented, it will +// immediately return it without modifying the layer. +// +// If the IPv4 layer is a fragment and we don't have all +// fragments, it will return nil and store whatever internal +// information it needs to eventually defrag the packet. +// +// If the IPv4 layer is the last fragment needed to reconstruct +// the packet, a new IPv4 layer will be returned, and will be set to +// the entire defragmented packet, +// +// It use a map of all the running flows +// +// Usage example: +// +// func HandlePacket(in *layers.IPv4) err { +// defragger := ip4defrag.NewIPv4Defragmenter() +// in, err := defragger.DefragIPv4(in) +// if err != nil { +// return err +// } else if in == nil { +// return nil // packet fragment, we don't have whole packet yet. +// } +// // At this point, we know that 'in' is defragmented. +// //It may be the same 'in' passed to +// // HandlePacket, or it may not, but we don't really care :) +// ... do stuff to 'in' ... +//} +// +func (d *IPv4Defragmenter) DefragIPv4(in *layers.IPv4) (*layers.IPv4, error) { + return d.DefragIPv4WithTimestamp(in, time.Now()) +} + +// DefragIPv4WithTimestamp provides functionality of DefragIPv4 with +// an additional timestamp parameter which is used for discarding +// old fragments instead of time.Now() +// +// This is useful when operating on pcap files instead of live captured data +// +func (d *IPv4Defragmenter) DefragIPv4WithTimestamp(in *layers.IPv4, t time.Time) (*layers.IPv4, error) { + // check if we need to defrag + if st := d.dontDefrag(in); st == true { + debug.Printf("defrag: do nothing, do not need anything") + return in, nil + } + // perfom security checks + st, err := d.securityChecks(in) + if err != nil || st == false { + debug.Printf("defrag: alert security check") + return nil, err + } + + // ok, got a fragment + debug.Printf("defrag: got a new fragment in.Id=%d in.FragOffset=%d in.Flags=%d\n", + in.Id, in.FragOffset*8, in.Flags) + + // have we already seen a flow between src/dst with that Id? + ipf := newIPv4(in) + var fl *fragmentList + var exist bool + d.Lock() + fl, exist = d.ipFlows[ipf] + if !exist { + debug.Printf("defrag: unknown flow, creating a new one\n") + fl = new(fragmentList) + d.ipFlows[ipf] = fl + } + d.Unlock() + // insert, and if final build it + out, err2 := fl.insert(in, t) + + // at last, if we hit the maximum frag list len + // without any defrag success, we just drop everything and + // raise an error + if out == nil && fl.List.Len()+1 > IPv4MaximumFragmentListLen { + d.flush(ipf) + return nil, fmt.Errorf("defrag: Fragment List hits its maximum"+ + "size(%d), without success. Flushing the list", + IPv4MaximumFragmentListLen) + } + + // if we got a packet, it's a new one, and he is defragmented + if out != nil { + // when defrag is done for a flow between two ip + // clean the list + d.flush(ipf) + return out, nil + } + return nil, err2 +} + +// DiscardOlderThan forgets all packets without any activity since +// time t. It returns the number of FragmentList aka number of +// fragment packets it has discarded. +func (d *IPv4Defragmenter) DiscardOlderThan(t time.Time) int { + var nb int + d.Lock() + for k, v := range d.ipFlows { + if v.LastSeen.Before(t) { + nb = nb + 1 + delete(d.ipFlows, k) + } + } + d.Unlock() + return nb +} + +// flush the fragment list for a particular flow +func (d *IPv4Defragmenter) flush(ipf ipv4) { + d.Lock() + fl := new(fragmentList) + d.ipFlows[ipf] = fl + d.Unlock() +} + +// dontDefrag returns true if the IPv4 packet do not need +// any defragmentation +func (d *IPv4Defragmenter) dontDefrag(ip *layers.IPv4) bool { + // don't defrag packet with DF flag + if ip.Flags&layers.IPv4DontFragment != 0 { + return true + } + // don't defrag not fragmented ones + if ip.Flags&layers.IPv4MoreFragments == 0 && ip.FragOffset == 0 { + return true + } + return false +} + +// securityChecks performs the needed security checks +func (d *IPv4Defragmenter) securityChecks(ip *layers.IPv4) (bool, error) { + // don't allow too big fragment offset + if ip.FragOffset > IPv4MaximumFragmentOffset { + return false, fmt.Errorf("defrag: fragment offset too big "+ + "(handcrafted? %d > %d)", ip.FragOffset, IPv4MaximumFragmentOffset) + } + fragOffset := ip.FragOffset * 8 + + // don't allow fragment that would oversize an IP packet + if fragOffset+ip.Length > IPv4MaximumSize { + return false, fmt.Errorf("defrag: fragment will overrun "+ + "(handcrafted? %d > %d)", ip.FragOffset*8+ip.Length, IPv4MaximumSize) + } + + return true, nil +} + +// fragmentList holds a container/list used to contains IP +// packets/fragments. It stores internal counters to track the +// maximum total of byte, and the current length it has received. +// It also stores a flag to know if he has seen the last packet. +type fragmentList struct { + List list.List + Highest uint16 + Current uint16 + FinalReceived bool + LastSeen time.Time +} + +// insert insert an IPv4 fragment/packet into the Fragment List +// It use the following strategy : we are inserting fragment based +// on their offset, latest first. This is sometimes called BSD-Right. +// See: http://www.sans.org/reading-room/whitepapers/detection/ip-fragment-reassembly-scapy-33969 +func (f *fragmentList) insert(in *layers.IPv4, t time.Time) (*layers.IPv4, error) { + // TODO: should keep a copy of *in in the list + // or not (ie the packet source is reliable) ? -> depends on Lazy / last packet + fragOffset := in.FragOffset * 8 + if fragOffset >= f.Highest { + f.List.PushBack(in) + } else { + for e := f.List.Front(); e != nil; e = e.Next() { + frag, _ := e.Value.(*layers.IPv4) + if in.FragOffset == frag.FragOffset { + // TODO: what if we receive a fragment + // that begins with duplicate data but + // *also* has new data? For example: + // + // AAAA + // BB + // BBCC + // DDDD + // + // In this situation we completely + // ignore CC and the complete packet can + // never be reassembled. + debug.Printf("defrag: ignoring frag %d as we already have it (duplicate?)\n", + fragOffset) + return nil, nil + } + if in.FragOffset < frag.FragOffset { + debug.Printf("defrag: inserting frag %d before existing frag %d\n", + fragOffset, frag.FragOffset*8) + f.List.InsertBefore(in, e) + break + } + } + } + + f.LastSeen = t + + fragLength := in.Length - 20 + // After inserting the Fragment, we update the counters + if f.Highest < fragOffset+fragLength { + f.Highest = fragOffset + fragLength + } + f.Current = f.Current + fragLength + + debug.Printf("defrag: insert ListLen: %d Highest:%d Current:%d\n", + f.List.Len(), + f.Highest, f.Current) + + // Final Fragment ? + if in.Flags&layers.IPv4MoreFragments == 0 { + f.FinalReceived = true + } + // Ready to try defrag ? + if f.FinalReceived && f.Highest == f.Current { + return f.build(in) + } + return nil, nil +} + +// Build builds the final datagram, modifying ip in place. +// It puts priority to packet in the early position of the list. +// See Insert for more details. +func (f *fragmentList) build(in *layers.IPv4) (*layers.IPv4, error) { + var final []byte + var currentOffset uint16 + + debug.Printf("defrag: building the datagram \n") + for e := f.List.Front(); e != nil; e = e.Next() { + frag, _ := e.Value.(*layers.IPv4) + if frag.FragOffset*8 == currentOffset { + debug.Printf("defrag: building - adding %d\n", frag.FragOffset*8) + final = append(final, frag.Payload...) + currentOffset = currentOffset + frag.Length - 20 + } else if frag.FragOffset*8 < currentOffset { + // overlapping fragment - let's take only what we need + startAt := currentOffset - frag.FragOffset*8 + debug.Printf("defrag: building - overlapping, starting at %d\n", + startAt) + if startAt > frag.Length-20 { + return nil, errors.New("defrag: building - invalid fragment") + } + final = append(final, frag.Payload[startAt:]...) + currentOffset = currentOffset + frag.FragOffset*8 + } else { + // Houston - we have an hole ! + debug.Printf("defrag: hole found while building, " + + "stopping the defrag process\n") + return nil, errors.New("defrag: building - hole found") + } + debug.Printf("defrag: building - next is %d\n", currentOffset) + } + + // TODO recompute IP Checksum + out := &layers.IPv4{ + Version: in.Version, + IHL: in.IHL, + TOS: in.TOS, + Length: f.Highest, + Id: 0, + Flags: 0, + FragOffset: 0, + TTL: in.TTL, + Protocol: in.Protocol, + Checksum: 0, + SrcIP: in.SrcIP, + DstIP: in.DstIP, + Options: in.Options, + Padding: in.Padding, + } + out.Payload = final + + return out, nil +} + +// ipv4 is a struct to be used as a key. +type ipv4 struct { + ip4 gopacket.Flow + id uint16 +} + +// newIPv4 returns a new initialized IPv4 Flow +func newIPv4(ip *layers.IPv4) ipv4 { + return ipv4{ + ip4: ip.NetworkFlow(), + id: ip.Id, + } +} + +// IPv4Defragmenter is a struct which embedded a map of +// all fragment/packet. +type IPv4Defragmenter struct { + sync.RWMutex + ipFlows map[ipv4]*fragmentList +} + +// NewIPv4Defragmenter returns a new IPv4Defragmenter +// with an initialized map. +func NewIPv4Defragmenter() *IPv4Defragmenter { + return &IPv4Defragmenter{ + ipFlows: make(map[ipv4]*fragmentList), + } +} diff --git a/vendor/github.com/google/gopacket/ip4defrag/defrag_test.go b/vendor/github.com/google/gopacket/ip4defrag/defrag_test.go new file mode 100644 index 00000000..1eb66d28 --- /dev/null +++ b/vendor/github.com/google/gopacket/ip4defrag/defrag_test.go @@ -0,0 +1,1396 @@ +// Copyright 2013 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package ip4defrag + +import ( + "bytes" + "fmt" + "net" + "testing" + + "github.com/google/gopacket" + "github.com/google/gopacket/bytediff" + "github.com/google/gopacket/layers" +) + +func TestNotFrag(t *testing.T) { + ip := layers.IPv4{ + Version: 4, + TTL: 220, + SrcIP: net.IPv4(1, 1, 1, 1), + DstIP: net.IPv4(2, 2, 2, 2), + Flags: layers.IPv4DontFragment, + } + defrag := NewIPv4Defragmenter() + + out, err := defrag.DefragIPv4(&ip) + if out == nil || err != nil { + t.Errorf("defrag: this packet do not need to be defrag ['%s']", err) + } +} + +func TestDefragPingMultipleFrags(t *testing.T) { + defrag := NewIPv4Defragmenter() + + // We inject the 4 fragments and test the DefragIPv4 interface + gentestDefrag(t, defrag, testPing1Frag1, false, "Ping1Frag1") + gentestDefrag(t, defrag, testPing1Frag1, false, "Ping1Frag1") + gentestDefrag(t, defrag, testPing1Frag1, false, "Ping1Frag1") + gentestDefrag(t, defrag, testPing1Frag3, false, "Ping1Frag3") + gentestDefrag(t, defrag, testPing1Frag2, false, "Ping1Frag2") + ip := gentestDefrag(t, defrag, testPing1Frag4, true, "Ping1Frag4") + + if len(ip.Payload) != 4508 { + t.Fatalf("defrag: expecting a packet of 4508 bytes, got %d", len(ip.Payload)) + } + + validPayload := append(testPing1Frag1[34:], testPing1Frag2[34:]...) + validPayload = append(validPayload, testPing1Frag3[34:]...) + validPayload = append(validPayload, testPing1Frag4[34:]...) + + if bytes.Compare(validPayload, ip.Payload) != 0 { + fmt.Println(bytediff.BashOutput.String( + bytediff.Diff(validPayload, ip.Payload))) + t.Errorf("defrag: payload is not correctly defragmented") + } +} + +func TestDefragPing1(t *testing.T) { + defrag := NewIPv4Defragmenter() + + // We inject the 4 fragments and test the DefragIPv4 interface + gentestDefrag(t, defrag, testPing1Frag1, false, "Ping1Frag1") + gentestDefrag(t, defrag, testPing1Frag3, false, "Ping1Frag3") + gentestDefrag(t, defrag, testPing1Frag2, false, "Ping1Frag2") + ip := gentestDefrag(t, defrag, testPing1Frag4, true, "Ping1Frag4") + + if len(ip.Payload) != 4508 { + t.Fatalf("defrag: expecting a packet of 4508 bytes, got %d", len(ip.Payload)) + } + + validPayload := append(testPing1Frag1[34:], testPing1Frag2[34:]...) + validPayload = append(validPayload, testPing1Frag3[34:]...) + validPayload = append(validPayload, testPing1Frag4[34:]...) + + if bytes.Compare(validPayload, ip.Payload) != 0 { + fmt.Println(bytediff.BashOutput.String( + bytediff.Diff(validPayload, ip.Payload))) + t.Errorf("defrag: payload is not correctly defragmented") + } + + // We redo the same test to handle duplication, and be sure + // that the internal list is correctly cleaned up. + gentestDefrag(t, defrag, testPing1Frag1, false, "Ping1Frag1") + gentestDefrag(t, defrag, testPing1Frag3, false, "Ping1Frag3") + gentestDefrag(t, defrag, testPing1Frag2, false, "Ping1Frag2") + ip2 := gentestDefrag(t, defrag, testPing1Frag4, true, "Ping1Frag4") + + if bytes.Compare(ip2.Payload, ip.Payload) != 0 { + fmt.Println(bytediff.BashOutput.String( + bytediff.Diff(validPayload, ip.Payload))) + t.Errorf("defrag: ip and ip2 payload are different") + } +} + +func TestDefragPing1and2(t *testing.T) { + debug = false + defrag := NewIPv4Defragmenter() + + // We inject the 8 mixed fragments from two "flows" + // and test the DefragIPv4 interface + gentestDefrag(t, defrag, testPing1Frag1, false, "Ping1Frag1") + gentestDefrag(t, defrag, testPing1Frag3, false, "Ping1Frag3") + gentestDefrag(t, defrag, testPing2Frag3, false, "Ping2Frag3") + gentestDefrag(t, defrag, testPing2Frag4, false, "Ping2Frag4") + gentestDefrag(t, defrag, testPing1Frag2, false, "Ping1Frag2") + gentestDefrag(t, defrag, testPing2Frag1, false, "Ping2Frag1") + ip := gentestDefrag(t, defrag, testPing1Frag4, true, "Ping1Frag4") + + if len(ip.Payload) != 4508 { + t.Fatalf("defrag: expecting a packet Ping1 of 4508 bytes, got %d", + len(ip.Payload)) + } + + validPayload := append(testPing1Frag1[34:], testPing1Frag2[34:]...) + validPayload = append(validPayload, testPing1Frag3[34:]...) + validPayload = append(validPayload, testPing1Frag4[34:]...) + + if bytes.Compare(validPayload, ip.Payload) != 0 { + fmt.Println(bytediff.BashOutput.String( + bytediff.Diff(validPayload, ip.Payload))) + t.Errorf("defrag: payload Ping1 is not correctly defragmented") + } + + ip = gentestDefrag(t, defrag, testPing2Frag2, true, "Ping2Frag2") + if len(ip.Payload) != 4508 { + t.Fatalf("defrag: expecting a packet Ping2 of 4508 bytes, got %d", + len(ip.Payload)) + } + + validPayload2 := append(testPing2Frag1[34:], testPing2Frag2[34:]...) + validPayload2 = append(validPayload2, testPing2Frag3[34:]...) + validPayload2 = append(validPayload2, testPing2Frag4[34:]...) + + if bytes.Compare(validPayload2, ip.Payload) != 0 { + fmt.Println(bytediff.BashOutput.String( + bytediff.Diff(validPayload2, ip.Payload))) + t.Errorf("defrag: payload Ping2 is not correctly defragmented") + } + debug = false +} + +func TestDefragPingTooMuch(t *testing.T) { + defrag := NewIPv4Defragmenter() + + ip1 := layers.IPv4{ + Version: 4, + TTL: 15, + SrcIP: net.IPv4(1, 1, 1, 1), + DstIP: net.IPv4(2, 2, 2, 2), + Id: 0xcc, + FragOffset: 0, + Length: 500, + Flags: layers.IPv4MoreFragments, + } + defrag.DefragIPv4(&ip1) + for i := 2; i < 8; i = i + 1 { + nip := ip1 + nip.FragOffset = ip1.Length * uint16(i) + out, err := defrag.DefragIPv4(&nip) + if err != nil || out != nil { + t.Fatalf("defrag: %s", err) + } + + } + ip8 := ip1 + ip8.FragOffset = 666 + + _, err := defrag.DefragIPv4(&ip8) + if err == nil { + t.Fatalf("defrag: Maximum number of fragments are supposed to be 8") + } +} + +func gentestDefrag(t *testing.T, defrag *IPv4Defragmenter, buf []byte, expect bool, label string) *layers.IPv4 { + p := gopacket.NewPacket(buf, layers.LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + ipL := p.Layer(layers.LayerTypeIPv4) + in, _ := ipL.(*layers.IPv4) + + out, err := defrag.DefragIPv4(in) + if err != nil { + t.Fatalf("defrag: %s", err) + } + status := false + if out != nil { + status = true + } + if status != expect { + t.Fatalf("defrag: a fragment was not detected (%s)", label) + } + return out +} + +/* Frame 1-1 (1514 bytes) */ +var testPing1Frag1 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x05, 0xdc, 0xb3, 0xe1, 0x20, 0x00, 0x40, 0x01, /* .... .@. */ + 0xcf, 0x70, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* .p...... */ + 0x08, 0x08, 0x08, 0x00, 0x45, 0xa6, 0x14, 0xbf, /* ....E... */ + 0x00, 0x01, 0x9e, 0x3e, 0x20, 0x55, 0x00, 0x00, /* ...> U.. */ + 0x00, 0x00, 0x16, 0x91, 0x0d, 0x00, 0x00, 0x00, /* ........ */ + 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, /* .. */ +} + +/* Frame (1514 bytes) */ +var testPing1Frag2 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x05, 0xdc, 0xb3, 0xe1, 0x20, 0xb9, 0x40, 0x01, /* .... .@. */ + 0xce, 0xb7, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* ........ */ + 0x08, 0x08, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, /* .. */ +} + +/* Frame (1514 bytes) */ +var testPing1Frag3 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x05, 0xdc, 0xb3, 0xe1, 0x21, 0x72, 0x40, 0x01, /* ....!r@. */ + 0xcd, 0xfe, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* ........ */ + 0x08, 0x08, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, /* NO */ +} + +/* Frame (102 bytes) */ +var testPing1Frag4 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x00, 0x58, 0xb3, 0xe1, 0x02, 0x2b, 0x40, 0x01, /* .X...+@. */ + 0xf2, 0xc9, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* ........ */ + 0x08, 0x08, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* ..PQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, /* ...... */ +} + +/* Frame (1514 bytes) */ +var testPing2Frag1 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x05, 0xdc, 0xb4, 0x9e, 0x20, 0x00, 0x40, 0x01, /* .... .@. */ + 0xce, 0xb3, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* ........ */ + 0x08, 0x08, 0x08, 0x00, 0x9e, 0xa0, 0x14, 0xbf, /* ........ */ + 0x00, 0x02, 0x9f, 0x3e, 0x20, 0x55, 0x00, 0x00, /* ...> U.. */ + 0x00, 0x00, 0xbc, 0x95, 0x0d, 0x00, 0x00, 0x00, /* ........ */ + 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, /* .. */ +} + +/* Frame (1514 bytes) */ +var testPing2Frag2 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x05, 0xdc, 0xb4, 0x9e, 0x20, 0xb9, 0x40, 0x01, /* .... .@. */ + 0xcd, 0xfa, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* ........ */ + 0x08, 0x08, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, /* .. */ +} + +/* Frame (1514 bytes) */ +var testPing2Frag3 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x05, 0xdc, 0xb4, 0x9e, 0x21, 0x72, 0x40, 0x01, /* ....!r@. */ + 0xcd, 0x41, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* .A...... */ + 0x08, 0x08, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* NOPQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, /* ........ */ + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, /* ........ */ + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, /* ........ */ + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, /* ........ */ + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, /* ........ */ + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, /* ........ */ + 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* ........ */ + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, /* ........ */ + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, /* ........ */ + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, /* ........ */ + 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* ........ */ + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* ........ */ + 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, /* ........ */ + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, /* ........ */ + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* ........ */ + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, /* ........ */ + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, /* ........ */ + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, /* ........ */ + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, /* .. !"#$% */ + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, /* &'()*+,- */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, /* ./012345 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* 6789:;<= */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, /* >?@ABCDE */ + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, /* FGHIJKLM */ + 0x4e, 0x4f, /* NO */ +} + +/* Frame (102 bytes) */ +var testPing2Frag4 = []byte{ + 0xf4, 0xca, 0xe5, 0x4e, 0xe1, 0x46, 0x7c, 0x7a, /* ...N.F|z */ + 0x91, 0x7d, 0x7c, 0x74, 0x08, 0x00, 0x45, 0x00, /* .}|t..E. */ + 0x00, 0x58, 0xb4, 0x9e, 0x02, 0x2b, 0x40, 0x01, /* .X...+@. */ + 0xf2, 0x0c, 0xc0, 0xa8, 0x01, 0x17, 0x08, 0x08, /* ........ */ + 0x08, 0x08, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, /* ..PQRSTU */ + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, /* VWXYZ[\] */ + 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, /* ^_`abcde */ + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, /* fghijklm */ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* nopqrstu */ + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* vwxyz{|} */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, /* ~....... */ + 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, /* ........ */ + 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, /* ...... */ +} diff --git a/vendor/github.com/google/gopacket/layerclass.go b/vendor/github.com/google/gopacket/layerclass.go new file mode 100644 index 00000000..775cd098 --- /dev/null +++ b/vendor/github.com/google/gopacket/layerclass.go @@ -0,0 +1,107 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +// LayerClass is a set of LayerTypes, used for grabbing one of a number of +// different types from a packet. +type LayerClass interface { + // Contains returns true if the given layer type should be considered part + // of this layer class. + Contains(LayerType) bool + // LayerTypes returns the set of all layer types in this layer class. + // Note that this may not be a fast operation on all LayerClass + // implementations. + LayerTypes() []LayerType +} + +// Contains implements LayerClass. +func (l LayerType) Contains(a LayerType) bool { + return l == a +} + +// LayerTypes implements LayerClass. +func (l LayerType) LayerTypes() []LayerType { + return []LayerType{l} +} + +// LayerClassSlice implements a LayerClass with a slice. +type LayerClassSlice []bool + +// Contains returns true if the given layer type should be considered part +// of this layer class. +func (s LayerClassSlice) Contains(t LayerType) bool { + return int(t) < len(s) && s[t] +} + +// LayerTypes returns all layer types in this LayerClassSlice. +// Because of LayerClassSlice's implementation, this could be quite slow. +func (s LayerClassSlice) LayerTypes() (all []LayerType) { + for i := 0; i < len(s); i++ { + if s[i] { + all = append(all, LayerType(i)) + } + } + return +} + +// NewLayerClassSlice creates a new LayerClassSlice by creating a slice of +// size max(types) and setting slice[t] to true for each type t. Note, if +// you implement your own LayerType and give it a high value, this WILL create +// a very large slice. +func NewLayerClassSlice(types []LayerType) LayerClassSlice { + var max LayerType + for _, typ := range types { + if typ > max { + max = typ + } + } + t := make([]bool, int(max+1)) + for _, typ := range types { + t[typ] = true + } + return t +} + +// LayerClassMap implements a LayerClass with a map. +type LayerClassMap map[LayerType]bool + +// Contains returns true if the given layer type should be considered part +// of this layer class. +func (m LayerClassMap) Contains(t LayerType) bool { + return m[t] +} + +// LayerTypes returns all layer types in this LayerClassMap. +func (m LayerClassMap) LayerTypes() (all []LayerType) { + for t := range m { + all = append(all, t) + } + return +} + +// NewLayerClassMap creates a LayerClassMap and sets map[t] to true for each +// type in types. +func NewLayerClassMap(types []LayerType) LayerClassMap { + m := LayerClassMap{} + for _, typ := range types { + m[typ] = true + } + return m +} + +// NewLayerClass creates a LayerClass, attempting to be smart about which type +// it creates based on which types are passed in. +func NewLayerClass(types []LayerType) LayerClass { + for _, typ := range types { + if typ > maxLayerType { + // NewLayerClassSlice could create a very large object, so instead create + // a map. + return NewLayerClassMap(types) + } + } + return NewLayerClassSlice(types) +} diff --git a/vendor/github.com/google/gopacket/layers/.linted b/vendor/github.com/google/gopacket/layers/.linted new file mode 100644 index 00000000..b2b29722 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/.linted @@ -0,0 +1,40 @@ +arp.go +base.go +base_test.go +cdp.go +ctp.go +decode_test.go +dhcp_test.go +dhcpv4.go +dns.go +dns_test.go +doc.go +dot11_test.go +dot1q.go +dot1q_test.go +eapol.go +etherip.go +fddi.go +gen.go +gre.go +gre_test.go +iana_ports.go +icmp6_test.go +igmp_test.go +ip4_test.go +ipsec.go +ipsec_test.go +loopback.go +mpls_test.go +ntp_test.go +ports.go +ppp.go +prism_test.go +radiotap_test.go +sflow_test.go +tcp_test.go +udp_test.go +usb_test.go +vrrp_test.go +vxlan.go +vxlan_test.go diff --git a/vendor/github.com/google/gopacket/layers/arp.go b/vendor/github.com/google/gopacket/layers/arp.go new file mode 100644 index 00000000..49e05ac9 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/arp.go @@ -0,0 +1,109 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +// Potential values for ARP.Operation. +const ( + ARPRequest = 1 + ARPReply = 2 +) + +// ARP is a ARP packet header. +type ARP struct { + BaseLayer + AddrType LinkType + Protocol EthernetType + HwAddressSize uint8 + ProtAddressSize uint8 + Operation uint16 + SourceHwAddress []byte + SourceProtAddress []byte + DstHwAddress []byte + DstProtAddress []byte +} + +// LayerType returns LayerTypeARP +func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP } + +// DecodeFromBytes decodes the given bytes into this layer. +func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2])) + arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4])) + arp.HwAddressSize = data[4] + arp.ProtAddressSize = data[5] + arp.Operation = binary.BigEndian.Uint16(data[6:8]) + arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize] + arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize] + arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize] + arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize] + + arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize + arp.Contents = data[:arpLength] + arp.Payload = data[arpLength:] + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress) + bytes, err := b.PrependBytes(size) + if err != nil { + return err + } + if opts.FixLengths { + if len(arp.SourceHwAddress) != len(arp.DstHwAddress) { + return errors.New("mismatched hardware address sizes") + } + arp.HwAddressSize = uint8(len(arp.SourceHwAddress)) + if len(arp.SourceProtAddress) != len(arp.DstProtAddress) { + return errors.New("mismatched prot address sizes") + } + arp.ProtAddressSize = uint8(len(arp.SourceProtAddress)) + } + binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType)) + binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol)) + bytes[4] = arp.HwAddressSize + bytes[5] = arp.ProtAddressSize + binary.BigEndian.PutUint16(bytes[6:], arp.Operation) + start := 8 + for _, addr := range [][]byte{ + arp.SourceHwAddress, + arp.SourceProtAddress, + arp.DstHwAddress, + arp.DstProtAddress, + } { + copy(bytes[start:], addr) + start += len(addr) + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (arp *ARP) CanDecode() gopacket.LayerClass { + return LayerTypeARP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (arp *ARP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeARP(data []byte, p gopacket.PacketBuilder) error { + + arp := &ARP{} + return decodingLayerDecoder(arp, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/base.go b/vendor/github.com/google/gopacket/layers/base.go new file mode 100644 index 00000000..cd59b467 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/base.go @@ -0,0 +1,52 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" +) + +// BaseLayer is a convenience struct which implements the LayerData and +// LayerPayload functions of the Layer interface. +type BaseLayer struct { + // Contents is the set of bytes that make up this layer. IE: for an + // Ethernet packet, this would be the set of bytes making up the + // Ethernet frame. + Contents []byte + // Payload is the set of bytes contained by (but not part of) this + // Layer. Again, to take Ethernet as an example, this would be the + // set of bytes encapsulated by the Ethernet protocol. + Payload []byte +} + +// LayerContents returns the bytes of the packet layer. +func (b *BaseLayer) LayerContents() []byte { return b.Contents } + +// LayerPayload returns the bytes contained within the packet layer. +func (b *BaseLayer) LayerPayload() []byte { return b.Payload } + +type layerDecodingLayer interface { + gopacket.Layer + DecodeFromBytes([]byte, gopacket.DecodeFeedback) error + NextLayerType() gopacket.LayerType +} + +func decodingLayerDecoder(d layerDecodingLayer, data []byte, p gopacket.PacketBuilder) error { + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(d) + next := d.NextLayerType() + if next == gopacket.LayerTypeZero { + return nil + } + return p.NextDecoder(next) +} + +// hacky way to zero out memory... there must be a better way? +var lotsOfZeros [1024]byte diff --git a/vendor/github.com/google/gopacket/layers/base_test.go b/vendor/github.com/google/gopacket/layers/base_test.go new file mode 100644 index 00000000..4be7480b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/base_test.go @@ -0,0 +1,42 @@ +// Copyright 2012, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This file contains some test helper functions. + +package layers + +import ( + "github.com/google/gopacket" + "testing" +) + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func checkLayers(p gopacket.Packet, want []gopacket.LayerType, t *testing.T) { + layers := p.Layers() + t.Log("Checking packet layers, want", want) + for _, l := range layers { + t.Logf(" Got layer %v, %d bytes, payload of %d bytes", l.LayerType(), + len(l.LayerContents()), len(l.LayerPayload())) + } + t.Log(p) + if len(layers) != len(want) { + t.Errorf(" Number of layers mismatch: got %d want %d", len(layers), + len(want)) + return + } + for i, l := range layers { + if l.LayerType() != want[i] { + t.Errorf(" Layer %d mismatch: got %v want %v", i, l.LayerType(), + want[i]) + } + } +} diff --git a/vendor/github.com/google/gopacket/layers/cdp.go b/vendor/github.com/google/gopacket/layers/cdp.go new file mode 100644 index 00000000..d67203eb --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/cdp.go @@ -0,0 +1,651 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Enum types courtesy of... +// http://search.cpan.org/~mchapman/Net-CDP-0.09/lib/Net/CDP.pm +// https://code.google.com/p/ladvd/ +// http://anonsvn.wireshark.org/viewvc/releases/wireshark-1.8.6/epan/dissectors/packet-cdp.c + +package layers + +import ( + "encoding/binary" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// CDPTLVType is the type of each TLV value in a CiscoDiscovery packet. +type CDPTLVType uint16 + +// CDPTLVType values. +const ( + CDPTLVDevID CDPTLVType = 0x0001 + CDPTLVAddress CDPTLVType = 0x0002 + CDPTLVPortID CDPTLVType = 0x0003 + CDPTLVCapabilities CDPTLVType = 0x0004 + CDPTLVVersion CDPTLVType = 0x0005 + CDPTLVPlatform CDPTLVType = 0x0006 + CDPTLVIPPrefix CDPTLVType = 0x0007 + CDPTLVHello CDPTLVType = 0x0008 + CDPTLVVTPDomain CDPTLVType = 0x0009 + CDPTLVNativeVLAN CDPTLVType = 0x000a + CDPTLVFullDuplex CDPTLVType = 0x000b + CDPTLVVLANReply CDPTLVType = 0x000e + CDPTLVVLANQuery CDPTLVType = 0x000f + CDPTLVPower CDPTLVType = 0x0010 + CDPTLVMTU CDPTLVType = 0x0011 + CDPTLVExtendedTrust CDPTLVType = 0x0012 + CDPTLVUntrustedCOS CDPTLVType = 0x0013 + CDPTLVSysName CDPTLVType = 0x0014 + CDPTLVSysOID CDPTLVType = 0x0015 + CDPTLVMgmtAddresses CDPTLVType = 0x0016 + CDPTLVLocation CDPTLVType = 0x0017 + CDPTLVExternalPortID CDPTLVType = 0x0018 + CDPTLVPowerRequested CDPTLVType = 0x0019 + CDPTLVPowerAvailable CDPTLVType = 0x001a + CDPTLVPortUnidirectional CDPTLVType = 0x001b + CDPTLVEnergyWise CDPTLVType = 0x001d + CDPTLVSparePairPOE CDPTLVType = 0x001f +) + +// CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer. +type CiscoDiscoveryValue struct { + Type CDPTLVType + Length uint16 + Value []byte +} + +// CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol. +// See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885 +type CiscoDiscovery struct { + BaseLayer + Version byte + TTL byte + Checksum uint16 + Values []CiscoDiscoveryValue +} + +// CDPCapability is the set of capabilities advertised by a CDP device. +type CDPCapability uint32 + +// CDPCapability values. +const ( + CDPCapMaskRouter CDPCapability = 0x0001 + CDPCapMaskTBBridge CDPCapability = 0x0002 + CDPCapMaskSPBridge CDPCapability = 0x0004 + CDPCapMaskSwitch CDPCapability = 0x0008 + CDPCapMaskHost CDPCapability = 0x0010 + CDPCapMaskIGMPFilter CDPCapability = 0x0020 + CDPCapMaskRepeater CDPCapability = 0x0040 + CDPCapMaskPhone CDPCapability = 0x0080 + CDPCapMaskRemote CDPCapability = 0x0100 +) + +// CDPCapabilities represents the capabilities of a device +type CDPCapabilities struct { + L3Router bool + TBBridge bool + SPBridge bool + L2Switch bool + IsHost bool + IGMPFilter bool + L1Repeater bool + IsPhone bool + RemotelyManaged bool +} + +// CDP Power-over-Ethernet values. +const ( + CDPPoEFourWire byte = 0x01 + CDPPoEPDArch byte = 0x02 + CDPPoEPDRequest byte = 0x04 + CDPPoEPSE byte = 0x08 +) + +// CDPSparePairPoE provides information on PoE. +type CDPSparePairPoE struct { + PSEFourWire bool // Supported / Not supported + PDArchShared bool // Shared / Independent + PDRequestOn bool // On / Off + PSEOn bool // On / Off +} + +// CDPVLANDialogue encapsulates a VLAN Query/Reply +type CDPVLANDialogue struct { + ID uint8 + VLAN uint16 +} + +// CDPPowerDialogue encapsulates a Power Query/Reply +type CDPPowerDialogue struct { + ID uint16 + MgmtID uint16 + Values []uint32 +} + +// CDPLocation provides location information for a CDP device. +type CDPLocation struct { + Type uint8 // Undocumented + Location string +} + +// CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields) +type CDPHello struct { + OUI []byte + ProtocolID uint16 + ClusterMaster net.IP + Unknown1 net.IP + Version byte + SubVersion byte + Status byte + Unknown2 byte + ClusterCommander net.HardwareAddr + SwitchMAC net.HardwareAddr + Unknown3 byte + ManagementVLAN uint16 +} + +// CDPEnergyWiseSubtype is used within CDP to define TLV values. +type CDPEnergyWiseSubtype uint32 + +// CDPEnergyWiseSubtype values. +const ( + CDPEnergyWiseRole CDPEnergyWiseSubtype = 0x00000007 + CDPEnergyWiseDomain CDPEnergyWiseSubtype = 0x00000008 + CDPEnergyWiseName CDPEnergyWiseSubtype = 0x00000009 + CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017 +) + +// CDPEnergyWise is used by CDP to monitor and control power usage. +type CDPEnergyWise struct { + EncryptedData []byte + Unknown1 uint32 + SequenceNumber uint32 + ModelNumber string + Unknown2 uint16 + HardwareID string + SerialNum string + Unknown3 []byte + Role string + Domain string + Name string + ReplyUnknown1 []byte + ReplyPort []byte + ReplyAddress []byte + ReplyUnknown2 []byte + ReplyUnknown3 []byte +} + +// CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues +type CiscoDiscoveryInfo struct { + BaseLayer + CDPHello + DeviceID string + Addresses []net.IP + PortID string + Capabilities CDPCapabilities + Version string + Platform string + IPPrefixes []net.IPNet + VTPDomain string + NativeVLAN uint16 + FullDuplex bool + VLANReply CDPVLANDialogue + VLANQuery CDPVLANDialogue + PowerConsumption uint16 + MTU uint32 + ExtendedTrust uint8 + UntrustedCOS uint8 + SysName string + SysOID string + MgmtAddresses []net.IP + Location CDPLocation + PowerRequest CDPPowerDialogue + PowerAvailable CDPPowerDialogue + SparePairPoe CDPSparePairPoE + EnergyWise CDPEnergyWise + Unknown []CiscoDiscoveryValue +} + +// LayerType returns gopacket.LayerTypeCiscoDiscovery. +func (c *CiscoDiscovery) LayerType() gopacket.LayerType { + return LayerTypeCiscoDiscovery +} + +func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error { + c := &CiscoDiscovery{ + Version: data[0], + TTL: data[1], + Checksum: binary.BigEndian.Uint16(data[2:4]), + } + if c.Version != 1 && c.Version != 2 { + return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version) + } + var err error + c.Values, err = decodeCiscoDiscoveryTLVs(data[4:]) + if err != nil { + return err + } + c.Contents = data[0:4] + c.Payload = data[4:] + p.AddLayer(c) + return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo)) +} + +// LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo. +func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType { + return LayerTypeCiscoDiscoveryInfo +} + +func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err error) { + for len(data) > 0 { + val := CiscoDiscoveryValue{ + Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])), + Length: binary.BigEndian.Uint16(data[2:4]), + } + if val.Length < 4 { + err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length) + break + } + val.Value = data[4:val.Length] + values = append(values, val) + data = data[val.Length:] + } + return +} + +func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error { + var err error + info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}} + p.AddLayer(info) + values, err := decodeCiscoDiscoveryTLVs(data) + if err != nil { // Unlikely, as parent decode will fail, but better safe... + return err + } + for _, val := range values { + switch val.Type { + case CDPTLVDevID: + info.DeviceID = string(val.Value) + case CDPTLVAddress: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.Addresses, err = decodeAddresses(val.Value) + if err != nil { + return err + } + case CDPTLVPortID: + info.PortID = string(val.Value) + case CDPTLVCapabilities: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4])) + info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0) + info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0) + info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0) + info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0) + info.Capabilities.IsHost = (val&CDPCapMaskHost > 0) + info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0) + info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0) + info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0) + info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0) + case CDPTLVVersion: + info.Version = string(val.Value) + case CDPTLVPlatform: + info.Platform = string(val.Value) + case CDPTLVIPPrefix: + v := val.Value + l := len(v) + if l%5 == 0 && l >= 5 { + for len(v) > 0 { + _, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4])) + info.IPPrefixes = append(info.IPPrefixes, *ipnet) + v = v[5:] + } + } else { + return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value)) + } + case CDPTLVHello: + if err = checkCDPTLVLen(val, 32); err != nil { + return err + } + v := val.Value + info.CDPHello.OUI = v[0:3] + info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5]) + info.CDPHello.ClusterMaster = v[5:9] + info.CDPHello.Unknown1 = v[9:13] + info.CDPHello.Version = v[13] + info.CDPHello.SubVersion = v[14] + info.CDPHello.Status = v[15] + info.CDPHello.Unknown2 = v[16] + info.CDPHello.ClusterCommander = v[17:23] + info.CDPHello.SwitchMAC = v[23:29] + info.CDPHello.Unknown3 = v[29] + info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32]) + case CDPTLVVTPDomain: + info.VTPDomain = string(val.Value) + case CDPTLVNativeVLAN: + if err = checkCDPTLVLen(val, 2); err != nil { + return err + } + info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2]) + case CDPTLVFullDuplex: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + info.FullDuplex = (val.Value[0] == 1) + case CDPTLVVLANReply: + if err = checkCDPTLVLen(val, 3); err != nil { + return err + } + info.VLANReply.ID = uint8(val.Value[0]) + info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3]) + case CDPTLVVLANQuery: + if err = checkCDPTLVLen(val, 3); err != nil { + return err + } + info.VLANQuery.ID = uint8(val.Value[0]) + info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3]) + case CDPTLVPower: + if err = checkCDPTLVLen(val, 2); err != nil { + return err + } + info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2]) + case CDPTLVMTU: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.MTU = binary.BigEndian.Uint32(val.Value[0:4]) + case CDPTLVExtendedTrust: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + info.ExtendedTrust = uint8(val.Value[0]) + case CDPTLVUntrustedCOS: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + info.UntrustedCOS = uint8(val.Value[0]) + case CDPTLVSysName: + info.SysName = string(val.Value) + case CDPTLVSysOID: + info.SysOID = string(val.Value) + case CDPTLVMgmtAddresses: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.MgmtAddresses, err = decodeAddresses(val.Value) + if err != nil { + return err + } + case CDPTLVLocation: + if err = checkCDPTLVLen(val, 2); err != nil { + return err + } + info.Location.Type = uint8(val.Value[0]) + info.Location.Location = string(val.Value[1:]) + + // case CDPTLVLExternalPortID: + // Undocumented + case CDPTLVPowerRequested: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2]) + info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4]) + for n := 4; n < len(val.Value); n += 4 { + info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4])) + } + case CDPTLVPowerAvailable: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2]) + info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4]) + for n := 4; n < len(val.Value); n += 4 { + info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4])) + } + // case CDPTLVPortUnidirectional + // Undocumented + case CDPTLVEnergyWise: + if err = checkCDPTLVLen(val, 72); err != nil { + return err + } + info.EnergyWise.EncryptedData = val.Value[0:20] + info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24]) + info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28]) + info.EnergyWise.ModelNumber = string(val.Value[28:44]) + info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46]) + info.EnergyWise.HardwareID = string(val.Value[46:49]) + info.EnergyWise.SerialNum = string(val.Value[49:60]) + info.EnergyWise.Unknown3 = val.Value[60:68] + tlvLen := binary.BigEndian.Uint16(val.Value[68:70]) + tlvNum := binary.BigEndian.Uint16(val.Value[70:72]) + data := val.Value[72:] + if len(data) < int(tlvLen) { + return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data)) + } + numSeen := 0 + for len(data) > 8 { + numSeen++ + if numSeen > int(tlvNum) { // Too many TLV's ? + return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen) + } + tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4])) + tLen := int(binary.BigEndian.Uint32(data[4:8])) + if tLen > len(data)-8 { + return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8) + } + data = data[8:] + switch tType { + case CDPEnergyWiseRole: + info.EnergyWise.Role = string(data[:]) + case CDPEnergyWiseDomain: + info.EnergyWise.Domain = string(data[:]) + case CDPEnergyWiseName: + info.EnergyWise.Name = string(data[:]) + case CDPEnergyWiseReplyTo: + if len(data) >= 18 { + info.EnergyWise.ReplyUnknown1 = data[0:2] + info.EnergyWise.ReplyPort = data[2:4] + info.EnergyWise.ReplyAddress = data[4:8] + info.EnergyWise.ReplyUnknown2 = data[8:10] + info.EnergyWise.ReplyUnknown3 = data[10:14] + } + } + data = data[tLen:] + } + case CDPTLVSparePairPOE: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + v := val.Value[0] + info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0) + info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0) + info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0) + info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0) + default: + info.Unknown = append(info.Unknown, val) + } + } + return nil +} + +// CDP Protocol Types +const ( + CDPProtocolTypeNLPID byte = 1 + CDPProtocolType802_2 byte = 2 +) + +// CDPAddressType is used to define TLV values within CDP addresses. +type CDPAddressType uint64 + +// CDP Address types. +const ( + CDPAddressTypeCLNP CDPAddressType = 0x81 + CDPAddressTypeIPV4 CDPAddressType = 0xcc + CDPAddressTypeIPV6 CDPAddressType = 0xaaaa030000000800 + CDPAddressTypeDECNET CDPAddressType = 0xaaaa030000006003 + CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b + CDPAddressTypeIPX CDPAddressType = 0xaaaa030000008137 + CDPAddressTypeVINES CDPAddressType = 0xaaaa0300000080c4 + CDPAddressTypeXNS CDPAddressType = 0xaaaa030000000600 + CDPAddressTypeAPOLLO CDPAddressType = 0xaaaa030000008019 +) + +func decodeAddresses(v []byte) (addresses []net.IP, err error) { + numaddr := int(binary.BigEndian.Uint32(v[0:4])) + if numaddr < 1 { + return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr) + } + v = v[4:] + if len(v) < numaddr*8 { + return nil, fmt.Errorf("Invalid Address TLV length %d", len(v)) + } + for i := 0; i < numaddr; i++ { + prottype := v[0] + if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type + return nil, fmt.Errorf("Invalid Address Protocol %d", prottype) + } + protlen := int(v[1]) + if (prottype == CDPProtocolTypeNLPID && protlen != 1) || + (prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length + return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen) + } + plen := make([]byte, 8) + copy(plen[8-protlen:], v[2:2+protlen]) + protocol := CDPAddressType(binary.BigEndian.Uint64(plen)) + v = v[2+protlen:] + addrlen := binary.BigEndian.Uint16(v[0:2]) + ab := v[2 : 2+addrlen] + if protocol == CDPAddressTypeIPV4 && addrlen == 4 { + addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3])) + } else if protocol == CDPAddressTypeIPV6 && addrlen == 16 { + addresses = append(addresses, net.IP(ab)) + } else { + // only handle IPV4 & IPV6 for now + } + v = v[2+addrlen:] + if len(v) < 8 { + break + } + } + return +} + +func (t CDPTLVType) String() (s string) { + switch t { + case CDPTLVDevID: + s = "Device ID" + case CDPTLVAddress: + s = "Addresses" + case CDPTLVPortID: + s = "Port ID" + case CDPTLVCapabilities: + s = "Capabilities" + case CDPTLVVersion: + s = "Software Version" + case CDPTLVPlatform: + s = "Platform" + case CDPTLVIPPrefix: + s = "IP Prefix" + case CDPTLVHello: + s = "Protocol Hello" + case CDPTLVVTPDomain: + s = "VTP Management Domain" + case CDPTLVNativeVLAN: + s = "Native VLAN" + case CDPTLVFullDuplex: + s = "Full Duplex" + case CDPTLVVLANReply: + s = "VoIP VLAN Reply" + case CDPTLVVLANQuery: + s = "VLANQuery" + case CDPTLVPower: + s = "Power consumption" + case CDPTLVMTU: + s = "MTU" + case CDPTLVExtendedTrust: + s = "Extended Trust Bitmap" + case CDPTLVUntrustedCOS: + s = "Untrusted Port CoS" + case CDPTLVSysName: + s = "System Name" + case CDPTLVSysOID: + s = "System OID" + case CDPTLVMgmtAddresses: + s = "Management Addresses" + case CDPTLVLocation: + s = "Location" + case CDPTLVExternalPortID: + s = "External Port ID" + case CDPTLVPowerRequested: + s = "Power Requested" + case CDPTLVPowerAvailable: + s = "Power Available" + case CDPTLVPortUnidirectional: + s = "Port Unidirectional" + case CDPTLVEnergyWise: + s = "Energy Wise" + case CDPTLVSparePairPOE: + s = "Spare Pair POE" + default: + s = "Unknown" + } + return +} + +func (a CDPAddressType) String() (s string) { + switch a { + case CDPAddressTypeCLNP: + s = "Connectionless Network Protocol" + case CDPAddressTypeIPV4: + s = "IPv4" + case CDPAddressTypeIPV6: + s = "IPv6" + case CDPAddressTypeDECNET: + s = "DECnet Phase IV" + case CDPAddressTypeAPPLETALK: + s = "Apple Talk" + case CDPAddressTypeIPX: + s = "Novell IPX" + case CDPAddressTypeVINES: + s = "Banyan VINES" + case CDPAddressTypeXNS: + s = "Xerox Network Systems" + case CDPAddressTypeAPOLLO: + s = "Apollo" + default: + s = "Unknown" + } + return +} + +func (t CDPEnergyWiseSubtype) String() (s string) { + switch t { + case CDPEnergyWiseRole: + s = "Role" + case CDPEnergyWiseDomain: + s = "Domain" + case CDPEnergyWiseName: + s = "Name" + case CDPEnergyWiseReplyTo: + s = "ReplyTo" + default: + s = "Unknown" + } + return +} + +func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) { + if len(v.Value) < l { + err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value)) + } + return +} diff --git a/vendor/github.com/google/gopacket/layers/ctp.go b/vendor/github.com/google/gopacket/layers/ctp.go new file mode 100644 index 00000000..82875845 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ctp.go @@ -0,0 +1,109 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +// EthernetCTPFunction is the function code used by the EthernetCTP protocol to identify each +// EthernetCTP layer. +type EthernetCTPFunction uint16 + +// EthernetCTPFunction values. +const ( + EthernetCTPFunctionReply EthernetCTPFunction = 1 + EthernetCTPFunctionForwardData EthernetCTPFunction = 2 +) + +// EthernetCTP implements the EthernetCTP protocol, see http://www.mit.edu/people/jhawk/ctp.html. +// We split EthernetCTP up into the top-level EthernetCTP layer, followed by zero or more +// EthernetCTPForwardData layers, followed by a final EthernetCTPReply layer. +type EthernetCTP struct { + BaseLayer + SkipCount uint16 +} + +// LayerType returns gopacket.LayerTypeEthernetCTP. +func (c *EthernetCTP) LayerType() gopacket.LayerType { + return LayerTypeEthernetCTP +} + +// EthernetCTPForwardData is the ForwardData layer inside EthernetCTP. See EthernetCTP's docs for more +// details. +type EthernetCTPForwardData struct { + BaseLayer + Function EthernetCTPFunction + ForwardAddress []byte +} + +// LayerType returns gopacket.LayerTypeEthernetCTPForwardData. +func (c *EthernetCTPForwardData) LayerType() gopacket.LayerType { + return LayerTypeEthernetCTPForwardData +} + +// ForwardEndpoint returns the EthernetCTPForwardData ForwardAddress as an endpoint. +func (c *EthernetCTPForwardData) ForwardEndpoint() gopacket.Endpoint { + return gopacket.NewEndpoint(EndpointMAC, c.ForwardAddress) +} + +// EthernetCTPReply is the Reply layer inside EthernetCTP. See EthernetCTP's docs for more details. +type EthernetCTPReply struct { + BaseLayer + Function EthernetCTPFunction + ReceiptNumber uint16 + Data []byte +} + +// LayerType returns gopacket.LayerTypeEthernetCTPReply. +func (c *EthernetCTPReply) LayerType() gopacket.LayerType { + return LayerTypeEthernetCTPReply +} + +// Payload returns the EthernetCTP reply's Data bytes. +func (c *EthernetCTPReply) Payload() []byte { return c.Data } + +func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error { + c := &EthernetCTP{ + SkipCount: binary.LittleEndian.Uint16(data[:2]), + BaseLayer: BaseLayer{data[:2], data[2:]}, + } + if c.SkipCount%2 != 0 { + return fmt.Errorf("EthernetCTP skip count is odd: %d", c.SkipCount) + } + p.AddLayer(c) + return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType)) +} + +// decodeEthernetCTPFromFunctionType reads in the first 2 bytes to determine the EthernetCTP +// layer type to decode next, then decodes based on that. +func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) error { + function := EthernetCTPFunction(binary.LittleEndian.Uint16(data[:2])) + switch function { + case EthernetCTPFunctionReply: + reply := &EthernetCTPReply{ + Function: function, + ReceiptNumber: binary.LittleEndian.Uint16(data[2:4]), + Data: data[4:], + BaseLayer: BaseLayer{data, nil}, + } + p.AddLayer(reply) + p.SetApplicationLayer(reply) + return nil + case EthernetCTPFunctionForwardData: + forward := &EthernetCTPForwardData{ + Function: function, + ForwardAddress: data[2:8], + BaseLayer: BaseLayer{data[:8], data[8:]}, + } + p.AddLayer(forward) + return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType)) + } + return fmt.Errorf("Unknown EthernetCTP function type %v", function) +} diff --git a/vendor/github.com/google/gopacket/layers/decode_test.go b/vendor/github.com/google/gopacket/layers/decode_test.go new file mode 100644 index 00000000..ecfbff7a --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/decode_test.go @@ -0,0 +1,1234 @@ +// Copyright 2012, Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "encoding/hex" + "fmt" + "net" + "reflect" + "strings" + "testing" + + "github.com/google/gopacket" + "github.com/google/gopacket/bytediff" +) + +var testSimpleTCPPacket = []byte{ + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, + 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06, + 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7, + 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18, + 0x00, 0x73, 0x9a, 0x8f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77, + 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, + 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, + 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61, + 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, + 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20, + 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, + 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, + 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54, + 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, + 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31, + 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20, + 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, + 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, + 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, + 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, + 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70, + 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, + 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55, + 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73, + 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, + 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30, + 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a, + 0x0d, 0x0a, +} + +var testDecodeOptions = gopacket.DecodeOptions{ + SkipDecodeRecovery: true, +} + +// Benchmarks for actual gopacket code + +func BenchmarkLayerClassSliceContains(b *testing.B) { + lc := gopacket.NewLayerClassSlice([]gopacket.LayerType{LayerTypeTCP, LayerTypeEthernet}) + for i := 0; i < b.N; i++ { + _ = lc.Contains(LayerTypeTCP) + } +} + +func BenchmarkLayerClassMapContains(b *testing.B) { + lc := gopacket.NewLayerClassMap([]gopacket.LayerType{LayerTypeTCP, LayerTypeEthernet}) + for i := 0; i < b.N; i++ { + _ = lc.Contains(LayerTypeTCP) + } +} + +func BenchmarkLazyNoCopyEthLayer(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}).Layer(LayerTypeEthernet) + } +} + +func BenchmarkLazyNoCopyIPLayer(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}).Layer(LayerTypeIPv4) + } +} + +func BenchmarkLazyNoCopyTCPLayer(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}).Layer(LayerTypeTCP) + } +} + +func BenchmarkLazyNoCopyAllLayers(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}).Layers() + } +} + +func BenchmarkDefault(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.Default) + } +} + +func getSerializeLayers() []gopacket.SerializableLayer { + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, testDecodeOptions) + slayers := []gopacket.SerializableLayer{} + for _, l := range p.Layers() { + slayers = append(slayers, l.(gopacket.SerializableLayer)) + } + p.Layer(LayerTypeTCP).(*TCP).SetNetworkLayerForChecksum( + p.NetworkLayer()) + return slayers +} + +func BenchmarkSerializeTcpNoOptions(b *testing.B) { + slayers := getSerializeLayers() + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} + for i := 0; i < b.N; i++ { + gopacket.SerializeLayers(buf, opts, slayers...) + } +} + +func BenchmarkSerializeTcpFixLengths(b *testing.B) { + slayers := getSerializeLayers() + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + for i := 0; i < b.N; i++ { + gopacket.SerializeLayers(buf, opts, slayers...) + } +} + +func BenchmarkSerializeTcpComputeChecksums(b *testing.B) { + slayers := getSerializeLayers() + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ComputeChecksums: true} + for i := 0; i < b.N; i++ { + gopacket.SerializeLayers(buf, opts, slayers...) + } +} + +func BenchmarkSerializeTcpFixLengthsComputeChecksums(b *testing.B) { + slayers := getSerializeLayers() + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true} + for i := 0; i < b.N; i++ { + gopacket.SerializeLayers(buf, opts, slayers...) + } +} + +func BenchmarkLazy(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.Lazy) + } +} + +func BenchmarkNoCopy(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.NoCopy) + } +} + +func BenchmarkLazyNoCopy(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) + } +} + +func BenchmarkKnownStack(b *testing.B) { + stack := []gopacket.DecodingLayer{&Ethernet{}, &IPv4{}, &TCP{}, &gopacket.Payload{}} + nf := gopacket.NilDecodeFeedback + for i := 0; i < b.N; i++ { + data := testSimpleTCPPacket[:] + for _, d := range stack { + _ = d.DecodeFromBytes(data, nf) + data = d.LayerPayload() + } + } +} + +func BenchmarkDecodingLayerParserIgnorePanic(b *testing.B) { + decoded := make([]gopacket.LayerType, 0, 20) + dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet, &Ethernet{}, &IPv4{}, &TCP{}, &gopacket.Payload{}) + dlp.IgnorePanic = true + for i := 0; i < b.N; i++ { + dlp.DecodeLayers(testSimpleTCPPacket, &decoded) + } +} + +func BenchmarkDecodingLayerParserHandlePanic(b *testing.B) { + decoded := make([]gopacket.LayerType, 0, 20) + dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet, &Ethernet{}, &IPv4{}, &TCP{}, &gopacket.Payload{}) + dlp.IgnorePanic = false + for i := 0; i < b.N; i++ { + dlp.DecodeLayers(testSimpleTCPPacket, &decoded) + } +} + +func BenchmarkAlloc(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = &TCP{} + } +} + +func BenchmarkFlow(b *testing.B) { + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) + net := p.NetworkLayer() + for i := 0; i < b.N; i++ { + net.NetworkFlow() + } +} + +func BenchmarkEndpoints(b *testing.B) { + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) + flow := p.NetworkLayer().NetworkFlow() + for i := 0; i < b.N; i++ { + flow.Endpoints() + } +} + +func BenchmarkTCPLayerFromDecodedPacket(b *testing.B) { + b.StopTimer() + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, testDecodeOptions) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = p.Layer(LayerTypeTCP) + } +} + +func BenchmarkTCPLayerClassFromDecodedPacket(b *testing.B) { + b.StopTimer() + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, testDecodeOptions) + lc := gopacket.NewLayerClass([]gopacket.LayerType{LayerTypeTCP}) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = p.LayerClass(lc) + } +} + +func BenchmarkTCPTransportLayerFromDecodedPacket(b *testing.B) { + b.StopTimer() + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, testDecodeOptions) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = p.TransportLayer() + } +} + +func testDecoder([]byte, gopacket.PacketBuilder) error { + return nil +} + +func BenchmarkDecodeFuncCallOverheadDirectCall(b *testing.B) { + var data []byte + var pb gopacket.PacketBuilder + for i := 0; i < b.N; i++ { + _ = testDecoder(data, pb) + } +} + +func BenchmarkDecodeFuncCallOverheadDecoderCall(b *testing.B) { + d := gopacket.DecodeFunc(testDecoder) + var data []byte + var pb gopacket.PacketBuilder + for i := 0; i < b.N; i++ { + _ = d.Decode(data, pb) + } +} + +func BenchmarkDecodeFuncCallOverheadArrayCall(b *testing.B) { + EthernetTypeMetadata[1] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(testDecoder)} + d := EthernetType(1) + var data []byte + var pb gopacket.PacketBuilder + for i := 0; i < b.N; i++ { + _ = d.Decode(data, pb) + } +} + +func BenchmarkFmtVerboseString(b *testing.B) { + b.StopTimer() + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, testDecodeOptions) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = fmt.Sprintf("%#v", p) + } +} + +func BenchmarkPacketString(b *testing.B) { + b.StopTimer() + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, testDecodeOptions) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = p.String() + } +} + +func BenchmarkPacketDumpString(b *testing.B) { + b.StopTimer() + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, testDecodeOptions) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = p.String() + } +} + +// TestFlowMapKey makes sure a flow and an endpoint can be used as map keys. +func TestFlowMapKey(t *testing.T) { + _ = map[gopacket.Flow]bool{} + _ = map[gopacket.Endpoint]bool{} + _ = map[[2]gopacket.Flow]bool{} +} + +func TestDecodeSimpleTCPPacket(t *testing.T) { + equal := func(desc, want string, got fmt.Stringer) { + if want != got.String() { + t.Errorf("%s: got %q want %q", desc, got.String(), want) + } + } + p := gopacket.NewPacket(testSimpleTCPPacket, LinkTypeEthernet, gopacket.DecodeOptions{Lazy: true, NoCopy: true}) + if eth := p.LinkLayer(); eth == nil { + t.Error("No ethernet layer found") + } else { + equal("Eth Src", "bc:30:5b:e8:d3:49", eth.LinkFlow().Src()) + equal("Eth Dst", "00:00:0c:9f:f0:20", eth.LinkFlow().Dst()) + } + if net := p.NetworkLayer(); net == nil { + t.Error("No net layer found") + } else if ip, ok := net.(*IPv4); !ok { + t.Error("Net layer is not IP layer") + } else { + equal("IP Src", "172.17.81.73", net.NetworkFlow().Src()) + equal("IP Dst", "173.222.254.225", net.NetworkFlow().Dst()) + want := &IPv4{ + BaseLayer: BaseLayer{testSimpleTCPPacket[14:34], testSimpleTCPPacket[34:]}, + Version: 4, + IHL: 5, + TOS: 0, + Length: 420, + Id: 14815, + Flags: 0x02, + FragOffset: 0, + TTL: 64, + Protocol: 6, + Checksum: 0x555A, + SrcIP: []byte{172, 17, 81, 73}, + DstIP: []byte{173, 222, 254, 225}, + } + if !reflect.DeepEqual(ip, want) { + t.Errorf("IP layer mismatch, \ngot %#v\nwant %#v\n", ip, want) + } + } + if trans := p.TransportLayer(); trans == nil { + t.Error("No transport layer found") + } else if tcp, ok := trans.(*TCP); !ok { + t.Error("Transport layer is not TCP layer") + } else { + equal("TCP Src", "50679", trans.TransportFlow().Src()) + equal("TCP Dst", "80", trans.TransportFlow().Dst()) + want := &TCP{ + BaseLayer: BaseLayer{testSimpleTCPPacket[34:66], testSimpleTCPPacket[66:]}, + SrcPort: 50679, + DstPort: 80, + Seq: 0xc57e0e48, + Ack: 0x49074232, + DataOffset: 8, + ACK: true, + PSH: true, + Window: 0x73, + Checksum: 0x9a8f, + Urgent: 0, + sPort: []byte{0xc5, 0xf7}, + dPort: []byte{0x0, 0x50}, + Options: []TCPOption{ + TCPOption{ + OptionType: 0x1, + OptionLength: 0x1, + }, + TCPOption{ + OptionType: 0x1, + OptionLength: 0x1, + }, + TCPOption{ + OptionType: 0x8, + OptionLength: 0xa, + OptionData: []byte{0x3, 0x77, 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a}, + }, + }, + opts: [4]TCPOption{ + TCPOption{ + OptionType: 0x1, + OptionLength: 0x1, + }, + TCPOption{ + OptionType: 0x1, + OptionLength: 0x1, + }, + TCPOption{ + OptionType: 0x8, + OptionLength: 0xa, + OptionData: []byte{0x3, 0x77, 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a}, + }, + TCPOption{}, + }, + } + if !reflect.DeepEqual(tcp, want) { + t.Errorf("TCP layer mismatch\ngot %#v\nwant %#v", tcp, want) + } + } + if payload, ok := p.Layer(gopacket.LayerTypePayload).(*gopacket.Payload); payload == nil || !ok { + t.Error("No payload layer found") + } else { + if string(payload.Payload()) != "GET / HTTP/1.1\r\nHost: www.fish.com\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n\r\n" { + t.Error("--- Payload STRING ---\n", string(payload.Payload()), "\n--- Payload BYTES ---\n", payload.Payload()) + } + } + + // Test re-serialization. + testSerialization(t, p, testSimpleTCPPacket) +} + +type canSetNetLayer interface { + SetNetworkLayerForChecksum(gopacket.NetworkLayer) error +} + +func testSerialization(t *testing.T, p gopacket.Packet, data []byte) { + for _, opts := range []gopacket.SerializeOptions{ + gopacket.SerializeOptions{}, + gopacket.SerializeOptions{FixLengths: true}, + gopacket.SerializeOptions{ComputeChecksums: true}, + gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}, + } { + testSerializationWithOpts(t, p, data, opts) + } +} + +func testSerializationWithOpts(t *testing.T, p gopacket.Packet, data []byte, opts gopacket.SerializeOptions) { + // Test re-serialization. + slayers := []gopacket.SerializableLayer{} + for _, l := range p.Layers() { + slayers = append(slayers, l.(gopacket.SerializableLayer)) + if h, ok := l.(canSetNetLayer); ok { + if err := h.SetNetworkLayerForChecksum(p.NetworkLayer()); err != nil { + t.Fatal("can't set network layer:", err) + } + } + } + buf := gopacket.NewSerializeBuffer() + err := gopacket.SerializeLayers(buf, opts, slayers...) + if err != nil { + t.Errorf("unable to reserialize layers with opts %#v: %v", opts, err) + } else if !bytes.Equal(buf.Bytes(), data) { + t.Errorf("serialization failure with opts %#v:\n---want---\n%v\n---got---\n%v\nBASH-colorized diff, want->got:\n%v\n\n---PACKET---\n%v", + opts, hex.Dump(data), hex.Dump(buf.Bytes()), bytediff.BashOutput.String(bytediff.Diff(data, buf.Bytes())), p) + } +} + +// Makes sure packet payload doesn't display the 6 trailing null of this packet +// as part of the payload. They're actually the ethernet trailer. +func TestDecodeSmallTCPPacketHasEmptyPayload(t *testing.T) { + smallPacket := []byte{ + 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 0xb8, 0xac, 0x6f, 0x92, 0xd5, 0xbf, + 0x08, 0x00, 0x45, 0x00, 0x00, 0x28, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06, + 0x3f, 0x9f, 0xac, 0x11, 0x51, 0xc5, 0xac, 0x11, 0x51, 0x49, 0x00, 0x63, + 0x9a, 0xef, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xc1, 0x27, 0x83, 0x50, 0x14, + 0x00, 0x00, 0xc3, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + p := gopacket.NewPacket(smallPacket, LinkTypeEthernet, testDecodeOptions) + + if payload := p.Layer(gopacket.LayerTypePayload); payload != nil { + t.Error("Payload found for empty TCP packet") + } + + testSerialization(t, p, smallPacket) +} + +func TestDecodeVLANPacket(t *testing.T) { + p := gopacket.NewPacket( + []byte{ + 0x00, 0x10, 0xdb, 0xff, 0x10, 0x00, 0x00, 0x15, 0x2c, 0x9d, 0xcc, 0x00, + 0x81, 0x00, 0x01, 0xf7, 0x08, 0x00, 0x45, 0x00, 0x00, 0x28, 0x29, 0x8d, + 0x40, 0x00, 0x7d, 0x06, 0x83, 0xa0, 0xac, 0x1b, 0xca, 0x8e, 0x45, 0x16, + 0x94, 0xe2, 0xd4, 0x0a, 0x00, 0x50, 0xdf, 0xab, 0x9c, 0xc6, 0xcd, 0x1e, + 0xe5, 0xd1, 0x50, 0x10, 0x01, 0x00, 0x5a, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }, LinkTypeEthernet, testDecodeOptions) + if err := p.ErrorLayer(); err != nil { + t.Error("Error while parsing vlan packet:", err) + } + if vlan := p.Layer(LayerTypeDot1Q); vlan == nil { + t.Error("Didn't detect vlan") + } else if _, ok := vlan.(*Dot1Q); !ok { + t.Error("LayerTypeDot1Q layer is not a Dot1Q object") + } + for i, l := range p.Layers() { + t.Logf("Layer %d: %#v", i, l) + } + want := []gopacket.LayerType{LayerTypeEthernet, LayerTypeDot1Q, LayerTypeIPv4, LayerTypeTCP} + checkLayers(p, want, t) +} + +func TestDecodeSCTPPackets(t *testing.T) { + sctpPackets := [][]byte{ + []byte{ // INIT + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x1f, 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x08, 0x00, 0x45, 0x02, + 0x00, 0x44, 0x00, 0x00, 0x40, 0x00, 0x40, 0x84, 0xc4, 0x22, 0xac, 0x1d, 0x14, 0x0f, 0xac, 0x19, + 0x09, 0xcc, 0x27, 0x0f, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x19, 0x6b, 0x0b, 0x40, 0x01, 0x00, + 0x00, 0x24, 0xb6, 0x96, 0xb0, 0x9e, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xdb, 0x85, + 0x60, 0x23, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04, 0xc0, 0x00, + 0x00, 0x04, + }, []byte{ // INIT ACK + 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x76, 0x40, 0x08, 0x00, 0x45, 0x20, + 0x01, 0x24, 0x00, 0x00, 0x40, 0x00, 0x36, 0x84, 0xcd, 0x24, 0xac, 0x19, 0x09, 0xcc, 0xac, 0x1d, + 0x14, 0x0f, 0x22, 0xb8, 0x27, 0x0f, 0xb6, 0x96, 0xb0, 0x9e, 0x4b, 0xab, 0x40, 0x9a, 0x02, 0x00, + 0x01, 0x04, 0x32, 0x80, 0xfb, 0x42, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x85, 0x98, + 0xb1, 0x26, 0x00, 0x07, 0x00, 0xe8, 0xd3, 0x08, 0xce, 0xe2, 0x52, 0x95, 0xcc, 0x09, 0xa1, 0x4c, + 0x6f, 0xa7, 0x9e, 0xba, 0x03, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfb, 0x80, 0x32, 0x9e, 0xb0, + 0x96, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x46, 0xc2, 0x50, 0x00, 0x00, + 0x00, 0x00, 0x5e, 0x25, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x26, 0xb1, + 0x98, 0x85, 0x02, 0x00, 0x27, 0x0f, 0xac, 0x1d, 0x14, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x22, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x24, 0x6a, 0x72, 0x5c, 0x1c, 0x3c, 0xaa, + 0x7a, 0xcd, 0xd3, 0x8f, 0x52, 0x78, 0x7c, 0x77, 0xfd, 0x46, 0xbd, 0x72, 0x82, 0xc1, 0x1f, 0x70, + 0x44, 0xcc, 0xc7, 0x9b, 0x9b, 0x7b, 0x13, 0x54, 0x3f, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0xb6, 0x96, + 0xb0, 0x9e, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xdb, 0x85, 0x60, 0x23, 0x00, 0x0c, + 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04, 0xc0, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04, 0xc0, 0x00, + 0x00, 0x04, + }, []byte{ // COOKIE ECHO, DATA + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x1f, 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x08, 0x00, 0x45, 0x02, + 0x01, 0x20, 0x00, 0x00, 0x40, 0x00, 0x40, 0x84, 0xc3, 0x46, 0xac, 0x1d, 0x14, 0x0f, 0xac, 0x19, + 0x09, 0xcc, 0x27, 0x0f, 0x22, 0xb8, 0x32, 0x80, 0xfb, 0x42, 0x01, 0xf9, 0xf3, 0xa9, 0x0a, 0x00, + 0x00, 0xe8, 0xd3, 0x08, 0xce, 0xe2, 0x52, 0x95, 0xcc, 0x09, 0xa1, 0x4c, 0x6f, 0xa7, 0x9e, 0xba, + 0x03, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfb, 0x80, 0x32, 0x9e, 0xb0, 0x96, 0xb6, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x46, 0xc2, 0x50, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x25, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x26, 0xb1, 0x98, 0x85, 0x02, 0x00, + 0x27, 0x0f, 0xac, 0x1d, 0x14, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x22, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x02, 0x00, 0x24, 0x6a, 0x72, 0x5c, 0x1c, 0x3c, 0xaa, 0x7a, 0xcd, 0xd3, 0x8f, + 0x52, 0x78, 0x7c, 0x77, 0xfd, 0x46, 0xbd, 0x72, 0x82, 0xc1, 0x1f, 0x70, 0x44, 0xcc, 0xc7, 0x9b, + 0x9b, 0x7b, 0x13, 0x54, 0x3f, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0xb6, 0x96, 0xb0, 0x9e, 0x00, 0x01, + 0xc0, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xdb, 0x85, 0x60, 0x23, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x05, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x04, 0xc0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0xdb, 0x85, 0x60, 0x23, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x21, 0x0a, 0x00, 0x00, 0x00, + }, []byte{ // COOKIE ACK, SACK + 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x76, 0x40, 0x08, 0x00, 0x45, 0x20, + 0x00, 0x34, 0x00, 0x00, 0x40, 0x00, 0x36, 0x84, 0xce, 0x14, 0xac, 0x19, 0x09, 0xcc, 0xac, 0x1d, + 0x14, 0x0f, 0x22, 0xb8, 0x27, 0x0f, 0xb6, 0x96, 0xb0, 0x9e, 0xed, 0x64, 0x30, 0x98, 0x0b, 0x00, + 0x00, 0x04, 0x03, 0x00, 0x00, 0x10, 0xdb, 0x85, 0x60, 0x23, 0x00, 0x00, 0xf3, 0xfa, 0x00, 0x00, + 0x00, 0x00, + }, []byte{ // DATA + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x1f, 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x08, 0x00, 0x45, 0x02, + 0x00, 0x3c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x84, 0xc4, 0x2a, 0xac, 0x1d, 0x14, 0x0f, 0xac, 0x19, + 0x09, 0xcc, 0x27, 0x0f, 0x22, 0xb8, 0x32, 0x80, 0xfb, 0x42, 0xa1, 0xe3, 0xb2, 0x31, 0x00, 0x03, + 0x00, 0x19, 0xdb, 0x85, 0x60, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x62, 0x69, + 0x7a, 0x7a, 0x6c, 0x65, 0x21, 0x0a, 0x00, 0x00, 0x00, 0x00, + }, []byte{ // SACK + 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x76, 0x40, 0x08, 0x00, 0x45, 0x20, + 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x36, 0x84, 0xce, 0x18, 0xac, 0x19, 0x09, 0xcc, 0xac, 0x1d, + 0x14, 0x0f, 0x22, 0xb8, 0x27, 0x0f, 0xb6, 0x96, 0xb0, 0x9e, 0xfa, 0x49, 0x94, 0x3a, 0x03, 0x00, + 0x00, 0x10, 0xdb, 0x85, 0x60, 0x24, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, + }, []byte{ // SHUTDOWN + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x1f, 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x08, 0x00, 0x45, 0x02, + 0x00, 0x28, 0x00, 0x00, 0x40, 0x00, 0x40, 0x84, 0xc4, 0x3e, 0xac, 0x1d, 0x14, 0x0f, 0xac, 0x19, + 0x09, 0xcc, 0x27, 0x0f, 0x22, 0xb8, 0x32, 0x80, 0xfb, 0x42, 0x3f, 0x29, 0x59, 0x23, 0x07, 0x00, + 0x00, 0x08, 0x85, 0x98, 0xb1, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, []byte{ // SHUTDOWN ACK + 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x76, 0x40, 0x08, 0x00, 0x45, 0x20, + 0x00, 0x24, 0x00, 0x00, 0x40, 0x00, 0x36, 0x84, 0xce, 0x24, 0xac, 0x19, 0x09, 0xcc, 0xac, 0x1d, + 0x14, 0x0f, 0x22, 0xb8, 0x27, 0x0f, 0xb6, 0x96, 0xb0, 0x9e, 0xb2, 0xc8, 0x99, 0x24, 0x08, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, []byte{ // SHUTDOWN COMPLETE + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x1f, 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x08, 0x00, 0x45, 0x02, + 0x00, 0x24, 0x00, 0x00, 0x40, 0x00, 0x40, 0x84, 0xc4, 0x42, 0xac, 0x1d, 0x14, 0x0f, 0xac, 0x19, + 0x09, 0xcc, 0x27, 0x0f, 0x22, 0xb8, 0x32, 0x80, 0xfb, 0x42, 0xa8, 0xd1, 0x86, 0x85, 0x0e, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }} + wantLayers := [][]gopacket.LayerType{ + []gopacket.LayerType{LayerTypeSCTPInit}, + []gopacket.LayerType{LayerTypeSCTPInitAck}, + []gopacket.LayerType{LayerTypeSCTPCookieEcho, LayerTypeSCTPData, gopacket.LayerTypePayload}, + []gopacket.LayerType{LayerTypeSCTPCookieAck, LayerTypeSCTPSack}, + []gopacket.LayerType{LayerTypeSCTPData, gopacket.LayerTypePayload}, + []gopacket.LayerType{LayerTypeSCTPSack}, + []gopacket.LayerType{LayerTypeSCTPShutdown}, + []gopacket.LayerType{LayerTypeSCTPShutdownAck}, + []gopacket.LayerType{LayerTypeSCTPShutdownComplete}, + } + for i, data := range sctpPackets { + p := gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + for _, typ := range wantLayers[i] { + if p.Layer(typ) == nil { + t.Errorf("Packet %d missing layer type %v, got:", i, typ) + for _, layer := range p.Layers() { + t.Errorf("\t%v", layer.LayerType()) + } + if p.ErrorLayer() != nil { + t.Error("\tPacket layer error:", p.ErrorLayer().Error()) + } + } + } + // Test re-serialization. + testSerializationWithOpts(t, p, data, gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}) + } +} + +func TestDecodeCiscoDiscovery(t *testing.T) { + // http://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=cdp_v2.pcap + data := []byte{ + 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc, 0x00, 0x0b, 0xbe, 0x18, 0x9a, 0x41, 0x01, 0xc3, 0xaa, 0xaa, + 0x03, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x02, 0xb4, 0x09, 0xa0, 0x00, 0x01, 0x00, 0x0c, 0x6d, 0x79, + 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x00, 0x02, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0xcc, 0x00, 0x04, 0xc0, 0xa8, 0x00, 0xfd, 0x00, 0x03, 0x00, 0x13, 0x46, 0x61, 0x73, 0x74, 0x45, + 0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x30, 0x2f, 0x31, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x28, 0x00, 0x05, 0x01, 0x14, 0x43, 0x69, 0x73, 0x63, 0x6f, 0x20, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, + 0x72, 0x65, 0x20, 0x0a, 0x49, 0x4f, 0x53, 0x20, 0x28, 0x74, 0x6d, 0x29, 0x20, 0x43, 0x32, 0x39, + 0x35, 0x30, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x28, 0x43, 0x32, 0x39, + 0x35, 0x30, 0x2d, 0x49, 0x36, 0x4b, 0x32, 0x4c, 0x32, 0x51, 0x34, 0x2d, 0x4d, 0x29, 0x2c, 0x20, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x32, 0x2e, 0x31, 0x28, 0x32, 0x32, 0x29, + 0x45, 0x41, 0x31, 0x34, 0x2c, 0x20, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x20, 0x53, 0x4f, + 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x20, 0x28, 0x66, 0x63, 0x31, 0x29, 0x0a, 0x54, 0x65, 0x63, + 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x3a, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x69, 0x73, 0x63, 0x6f, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x65, 0x63, 0x68, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x0a, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, + 0x39, 0x38, 0x36, 0x2d, 0x32, 0x30, 0x31, 0x30, 0x20, 0x62, 0x79, 0x20, 0x63, 0x69, 0x73, 0x63, + 0x6f, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a, + 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x54, 0x75, 0x65, 0x20, 0x32, 0x36, 0x2d, + 0x4f, 0x63, 0x74, 0x2d, 0x31, 0x30, 0x20, 0x31, 0x30, 0x3a, 0x33, 0x35, 0x20, 0x62, 0x79, 0x20, + 0x6e, 0x62, 0x75, 0x72, 0x72, 0x61, 0x00, 0x06, 0x00, 0x15, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x20, + 0x57, 0x53, 0x2d, 0x43, 0x32, 0x39, 0x35, 0x30, 0x2d, 0x31, 0x32, 0x00, 0x08, 0x00, 0x24, 0x00, + 0x00, 0x0c, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x20, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xbe, 0x18, 0x9a, 0x40, 0xff, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x0c, 0x4d, 0x59, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x00, 0x0a, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x0b, 0x00, 0x05, 0x01, 0x00, 0x12, 0x00, 0x05, 0x00, 0x00, 0x13, 0x00, 0x05, 0x00, + 0x00, 0x16, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0xcc, 0x00, 0x04, 0xc0, 0xa8, 0x00, + 0xfd, + } + p := gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + wantLayers := []gopacket.LayerType{LayerTypeEthernet, LayerTypeLLC, LayerTypeSNAP, LayerTypeCiscoDiscovery, LayerTypeCiscoDiscoveryInfo} + checkLayers(p, wantLayers, t) + + want := &CiscoDiscoveryInfo{ + CDPHello: CDPHello{ + OUI: []byte{0, 0, 12}, + ProtocolID: 274, + ClusterMaster: []byte{0, 0, 0, 0}, + Unknown1: []byte{255, 255, 255, 255}, + Version: 1, + SubVersion: 2, + Status: 32, + Unknown2: 255, + ClusterCommander: net.HardwareAddr{0, 0, 0, 0, 0, 0}, + SwitchMAC: net.HardwareAddr{0, 0x0b, 0xbe, 0x18, 0x9a, 0x40}, + Unknown3: 255, + ManagementVLAN: 0, + }, + DeviceID: "myswitch", + Addresses: []net.IP{net.IPv4(192, 168, 0, 253)}, + PortID: "FastEthernet0/1", + Capabilities: CDPCapabilities{false, false, false, true, false, true, false, false, false}, + Version: "Cisco Internetwork Operating System Software \nIOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA14, RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.cisco.com/techsupport\nCopyright (c) 1986-2010 by cisco Systems, Inc.\nCompiled Tue 26-Oct-10 10:35 by nburra", + Platform: "cisco WS-C2950-12", + VTPDomain: "MYDOMAIN", + NativeVLAN: 1, + FullDuplex: true, + MgmtAddresses: []net.IP{net.IPv4(192, 168, 0, 253)}, + BaseLayer: BaseLayer{Contents: data[26:]}, + } + cdpL := p.Layer(LayerTypeCiscoDiscoveryInfo) + info, _ := cdpL.(*CiscoDiscoveryInfo) + if !reflect.DeepEqual(info, want) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", info, want) + } +} + +func TestDecodeLinkLayerDiscovery(t *testing.T) { + // http://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=lldp.detailed.pcap + data := []byte{ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, + 0x88, 0xcc, 0x02, 0x07, 0x04, 0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, 0x04, + 0x04, 0x05, 0x31, 0x2f, 0x31, 0x06, 0x02, 0x00, 0x78, 0x08, 0x17, 0x53, + 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, 0x2d, + 0x50, 0x6f, 0x72, 0x74, 0x20, 0x31, 0x30, 0x30, 0x31, 0x00, 0x0a, 0x0d, + 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, 0x2d, 0x34, 0x38, + 0x00, 0x0c, 0x4c, 0x53, 0x75, 0x6d, 0x6d, 0x69, 0x74, 0x33, 0x30, 0x30, + 0x2d, 0x34, 0x38, 0x20, 0x2d, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x37, 0x2e, 0x34, 0x65, 0x2e, 0x31, 0x20, 0x28, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x20, 0x35, 0x29, 0x20, 0x62, 0x79, 0x20, 0x52, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x20, 0x30, 0x35, 0x2f, 0x32, 0x37, 0x2f, 0x30, 0x35, 0x20, 0x30, 0x34, + 0x3a, 0x35, 0x33, 0x3a, 0x31, 0x31, 0x00, 0x0e, 0x04, 0x00, 0x14, 0x00, + 0x14, 0x10, 0x0e, 0x07, 0x06, 0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, 0x02, + 0x00, 0x00, 0x03, 0xe9, 0x00, 0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, 0x07, + 0x01, 0x00, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0x6c, 0x00, 0x00, + 0x10, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0x06, 0x00, 0x12, 0x0f, 0x04, 0x05, 0xf2, 0xfe, 0x06, 0x00, 0x80, + 0xc2, 0x01, 0x01, 0xe8, 0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, 0x01, 0x00, + 0x00, 0xfe, 0x17, 0x00, 0x80, 0xc2, 0x03, 0x01, 0xe8, 0x10, 0x76, 0x32, + 0x2d, 0x30, 0x34, 0x38, 0x38, 0x2d, 0x30, 0x33, 0x2d, 0x30, 0x35, 0x30, + 0x35, 0x00, 0xfe, 0x05, 0x00, 0x80, 0xc2, 0x04, 0x00, 0x00, 0x00, + } + + p := gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + wantLayers := []gopacket.LayerType{LayerTypeEthernet, LayerTypeLinkLayerDiscovery, LayerTypeLinkLayerDiscoveryInfo} + checkLayers(p, wantLayers, t) + lldpL := p.Layer(LayerTypeLinkLayerDiscovery) + lldp := lldpL.(*LinkLayerDiscovery) + want := &LinkLayerDiscovery{ + ChassisID: LLDPChassisID{LLDPChassisIDSubTypeMACAddr, []byte{0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0}}, + PortID: LLDPPortID{LLDPPortIDSubtypeIfaceName, []byte("1/1")}, + TTL: 120, + BaseLayer: BaseLayer{Contents: data[14:]}, + } + lldp.Values = nil // test these in next stage + if !reflect.DeepEqual(lldp, want) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", lldp, want) + } + + infoL := p.Layer(LayerTypeLinkLayerDiscoveryInfo) + info := infoL.(*LinkLayerDiscoveryInfo) + wantinfo := &LinkLayerDiscoveryInfo{ + PortDescription: "Summit300-48-Port 1001\x00", + SysName: "Summit300-48\x00", + SysDescription: "Summit300-48 - Version 7.4e.1 (Build 5) by Release_Master 05/27/05 04:53:11\x00", + SysCapabilities: LLDPSysCapabilities{ + SystemCap: LLDPCapabilities{Bridge: true, Router: true}, + EnabledCap: LLDPCapabilities{Bridge: true, Router: true}, + }, + MgmtAddress: LLDPMgmtAddress{IANAAddressFamily802, []byte{0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0}, LLDPInterfaceSubtypeifIndex, 1001, ""}, + OrgTLVs: []LLDPOrgSpecificTLV{ + LLDPOrgSpecificTLV{OUI: 0x120f, SubType: 0x2, Info: []uint8{0x7, 0x1, 0x0}}, + LLDPOrgSpecificTLV{OUI: 0x120f, SubType: 0x1, Info: []uint8{0x3, 0x6c, 0x0, 0x0, 0x10}}, + LLDPOrgSpecificTLV{OUI: 0x120f, SubType: 0x3, Info: []uint8{0x1, 0x0, 0x0, 0x0, 0x0}}, + LLDPOrgSpecificTLV{OUI: 0x120f, SubType: 0x4, Info: []uint8{0x5, 0xf2}}, + LLDPOrgSpecificTLV{OUI: 0x80c2, SubType: 0x1, Info: []uint8{0x1, 0xe8}}, + LLDPOrgSpecificTLV{OUI: 0x80c2, SubType: 0x2, Info: []uint8{0x1, 0x0, 0x0}}, + LLDPOrgSpecificTLV{OUI: 0x80c2, SubType: 0x3, Info: []uint8{0x1, 0xe8, 0x10, 0x76, 0x32, 0x2d, 0x30, 0x34, 0x38, 0x38, 0x2d, 0x30, 0x33, 0x2d, 0x30, 0x35, 0x30, 0x35, 0x0}}, + LLDPOrgSpecificTLV{OUI: 0x80c2, SubType: 0x4, Info: []uint8{0x0}}, + }, + Unknown: nil, + } + if !reflect.DeepEqual(info, wantinfo) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", info, wantinfo) + } + info8021, err := info.Decode8021() + if err != nil { + t.Errorf("8021 Values decode error: %v", err) + } + want8021 := LLDPInfo8021{ + PVID: 488, + PPVIDs: []PortProtocolVLANID{PortProtocolVLANID{false, false, 0}}, + VLANNames: []VLANName{VLANName{488, "v2-0488-03-0505\x00"}}, + ProtocolIdentities: nil, + VIDUsageDigest: 0, + ManagementVID: 0, + LinkAggregation: LLDPLinkAggregation{false, false, 0}, + } + if !reflect.DeepEqual(info8021, want8021) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", info8021, want8021) + } + info8023, err := info.Decode8023() + if err != nil { + t.Errorf("8023 Values decode error: %v", err) + } + want8023 := LLDPInfo8023{ + LinkAggregation: LLDPLinkAggregation{true, false, 0}, + MACPHYConfigStatus: LLDPMACPHYConfigStatus{true, true, 0x6c00, 0x0010}, + PowerViaMDI: LLDPPowerViaMDI8023{true, true, true, false, 1, 0, 0, 0, 0, 0, 0}, + MTU: 1522, + } + + if !reflect.DeepEqual(info8023, want8023) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", info8023, want8023) + } + + // http://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=lldpmed_civicloc.pcap + data = []byte{ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x00, 0x13, 0x21, 0x57, 0xca, 0x7f, + 0x88, 0xcc, 0x02, 0x07, 0x04, 0x00, 0x13, 0x21, 0x57, 0xca, 0x40, 0x04, + 0x02, 0x07, 0x31, 0x06, 0x02, 0x00, 0x78, 0x08, 0x01, 0x31, 0x0a, 0x1a, + 0x50, 0x72, 0x6f, 0x43, 0x75, 0x72, 0x76, 0x65, 0x20, 0x53, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x20, 0x32, 0x36, 0x30, 0x30, 0x2d, 0x38, 0x2d, 0x50, + 0x57, 0x52, 0x0c, 0x5f, 0x50, 0x72, 0x6f, 0x43, 0x75, 0x72, 0x76, 0x65, + 0x20, 0x4a, 0x38, 0x37, 0x36, 0x32, 0x41, 0x20, 0x53, 0x77, 0x69, 0x74, + 0x63, 0x68, 0x20, 0x32, 0x36, 0x30, 0x30, 0x2d, 0x38, 0x2d, 0x50, 0x57, + 0x52, 0x2c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, + 0x48, 0x2e, 0x30, 0x38, 0x2e, 0x38, 0x39, 0x2c, 0x20, 0x52, 0x4f, 0x4d, + 0x20, 0x48, 0x2e, 0x30, 0x38, 0x2e, 0x35, 0x58, 0x20, 0x28, 0x2f, 0x73, + 0x77, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x2f, 0x66, 0x69, 0x73, 0x68, 0x28, 0x74, 0x73, 0x5f, 0x30, 0x38, 0x5f, + 0x35, 0x29, 0x29, 0x0e, 0x04, 0x00, 0x14, 0x00, 0x04, 0x10, 0x0c, 0x05, + 0x01, 0x0f, 0xff, 0x7a, 0x94, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0x6c, 0x00, 0x00, 0x10, 0xfe, 0x07, + 0x00, 0x12, 0xbb, 0x01, 0x00, 0x0f, 0x04, 0xfe, 0x08, 0x00, 0x12, 0xbb, + 0x02, 0x01, 0x40, 0x65, 0xae, 0xfe, 0x2e, 0x00, 0x12, 0xbb, 0x03, 0x02, + 0x28, 0x02, 0x55, 0x53, 0x01, 0x02, 0x43, 0x41, 0x03, 0x09, 0x52, 0x6f, + 0x73, 0x65, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x06, 0x09, 0x46, 0x6f, 0x6f, + 0x74, 0x68, 0x69, 0x6c, 0x6c, 0x73, 0x13, 0x04, 0x38, 0x30, 0x30, 0x30, + 0x1a, 0x03, 0x52, 0x33, 0x4c, 0xfe, 0x07, 0x00, 0x12, 0xbb, 0x04, 0x03, + 0x00, 0x41, 0x00, 0x00, + } + + p = gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + wantLayers = []gopacket.LayerType{LayerTypeEthernet, LayerTypeLinkLayerDiscovery, LayerTypeLinkLayerDiscoveryInfo} + checkLayers(p, wantLayers, t) + lldpL = p.Layer(LayerTypeLinkLayerDiscovery) + lldp = lldpL.(*LinkLayerDiscovery) + want = &LinkLayerDiscovery{ + ChassisID: LLDPChassisID{LLDPChassisIDSubTypeMACAddr, []byte{0x00, 0x13, 0x21, 0x57, 0xca, 0x40}}, + PortID: LLDPPortID{LLDPPortIDSubtypeLocal, []byte("1")}, + TTL: 120, + BaseLayer: BaseLayer{Contents: data[14:]}, + } + lldp.Values = nil // test these in next stage + if !reflect.DeepEqual(lldp, want) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", lldp, want) + } + + infoL = p.Layer(LayerTypeLinkLayerDiscoveryInfo) + info = infoL.(*LinkLayerDiscoveryInfo) + wantinfo = &LinkLayerDiscoveryInfo{ + PortDescription: "1", + SysName: "ProCurve Switch 2600-8-PWR", + SysDescription: "ProCurve J8762A Switch 2600-8-PWR, revision H.08.89, ROM H.08.5X (/sw/code/build/fish(ts_08_5))", + SysCapabilities: LLDPSysCapabilities{ + SystemCap: LLDPCapabilities{Bridge: true, Router: true}, + EnabledCap: LLDPCapabilities{Bridge: true}, + }, + MgmtAddress: LLDPMgmtAddress{IANAAddressFamilyIPV4, []byte{0x0f, 0xff, 0x7a, 0x94}, LLDPInterfaceSubtypeifIndex, 0, ""}, + OrgTLVs: []LLDPOrgSpecificTLV{ + LLDPOrgSpecificTLV{OUI: 0x120f, SubType: 0x1, Info: []uint8{0x3, 0x6c, 0x0, 0x0, 0x10}}, + LLDPOrgSpecificTLV{OUI: 0x12bb, SubType: 0x1, Info: []uint8{0x0, 0xf, 0x4}}, + LLDPOrgSpecificTLV{OUI: 0x12bb, SubType: 0x2, Info: []uint8{0x1, 0x40, 0x65, 0xae}}, + LLDPOrgSpecificTLV{OUI: 0x12bb, SubType: 0x3, Info: []uint8{0x2, 0x28, 0x2, 0x55, 0x53, 0x1, 0x2, 0x43, 0x41, 0x3, 0x9, 0x52, 0x6f, 0x73, 0x65, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x6, 0x9, 0x46, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6c, 0x6c, 0x73, 0x13, 0x4, 0x38, 0x30, 0x30, 0x30, 0x1a, 0x3, 0x52, 0x33, 0x4c}}, + LLDPOrgSpecificTLV{OUI: 0x12bb, SubType: 0x4, Info: []uint8{0x3, 0x0, 0x41}}, + }, + Unknown: nil, + } + if !reflect.DeepEqual(info, wantinfo) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", info, wantinfo) + } + info8023, err = info.Decode8023() + if err != nil { + t.Errorf("8023 Values decode error: %v", err) + } + want8023 = LLDPInfo8023{ + MACPHYConfigStatus: LLDPMACPHYConfigStatus{true, true, 0x6c00, 0x0010}, + } + + if !reflect.DeepEqual(info8023, want8023) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", info8023, want8023) + } + + infoMedia, err := info.DecodeMedia() + if err != nil { + t.Errorf("8023 Values decode error: %v", err) + } + wantMedia := LLDPInfoMedia{ + MediaCapabilities: LLDPMediaCapabilities{true, true, true, true, false, false, LLDPMediaClassNetwork}, + NetworkPolicy: LLDPNetworkPolicy{LLDPAppTypeVoice, true, true, 50, 6, 46}, + Location: LLDPLocation{Format: LLDPLocationFormatAddress, Address: LLDPLocationAddress{ + What: LLDPLocationAddressWhatClient, + CountryCode: "US", + AddressLines: []LLDPLocationAddressLine{ + LLDPLocationAddressLine{LLDPLocationAddressTypeNational, "CA"}, + LLDPLocationAddressLine{LLDPLocationAddressTypeCity, "Roseville"}, + LLDPLocationAddressLine{LLDPLocationAddressTypeStreet, "Foothills"}, + LLDPLocationAddressLine{LLDPLocationAddressTypeHouseNum, "8000"}, + LLDPLocationAddressLine{LLDPLocationAddressTypeUnit, "R3L"}, + }, + }}, + PowerViaMDI: LLDPPowerViaMDI{0, 0, LLDPPowerPriorityLow, 6500}, + } + + if !reflect.DeepEqual(infoMedia, wantMedia) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", infoMedia, wantMedia) + } + +} + +func TestDecodeNortelDiscovery(t *testing.T) { + // http://www.thetechfirm.com/packets/nortel_btdp/btdp_nai.enc + data := []byte{ + 0x01, 0x00, 0x81, 0x00, 0x01, 0x00, 0x00, 0x04, 0x38, 0xe0, 0xcc, 0xde, + 0x00, 0x13, 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x81, 0x01, 0xa2, 0xac, 0x13, + 0x58, 0x03, 0x00, 0x04, 0x15, 0x30, 0x0c, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x38, 0xe0, 0xcc, 0xde, 0x80, 0x6a, 0x00, 0x01, 0x14, 0x00, + 0x02, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + p := gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + wantLayers := []gopacket.LayerType{LayerTypeEthernet, LayerTypeLLC, LayerTypeSNAP, LayerTypeNortelDiscovery} + checkLayers(p, wantLayers, t) + + want := &NortelDiscovery{ + IPAddress: []byte{172, 19, 88, 3}, + SegmentID: []byte{0x00, 0x04, 0x15}, + Chassis: NDPChassisBayStack450101001000Switches, + Backplane: NDPBackplaneEthernetFastEthernetGigabitEthernet, + State: NDPStateHeartbeat, + NumLinks: 0, + } + ndpL := p.Layer(LayerTypeNortelDiscovery) + info, _ := ndpL.(*NortelDiscovery) + if !reflect.DeepEqual(info, want) { + t.Errorf("Values mismatch, \ngot %#v\nwant %#v\n", info, want) + } +} + +func TestDecodeIPv6Jumbogram(t *testing.T) { + // Haven't found any of these in the wild or on example pcaps online, so had + // to generate one myself via scapy. Unfortunately, scapy can only + // str(packet) for packets with length < 65536, due to limitations in python's + // struct library, so I generated the header with: + // Ether() / IPv6(src='::1', dst='::2') / IPv6ExtHdrHopByHop(options=[Jumbo(jumboplen=70000)]) / TCP(sport=8888, dport=80) + // then added the payload manually ("payload" * 9996). The checksums here are + // not correct, but we don't check, so who cares ;) + dataStr := "\x00\x1f\xca\xb3v@$\xbe\x05'\x0b\x17\x86\xdd`\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06\x00\xc2\x04\x00\x01\x11p\"\xb8\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00l\xd8\x00\x00" + payload := strings.Repeat("payload", 9996) + data := []byte(dataStr + payload) + p := gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv6, LayerTypeIPv6HopByHop, LayerTypeTCP, gopacket.LayerTypePayload}, t) + if p.ApplicationLayer() == nil { + t.Error("Packet has no application layer") + } else if string(p.ApplicationLayer().Payload()) != payload { + t.Errorf("Jumbogram payload wrong") + } + // Check truncated for jumbograms + data = data[:len(data)-1] + p = gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv6, LayerTypeIPv6HopByHop, LayerTypeTCP, gopacket.LayerTypePayload}, t) + if !p.Metadata().Truncated { + t.Error("Jumbogram should be truncated") + } +} + +func TestDecodeUDPPacketTooSmall(t *testing.T) { + data := []byte{ + 0x00, 0x15, 0x2c, 0x9d, 0xcc, 0x00, 0x00, 0x10, 0xdb, 0xff, 0x10, 0x00, 0x81, 0x00, 0x01, 0xf7, + 0x08, 0x00, 0x45, 0x60, 0x00, 0x3c, 0x0f, 0xa9, 0x00, 0x00, 0x6e, 0x11, 0x01, 0x0a, 0x47, 0xe6, + 0xee, 0x2e, 0xac, 0x16, 0x59, 0x73, 0x00, 0x50, 0x00, 0x50, 0x00, 0x28, 0x4d, 0xad, 0x00, 0x67, + 0x00, 0x01, 0x00, 0x72, 0xd5, 0xc7, 0xf1, 0x07, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x19, 0xba, + } + p := gopacket.NewPacket(data, LinkTypeEthernet, testDecodeOptions) + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeDot1Q, LayerTypeIPv4, LayerTypeUDP, gopacket.LayerTypePayload}, t) + if !p.Metadata().Truncated { + t.Error("UDP short packet should be truncated") + } +} + +func TestDecodingLayerParserFullTCPPacket(t *testing.T) { + dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet, &Ethernet{}, &IPv4{}, &TCP{}, &gopacket.Payload{}) + decoded := make([]gopacket.LayerType, 1) + err := dlp.DecodeLayers(testSimpleTCPPacket, &decoded) + if err != nil { + t.Error("Error from dlp parser: ", err) + } + if len(decoded) != 4 { + t.Error("Expected 4 layers parsed, instead got ", len(decoded)) + } +} + +// testICMP is the packet: +// 15:49:15.773265 IP 72.14.222.226 > 172.29.20.15: ICMP host 10.66.73.201 unreachable - admin prohibited filter, length 36 +// 0x0000: 24be 0527 0b17 001f cab3 75c0 0800 4500 $..'......u...E. +// 0x0010: 0038 0000 0000 fc01 d7a7 480e dee2 ac1d .8........H..... +// 0x0020: 140f 030d 946e 0000 0000 4520 004d 0000 .....n....E..M.. +// 0x0030: 4000 3e11 2849 ac1d 140f 0a42 49c9 8ecc @.>.(I.....BI... +// 0x0040: 62e1 0039 769d b..9v. +var testICMP = []byte{ + 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x75, 0xc0, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xd7, 0xa7, 0x48, 0x0e, 0xde, 0xe2, 0xac, 0x1d, + 0x14, 0x0f, 0x03, 0x0d, 0x94, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x45, 0x20, 0x00, 0x4d, 0x00, 0x00, + 0x40, 0x00, 0x3e, 0x11, 0x28, 0x49, 0xac, 0x1d, 0x14, 0x0f, 0x0a, 0x42, 0x49, 0xc9, 0x8e, 0xcc, + 0x62, 0xe1, 0x00, 0x39, 0x76, 0x9d, +} + +func TestICMP(t *testing.T) { + p := gopacket.NewPacket(testICMP, LinkTypeEthernet, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + testSerialization(t, p, testICMP) +} +func BenchmarkDecodeICMP(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testICMP, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// testICMP6 is the packet: +// 16:17:37.758937 IP6 fe80::21f:caff:feb3:75c0 > 2620:0:1005:0:26be:5ff:fe27:b17: ICMP6, neighbor solicitation, who has 2620:0:1005:0:26be:5ff:fe27:b17, length 32 +// 0x0000: 24be 0527 0b17 001f cab3 75c0 86dd 6e00 $..'......u...n. +// 0x0010: 0000 0020 3aff fe80 0000 0000 0000 021f ....:........... +// 0x0020: caff feb3 75c0 2620 0000 1005 0000 26be ....u.&.......&. +// 0x0030: 05ff fe27 0b17 8700 1eba 0000 0000 2620 ...'..........&. +// 0x0040: 0000 1005 0000 26be 05ff fe27 0b17 0101 ......&....'.... +// 0x0050: 001f cab3 75c0 ....u. +var testICMP6 = []byte{ + 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x75, 0xc0, 0x86, 0xdd, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x3a, 0xff, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1f, + 0xca, 0xff, 0xfe, 0xb3, 0x75, 0xc0, 0x26, 0x20, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x26, 0xbe, + 0x05, 0xff, 0xfe, 0x27, 0x0b, 0x17, 0x87, 0x00, 0x1e, 0xba, 0x00, 0x00, 0x00, 0x00, 0x26, 0x20, + 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x26, 0xbe, 0x05, 0xff, 0xfe, 0x27, 0x0b, 0x17, 0x01, 0x01, + 0x00, 0x1f, 0xca, 0xb3, 0x75, 0xc0, +} + +func TestICMP6(t *testing.T) { + p := gopacket.NewPacket(testICMP6, LinkTypeEthernet, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv6, LayerTypeICMPv6, gopacket.LayerTypePayload}, t) + testSerialization(t, p, testICMP6) +} +func BenchmarkDecodeICMP6(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testICMP6, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// testMPLS is the packet: +// 12:48:57.201014 MPLS (label 29, exp 0, [S], ttl 255) IP 10.1.2.1 > 10.34.0.1: ICMP echo request, id 2618, seq 1579, length 80 +// 0x0000: 0030 96e6 fc39 0030 9605 2838 8847 0001 .0...9.0..(8.G.. +// 0x0010: d1ff 4500 0064 000b 0000 ff01 a569 0a01 ..E..d.......i.. +// 0x0020: 0201 0a22 0001 0800 3a76 0a3a 062b 0000 ..."....:v.:.+.. +// 0x0030: 0000 001f 3350 abcd abcd abcd abcd abcd ....3P.......... +// 0x0040: abcd abcd abcd abcd abcd abcd abcd abcd ................ +// 0x0050: abcd abcd abcd abcd abcd abcd abcd abcd ................ +// 0x0060: abcd abcd abcd abcd abcd abcd abcd abcd ................ +// 0x0070: abcd abcd abcd ...... +var testMPLS = []byte{ + 0x00, 0x30, 0x96, 0xe6, 0xfc, 0x39, 0x00, 0x30, 0x96, 0x05, 0x28, 0x38, 0x88, 0x47, 0x00, 0x01, + 0xd1, 0xff, 0x45, 0x00, 0x00, 0x64, 0x00, 0x0b, 0x00, 0x00, 0xff, 0x01, 0xa5, 0x69, 0x0a, 0x01, + 0x02, 0x01, 0x0a, 0x22, 0x00, 0x01, 0x08, 0x00, 0x3a, 0x76, 0x0a, 0x3a, 0x06, 0x2b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x33, 0x50, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, +} + +func TestMPLS(t *testing.T) { + p := gopacket.NewPacket(testMPLS, LinkTypeEthernet, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeMPLS, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + testSerialization(t, p, testMPLS) +} +func BenchmarkDecodeMPLS(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testMPLS, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// testPPPoEICMPv6 is the packet: +// 07:43:31.091560 PPPoE [ses 0x11] IP6 fe80::c801:eff:fe88:8 > ff02::1: ICMP6, neighbor advertisement, tgt is fe80::c801:eff:fe88:8, length 24 +// 0x0000: cc05 0e88 0000 ca01 0e88 0006 8864 1100 .............d.. +// 0x0010: 0011 0042 0057 6e00 0000 0018 3aff fe80 ...B.Wn.....:... +// 0x0020: 0000 0000 0000 c801 0eff fe88 0008 ff02 ................ +// 0x0030: 0000 0000 0000 0000 0000 0000 0001 8800 ................ +// 0x0040: 5083 8000 0000 fe80 0000 0000 0000 c801 P............... +// 0x0050: 0eff fe88 0008 ...... +var testPPPoEICMPv6 = []byte{ + 0xcc, 0x05, 0x0e, 0x88, 0x00, 0x00, 0xca, 0x01, 0x0e, 0x88, 0x00, 0x06, 0x88, 0x64, 0x11, 0x00, + 0x00, 0x11, 0x00, 0x42, 0x00, 0x57, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3a, 0xff, 0xfe, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x0e, 0xff, 0xfe, 0x88, 0x00, 0x08, 0xff, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, + 0x50, 0x83, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01, + 0x0e, 0xff, 0xfe, 0x88, 0x00, 0x08, +} + +func TestPPPoEICMPv6(t *testing.T) { + p := gopacket.NewPacket(testPPPoEICMPv6, LinkTypeEthernet, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{ + LayerTypeEthernet, + LayerTypePPPoE, + LayerTypePPP, + LayerTypeIPv6, + LayerTypeICMPv6, + gopacket.LayerTypePayload, + }, t) + testSerialization(t, p, testPPPoEICMPv6) +} +func BenchmarkDecodePPPoEICMPv6(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPPPoEICMPv6, LinkTypeEthernet, gopacket.NoCopy) + } +} + +var testPFLogUDP = []byte{ + 0x3d, 0x02, 0x00, 0x00, 0x65, 0x6e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xff, 0xff, 0xff, 0x7f, + 0xa0, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xb8, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x2c, 0x02, 0x16, 0x00, 0x00, 0x40, 0x11, 0x4e, 0xb0, 0xac, 0x17, 0xe8, 0xcc, + 0xac, 0x17, 0xe8, 0xff, 0xf0, 0xff, 0x21, 0xa4, 0x00, 0x18, 0x2a, 0x25, 0x50, 0x4e, 0x4a, 0x50, + 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestPFLogUDP(t *testing.T) { + p := gopacket.NewPacket(testPFLogUDP, LinkTypePFLog, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{ + LayerTypePFLog, + LayerTypeIPv4, + LayerTypeUDP, + gopacket.LayerTypePayload, + }, t) +} + +func TestRegressionDot1QPriority(t *testing.T) { + d := &Dot1Q{ + Priority: 2, + } + out := gopacket.NewSerializeBuffer() + gopacket.SerializeLayers(out, gopacket.SerializeOptions{}, d) + if err := d.DecodeFromBytes(out.Bytes(), gopacket.NilDecodeFeedback); err != nil { + t.Errorf("could not decode encoded dot1q") + } else if d.Priority != 2 { + t.Errorf("priority mismatch, want 2 got %d", d.Priority) + } +} + +// testPacketMPLSInMPLS is the packet: +// 15:27:44.753678 MPLS (label 18, exp 0, ttl 255) (label 16, exp 0, [S], ttl +// 255) IP 10.31.0.1 > 10.34.0.1: ICMP echo request, id 3941, seq 4768, length +// 80 +// 0x0000: 0030 96e6 fc39 0030 9605 2838 8847 0001 .0...9.0..(8.G.. +// 0x0010: 20ff 0001 01ff 4500 0064 0050 0000 ff01 ......E..d.P.... +// 0x0020: a706 0a1f 0001 0a22 0001 0800 bd11 0f65 .......".......e +// 0x0030: 12a0 0000 0000 0053 9ee0 abcd abcd abcd .......S........ +// 0x0040: abcd abcd abcd abcd abcd abcd abcd abcd ................ +// 0x0050: abcd abcd abcd abcd abcd abcd abcd abcd ................ +// 0x0060: abcd abcd abcd abcd abcd abcd abcd abcd ................ +// 0x0070: abcd abcd abcd abcd abcd .......... +var testPacketMPLSInMPLS = []byte{ + 0x00, 0x30, 0x96, 0xe6, 0xfc, 0x39, 0x00, 0x30, 0x96, 0x05, 0x28, 0x38, 0x88, 0x47, 0x00, 0x01, + 0x20, 0xff, 0x00, 0x01, 0x01, 0xff, 0x45, 0x00, 0x00, 0x64, 0x00, 0x50, 0x00, 0x00, 0xff, 0x01, + 0xa7, 0x06, 0x0a, 0x1f, 0x00, 0x01, 0x0a, 0x22, 0x00, 0x01, 0x08, 0x00, 0xbd, 0x11, 0x0f, 0x65, + 0x12, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x9e, 0xe0, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, +} + +func TestPacketMPLSInMPLS(t *testing.T) { + p := gopacket.NewPacket(testPacketMPLSInMPLS, LinkTypeEthernet, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{ + LayerTypeEthernet, + LayerTypeMPLS, + LayerTypeMPLS, + LayerTypeIPv4, + LayerTypeICMPv4, + gopacket.LayerTypePayload}, t) +} + +// testPacketIPv4Fragmented is the packet: +// 22:11:26.616090 IP 10.1.1.1.31915 > 129.111.30.27.20197: UDP, length 28 +// 0x0000: 0000 39cf d9cd 0040 33d9 7cfd 0800 4500 ..9....@3.|...E. +// 0x0010: 0038 00f2 2000 4011 af37 0a01 0101 816f .8....@..7.....o +// 0x0020: 1e1b 7cab 4ee5 0024 0000 0000 0000 0000 ..|.N..$........ +// 0x0030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x0040: 0000 0000 0000 ...... +var testPacketIPv4Fragmented = []byte{ + 0x00, 0x00, 0x39, 0xcf, 0xd9, 0xcd, 0x00, 0x40, 0x33, 0xd9, 0x7c, 0xfd, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x38, 0x00, 0xf2, 0x20, 0x00, 0x40, 0x11, 0xaf, 0x37, 0x0a, 0x01, 0x01, 0x01, 0x81, 0x6f, + 0x1e, 0x1b, 0x7c, 0xab, 0x4e, 0xe5, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestPacketIPv4Fragmented(t *testing.T) { + p := gopacket.NewPacket(testPacketIPv4Fragmented, LinkTypeEthernet, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, gopacket.LayerTypeFragment}, t) + testSerializationWithOpts(t, p, testPacketIPv4Fragmented, gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}) +} + +// TestSCTPChunkBadLength tests for issue #146 +func TestSCTPChunkBadLength(t *testing.T) { + data := []byte( + "0000\xad9$e\x11\xe4\xaeo\b\x00E\x00\x018\xb4\xa3" + + "\x00\x00Y\x84\xc4@\x11gz\xc0\xa8\xee\x01\xc0\xa8" + + "\xeeD\x007le\x03\x01\xc0\f\xdf\b\x01\x00\x00") + + // this panic'd previously due to a zero length chunk getting + // repeatedly read + gopacket.NewPacket(data, LinkTypeEthernet, gopacket.Default) +} + +// TestSTP +func TestSTP(t *testing.T) { + testSTPpacket := []byte{ + 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0E, 0x87, 0x85, 0x04, 0x00, 0x26, 0x42, 0x42, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x64, 0x00, 0x1C, 0x0E, 0x87, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x80, 0x64, 0x00, 0x1C, 0x0E, 0x87, 0x85, 0x00, 0x80, 0x04, 0x01, 0x00, 0x14, 0x00, + 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + p := gopacket.NewPacket(testSTPpacket, LinkTypeEthernet, testDecodeOptions) + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeLLC, LayerTypeSTP}, t) +} diff --git a/vendor/github.com/google/gopacket/layers/dhcp_test.go b/vendor/github.com/google/gopacket/layers/dhcp_test.go new file mode 100644 index 00000000..c4975dcc --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dhcp_test.go @@ -0,0 +1,129 @@ +// Copyright 2016, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "net" + "testing" + + "github.com/google/gopacket" +) + +func TestDHCPv4EncodeRequest(t *testing.T) { + dhcp := &DHCPv4{Operation: DHCPOpRequest, HardwareType: LinkTypeEthernet, Xid: 0x12345678, + ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{0, 0, 0, 0}, NextServerIP: net.IP{0, 0, 0, 0}, RelayAgentIP: net.IP{0, 0, 0, 0}, + ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, + ServerName: make([]byte, 64), File: make([]byte, 128)} + + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptMessageType, []byte{byte(DHCPMsgTypeDiscover)})) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptHostname, []byte{'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm'})) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptPad, nil)) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptParamsRequest, + []byte{byte(DHCPOptSubnetMask), byte(DHCPOptBroadcastAddr), byte(DHCPOptTimeOffset), + byte(DHCPOptRouter), byte(DHCPOptDomainName), byte(DHCPOptDNS), byte(DHCPOptDomainSearch), + byte(DHCPOptHostname), byte(DHCPOptNetBIOSTCPNS), byte(DHCPOptInterfaceMTU), byte(DHCPOptClasslessStaticRoute), + byte(DHCPOptNTPServers)})) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err := gopacket.SerializeLayers(buf, opts, dhcp) + if err != nil { + t.Fatal(err) + } + + p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDHCPv4, testDecodeOptions) + dhcp2 := p2.Layer(LayerTypeDHCPv4).(*DHCPv4) + testDHCPEqual(t, dhcp, dhcp2) +} + +func TestDHCPv4EncodeResponse(t *testing.T) { + dhcp := &DHCPv4{Operation: DHCPOpReply, HardwareType: LinkTypeEthernet, Xid: 0x12345678, + ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{192, 168, 0, 123}, NextServerIP: net.IP{192, 168, 0, 1}, RelayAgentIP: net.IP{0, 0, 0, 0}, + ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}, + ServerName: make([]byte, 64), File: make([]byte, 128)} + + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptMessageType, []byte{byte(DHCPMsgTypeOffer)})) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptSubnetMask, []byte{255, 255, 255, 0})) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptPad, nil)) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptT1, []byte{0x00, 0x00, 0x0e, 0x10})) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptT2, []byte{0x00, 0x00, 0x0e, 0x10})) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptLeaseTime, []byte{0x00, 0x00, 0x0e, 0x10})) + dhcp.Options = append(dhcp.Options, NewDHCPOption(DHCPOptServerID, []byte{192, 168, 0, 1})) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err := gopacket.SerializeLayers(buf, opts, dhcp) + if err != nil { + t.Fatal(err) + } + + p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDHCPv4, testDecodeOptions) + dhcp2 := p2.Layer(LayerTypeDHCPv4).(*DHCPv4) + testDHCPEqual(t, dhcp, dhcp2) +} + +func testDHCPEqual(t *testing.T, d1, d2 *DHCPv4) { + if d1.Operation != d2.Operation { + t.Errorf("expected Operation=%s, got %s", d1.Operation, d2.Operation) + } + if d1.HardwareType != d2.HardwareType { + t.Errorf("expected HardwareType=%s, got %s", d1.HardwareType, d2.HardwareType) + } + if d1.HardwareLen != d2.HardwareLen { + t.Errorf("expected HardwareLen=%v, got %v", d1.HardwareLen, d2.HardwareLen) + } + if d1.HardwareOpts != d2.HardwareOpts { + t.Errorf("expected HardwareOpts=%v, got %v", d1.HardwareOpts, d2.HardwareOpts) + } + if d1.Xid != d2.Xid { + t.Errorf("expected Xid=%v, got %v", d1.Xid, d2.Xid) + } + if d1.Secs != d2.Secs { + t.Errorf("expected Secs=%v, got %v", d1.Secs, d2.Secs) + } + if d1.Flags != d2.Flags { + t.Errorf("expected Flags=%v, got %v", d1.Flags, d2.Flags) + } + if !d1.ClientIP.Equal(d2.ClientIP) { + t.Errorf("expected ClientIP=%v, got %v", d1.ClientIP, d2.ClientIP) + } + if !d1.YourClientIP.Equal(d2.YourClientIP) { + t.Errorf("expected YourClientIP=%v, got %v", d1.YourClientIP, d2.YourClientIP) + } + if !d1.NextServerIP.Equal(d2.NextServerIP) { + t.Errorf("expected NextServerIP=%v, got %v", d1.NextServerIP, d2.NextServerIP) + } + if !d1.RelayAgentIP.Equal(d2.RelayAgentIP) { + t.Errorf("expected RelayAgentIP=%v, got %v", d1.RelayAgentIP, d2.RelayAgentIP) + } + if !bytes.Equal(d1.ClientHWAddr, d2.ClientHWAddr) { + t.Errorf("expected ClientHWAddr=%v, got %v", d1.ClientHWAddr, d2.ClientHWAddr) + } + if !bytes.Equal(d1.ServerName, d2.ServerName) { + t.Errorf("expected ServerName=%v, got %v", d1.ServerName, d2.ServerName) + } + if !bytes.Equal(d1.File, d2.File) { + t.Errorf("expected File=%v, got %v", d1.File, d2.File) + } + if len(d1.Options) != len(d2.Options) { + t.Errorf("expected %d options, got %d", len(d1.Options), len(d2.Options)) + } + + for i, o := range d1.Options { + testDHCPOptionEqual(t, i, o, d2.Options[i]) + } +} + +func testDHCPOptionEqual(t *testing.T, idx int, d1, d2 DHCPOption) { + if d1.Type != d2.Type { + t.Errorf("expection Options[%d].Type = %s, got %s", idx, d1.Type, d2.Type) + } + if !bytes.Equal(d1.Data, d2.Data) { + t.Errorf("expection Options[%d].Data to be = %v, got %v", idx, d1.Data, d2.Data) + } +} diff --git a/vendor/github.com/google/gopacket/layers/dhcpv4.go b/vendor/github.com/google/gopacket/layers/dhcpv4.go new file mode 100644 index 00000000..761b201a --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dhcpv4.go @@ -0,0 +1,571 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// DHCPOp rerprents a bootp operation +type DHCPOp byte + +// bootp operations +const ( + DHCPOpRequest DHCPOp = 1 + DHCPOpReply DHCPOp = 2 +) + +// String returns a string version of a DHCPOp. +func (o DHCPOp) String() string { + switch o { + case DHCPOpRequest: + return "Request" + case DHCPOpReply: + return "Reply" + default: + return "Unknown" + } +} + +// DHCPMsgType represents a DHCP operation +type DHCPMsgType byte + +// Constants that represent DHCP operations +const ( + DHCPMsgTypeUnspecified DHCPMsgType = iota + DHCPMsgTypeDiscover + DHCPMsgTypeOffer + DHCPMsgTypeRequest + DHCPMsgTypeDecline + DHCPMsgTypeAck + DHCPMsgTypeNak + DHCPMsgTypeRelease + DHCPMsgTypeInform +) + +// String returns a string version of a DHCPMsgType. +func (o DHCPMsgType) String() string { + switch o { + case DHCPMsgTypeUnspecified: + return "Unspecified" + case DHCPMsgTypeDiscover: + return "Discover" + case DHCPMsgTypeOffer: + return "Offer" + case DHCPMsgTypeRequest: + return "Request" + case DHCPMsgTypeDecline: + return "Decline" + case DHCPMsgTypeAck: + return "Ack" + case DHCPMsgTypeNak: + return "Nak" + case DHCPMsgTypeRelease: + return "Release" + case DHCPMsgTypeInform: + return "Inform" + default: + return "Unknown" + } +} + +//DHCPMagic is the RFC 2131 "magic cooke" for DHCP. +var DHCPMagic uint32 = 0x63825363 + +// DHCPv4 contains data for a single DHCP packet. +type DHCPv4 struct { + BaseLayer + Operation DHCPOp + HardwareType LinkType + HardwareLen uint8 + HardwareOpts uint8 + Xid uint32 + Secs uint16 + Flags uint16 + ClientIP net.IP + YourClientIP net.IP + NextServerIP net.IP + RelayAgentIP net.IP + ClientHWAddr net.HardwareAddr + ServerName []byte + File []byte + Options DHCPOptions +} + +// DHCPOptions is used to get nicely printed option lists which would normally +// be cut off after 5 options. +type DHCPOptions []DHCPOption + +// String returns a string version of the options list. +func (o DHCPOptions) String() string { + buf := &bytes.Buffer{} + buf.WriteByte('[') + for i, opt := range o { + buf.WriteString(opt.String()) + if i+1 != len(o) { + buf.WriteString(", ") + } + } + buf.WriteByte(']') + return buf.String() +} + +// LayerType returns gopacket.LayerTypeDHCPv4 +func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 } + +// DecodeFromBytes decodes the given bytes into this layer. +func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + d.Operation = DHCPOp(data[0]) + d.HardwareType = LinkType(data[1]) + d.HardwareLen = data[2] + d.HardwareOpts = data[3] + d.Xid = binary.BigEndian.Uint32(data[4:8]) + d.Secs = binary.BigEndian.Uint16(data[8:10]) + d.Flags = binary.BigEndian.Uint16(data[10:12]) + d.ClientIP = net.IP(data[12:16]) + d.YourClientIP = net.IP(data[16:20]) + d.NextServerIP = net.IP(data[20:24]) + d.RelayAgentIP = net.IP(data[24:28]) + d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen]) + d.ServerName = data[44:108] + d.File = data[108:236] + if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic { + return errors.New("Bad DHCP header") + } + + if len(data) <= 240 { + // DHCP Packet could have no option (??) + return nil + } + + options := data[240:] + + stop := len(options) + start := 0 + for start < stop { + o := DHCPOption{} + if err := o.decode(options[start:]); err != nil { + return err + } + if o.Type == DHCPOptEnd { + break + } + d.Options = append(d.Options, o) + // Check if the option is a single byte pad + if o.Type == DHCPOptPad { + start++ + } else { + start += int(o.Length) + 2 + } + } + return nil +} + +// Len returns the length of a DHCPv4 packet. +func (d *DHCPv4) Len() uint16 { + n := uint16(240) + for _, o := range d.Options { + if o.Type == DHCPOptPad { + n++ + } else { + n += uint16(o.Length) + 2 + } + } + n++ // for opt end + return n +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + plen := int(d.Len()) + + data, err := b.PrependBytes(plen) + if err != nil { + return err + } + + data[0] = byte(d.Operation) + data[1] = byte(d.HardwareType) + if opts.FixLengths { + d.HardwareLen = uint8(len(d.ClientHWAddr)) + } + data[2] = d.HardwareLen + data[3] = d.HardwareOpts + binary.BigEndian.PutUint32(data[4:8], d.Xid) + binary.BigEndian.PutUint16(data[8:10], d.Secs) + binary.BigEndian.PutUint16(data[10:12], d.Flags) + copy(data[12:16], d.ClientIP.To4()) + copy(data[16:20], d.YourClientIP.To4()) + copy(data[20:24], d.NextServerIP.To4()) + copy(data[24:28], d.RelayAgentIP.To4()) + copy(data[28:44], d.ClientHWAddr) + copy(data[44:108], d.ServerName) + copy(data[108:236], d.File) + binary.BigEndian.PutUint32(data[236:240], DHCPMagic) + + if len(d.Options) > 0 { + offset := 240 + for _, o := range d.Options { + if err := o.encode(data[offset:]); err != nil { + return err + } + // A pad option is only a single byte + if o.Type == DHCPOptPad { + offset++ + } else { + offset += 2 + len(o.Data) + } + } + optend := NewDHCPOption(DHCPOptEnd, nil) + if err := optend.encode(data[offset:]); err != nil { + return err + } + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (d *DHCPv4) CanDecode() gopacket.LayerClass { + return LayerTypeDHCPv4 +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (d *DHCPv4) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error { + dhcp := &DHCPv4{} + err := dhcp.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(dhcp) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +// DHCPOpt represents a DHCP option or parameter from RFC-2132 +type DHCPOpt byte + +// Constants for the DHCPOpt options. +const ( + DHCPOptPad DHCPOpt = 0 + DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP + DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC) + DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP + DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP + DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP + DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP + DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP + DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP + DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP + DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP + DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP + DHCPOptHostname DHCPOpt = 12 // n, string + DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16 + DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string + DHCPOptDomainName DHCPOpt = 15 // n, string + DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP + DHCPOptRootPath DHCPOpt = 17 // n, string + DHCPOptExtensionsPath DHCPOpt = 18 // n, string + DHCPOptIPForwarding DHCPOpt = 19 // 1, bool + DHCPOptSourceRouting DHCPOpt = 20 // 1, bool + DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP} + DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16 + DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte + DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32 + DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16 + DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16 + DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool + DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP + DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool + DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool + DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool + DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP + DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask + DHCPOptARPTrailers DHCPOpt = 34 // 1, bool + DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32 + DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool + DHCPOptTCPTTL DHCPOpt = 37 // 1, byte + DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32 + DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool + DHCPOptNISDomain DHCPOpt = 40 // n, string + DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP + DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP + DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated. + DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP + DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP + DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte + DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string + DHCPOptXFontServer DHCPOpt = 48 // n, string + DHCPOptXDisplayManager DHCPOpt = 49 // n, string + DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP + DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32 + DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3 + DHCPOptMessageType DHCPOpt = 53 // 1, 1-7 + DHCPOptServerID DHCPOpt = 54 // 4, net.IP + DHCPOptParamsRequest DHCPOpt = 55 // n, []byte + DHCPOptMessage DHCPOpt = 56 // n, 3 + DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16 + DHCPOptT1 DHCPOpt = 58 // 4, uint32 + DHCPOptT2 DHCPOpt = 59 // 4, uint32 + DHCPOptClassID DHCPOpt = 60 // n, []byte + DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte + DHCPOptDomainSearch DHCPOpt = 119 // n, string + DHCPOptSIPServers DHCPOpt = 120 // n, url + DHCPOptClasslessStaticRoute DHCPOpt = 121 // + DHCPOptEnd DHCPOpt = 255 +) + +// String returns a string version of a DHCPOpt. +func (o DHCPOpt) String() string { + switch o { + case DHCPOptPad: + return "(padding)" + case DHCPOptSubnetMask: + return "SubnetMask" + case DHCPOptTimeOffset: + return "TimeOffset" + case DHCPOptRouter: + return "Router" + case DHCPOptTimeServer: + return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP + case DHCPOptNameServer: + return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS + case DHCPOptDNS: + return "DNS" + case DHCPOptLogServer: + return "mitLCS" // MIT LCS server protocol yada yada w. Syslog + case DHCPOptCookieServer: + return "CookieServer" + case DHCPOptLPRServer: + return "LPRServer" + case DHCPOptImpressServer: + return "ImpressServer" + case DHCPOptResLocServer: + return "ResourceLocationServer" + case DHCPOptHostname: + return "Hostname" + case DHCPOptBootfileSize: + return "BootfileSize" + case DHCPOptMeritDumpFile: + return "MeritDumpFile" + case DHCPOptDomainName: + return "DomainName" + case DHCPOptSwapServer: + return "SwapServer" + case DHCPOptRootPath: + return "RootPath" + case DHCPOptExtensionsPath: + return "ExtensionsPath" + case DHCPOptIPForwarding: + return "IPForwarding" + case DHCPOptSourceRouting: + return "SourceRouting" + case DHCPOptPolicyFilter: + return "PolicyFilter" + case DHCPOptDatagramMTU: + return "DatagramMTU" + case DHCPOptDefaultTTL: + return "DefaultTTL" + case DHCPOptPathMTUAgingTimeout: + return "PathMTUAgingTimeout" + case DHCPOptPathPlateuTableOption: + return "PathPlateuTableOption" + case DHCPOptInterfaceMTU: + return "InterfaceMTU" + case DHCPOptAllSubsLocal: + return "AllSubsLocal" + case DHCPOptBroadcastAddr: + return "BroadcastAddress" + case DHCPOptMaskDiscovery: + return "MaskDiscovery" + case DHCPOptMaskSupplier: + return "MaskSupplier" + case DHCPOptRouterDiscovery: + return "RouterDiscovery" + case DHCPOptSolicitAddr: + return "SolicitAddr" + case DHCPOptStaticRoute: + return "StaticRoute" + case DHCPOptARPTrailers: + return "ARPTrailers" + case DHCPOptARPTimeout: + return "ARPTimeout" + case DHCPOptEthernetEncap: + return "EthernetEncap" + case DHCPOptTCPTTL: + return "TCPTTL" + case DHCPOptTCPKeepAliveInt: + return "TCPKeepAliveInt" + case DHCPOptTCPKeepAliveGarbage: + return "TCPKeepAliveGarbage" + case DHCPOptNISDomain: + return "NISDomain" + case DHCPOptNISServers: + return "NISServers" + case DHCPOptNTPServers: + return "NTPServers" + case DHCPOptVendorOption: + return "VendorOption" + case DHCPOptNetBIOSTCPNS: + return "NetBIOSOverTCPNS" + case DHCPOptNetBIOSTCPDDS: + return "NetBiosOverTCPDDS" + case DHCPOptNETBIOSTCPNodeType: + return "NetBIOSOverTCPNodeType" + case DHCPOptNetBIOSTCPScope: + return "NetBIOSOverTCPScope" + case DHCPOptXFontServer: + return "XFontServer" + case DHCPOptXDisplayManager: + return "XDisplayManager" + case DHCPOptEnd: + return "(end)" + case DHCPOptSIPServers: + return "SipServers" + case DHCPOptRequestIP: + return "RequestIP" + case DHCPOptLeaseTime: + return "LeaseTime" + case DHCPOptExtOptions: + return "ExtOpts" + case DHCPOptMessageType: + return "MessageType" + case DHCPOptServerID: + return "ServerID" + case DHCPOptParamsRequest: + return "ParamsRequest" + case DHCPOptMessage: + return "Message" + case DHCPOptMaxMessageSize: + return "MaxDHCPSize" + case DHCPOptT1: + return "Timer1" + case DHCPOptT2: + return "Timer2" + case DHCPOptClassID: + return "ClassID" + case DHCPOptClientID: + return "ClientID" + case DHCPOptDomainSearch: + return "DomainSearch" + case DHCPOptClasslessStaticRoute: + return "ClasslessStaticRoute" + default: + return "Unknown" + } +} + +// DHCPOption rerpresents a DHCP option. +type DHCPOption struct { + Type DHCPOpt + Length uint8 + Data []byte +} + +// String returns a string version of a DHCP Option. +func (o DHCPOption) String() string { + switch o.Type { + + case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath, + DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer, + DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string + return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data)) + + case DHCPOptMessageType: + if len(o.Data) != 1 { + return fmt.Sprintf("Option(%s:INVALID)", o.Type) + } + return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0])) + + case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr, + DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP + if len(o.Data) < 4 { + return fmt.Sprintf("Option(%s:INVALID)", o.Type) + } + return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data)) + + case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout, + DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32 + if len(o.Data) != 4 { + return fmt.Sprintf("Option(%s:INVALID)", o.Type) + } + return fmt.Sprintf("Option(%s:%d)", o.Type, + uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3])) + + case DHCPOptParamsRequest: + buf := &bytes.Buffer{} + buf.WriteString(fmt.Sprintf("Option(%s:", o.Type)) + for i, v := range o.Data { + buf.WriteString(DHCPOpt(v).String()) + if i+1 != len(o.Data) { + buf.WriteByte(',') + } + } + buf.WriteString(")") + return buf.String() + + default: + return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data) + } +} + +// NewDHCPOption constructs a new DHCPOption with a given type and data. +func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption { + o := DHCPOption{Type: t} + if data != nil { + o.Data = data + o.Length = uint8(len(data)) + } + return o +} + +func (o *DHCPOption) encode(b []byte) error { + switch o.Type { + case DHCPOptPad, DHCPOptEnd: + b[0] = byte(o.Type) + default: + if o.Length > 253 { + return errors.New("data too long to encode") + } + b[0] = byte(o.Type) + b[1] = o.Length + copy(b[2:], o.Data) + } + return nil +} + +func (o *DHCPOption) decode(data []byte) error { + if len(data) < 1 { + // Pad/End have a length of 1 + return errors.New("Not enough data to decode") + } + o.Type = DHCPOpt(data[0]) + switch o.Type { + case DHCPOptPad, DHCPOptEnd: + o.Data = nil + default: + if len(data) < 3 { + return errors.New("Not enough data to decode") + } + o.Length = data[1] + if o.Length > 253 { + return errors.New("data too long to decode") + } + o.Data = data[2 : 2+o.Length] + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/dns.go b/vendor/github.com/google/gopacket/layers/dns.go new file mode 100644 index 00000000..2368a28c --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dns.go @@ -0,0 +1,894 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// DNSClass defines the class associated with a request/response. Different DNS +// classes can be thought of as an array of parallel namespace trees. +type DNSClass uint16 + +// DNSClass known values. +const ( + DNSClassIN DNSClass = 1 // Internet + DNSClassCS DNSClass = 2 // the CSNET class (Obsolete) + DNSClassCH DNSClass = 3 // the CHAOS class + DNSClassHS DNSClass = 4 // Hesiod [Dyer 87] + DNSClassAny DNSClass = 255 // AnyClass +) + +func (dc DNSClass) String() string { + switch dc { + default: + return "Unknown" + case DNSClassIN: + return "IN" + case DNSClassCS: + return "CS" + case DNSClassCH: + return "CH" + case DNSClassHS: + return "HS" + case DNSClassAny: + return "Any" + } +} + +// DNSType defines the type of data being requested/returned in a +// question/answer. +type DNSType uint16 + +// DNSType known values. +const ( + DNSTypeA DNSType = 1 // a host address + DNSTypeNS DNSType = 2 // an authoritative name server + DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX) + DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX) + DNSTypeCNAME DNSType = 5 // the canonical name for an alias + DNSTypeSOA DNSType = 6 // marks the start of a zone of authority + DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL) + DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL) + DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL) + DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL) + DNSTypeWKS DNSType = 11 // a well known service description + DNSTypePTR DNSType = 12 // a domain name pointer + DNSTypeHINFO DNSType = 13 // host information + DNSTypeMINFO DNSType = 14 // mailbox or mail list information + DNSTypeMX DNSType = 15 // mail exchange + DNSTypeTXT DNSType = 16 // text strings + DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596] + DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195] +) + +func (dt DNSType) String() string { + switch dt { + default: + return "Unknown" + case DNSTypeA: + return "A" + case DNSTypeNS: + return "NS" + case DNSTypeMD: + return "MD" + case DNSTypeMF: + return "MF" + case DNSTypeCNAME: + return "CNAME" + case DNSTypeSOA: + return "SOA" + case DNSTypeMB: + return "MB" + case DNSTypeMG: + return "MG" + case DNSTypeMR: + return "MR" + case DNSTypeNULL: + return "NULL" + case DNSTypeWKS: + return "WKS" + case DNSTypePTR: + return "PTR" + case DNSTypeHINFO: + return "HINFO" + case DNSTypeMINFO: + return "MINFO" + case DNSTypeMX: + return "MX" + case DNSTypeTXT: + return "TXT" + case DNSTypeAAAA: + return "AAAA" + case DNSTypeSRV: + return "SRV" + } +} + +// DNSResponseCode provides response codes for question answers. +type DNSResponseCode uint8 + +// DNSResponseCode known values. +const ( + DNSResponseCodeNoErr DNSResponseCode = 0 // No error + DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035] + DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035] + DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035] + DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035] + DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035] + DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136] + DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136] + DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136] + DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136] + DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136] + DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671] + DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845] + DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845] + DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845] + DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930] + DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930] + DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930] + DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635] +) + +func (drc DNSResponseCode) String() string { + switch drc { + default: + return "Unknown" + case DNSResponseCodeNoErr: + return "No Error" + case DNSResponseCodeFormErr: + return "Format Error" + case DNSResponseCodeServFail: + return "Server Failure " + case DNSResponseCodeNXDomain: + return "Non-Existent Domain" + case DNSResponseCodeNotImp: + return "Not Implemented" + case DNSResponseCodeRefused: + return "Query Refused" + case DNSResponseCodeYXDomain: + return "Name Exists when it should not" + case DNSResponseCodeYXRRSet: + return "RR Set Exists when it should not" + case DNSResponseCodeNXRRSet: + return "RR Set that should exist does not" + case DNSResponseCodeNotAuth: + return "Server Not Authoritative for zone" + case DNSResponseCodeNotZone: + return "Name not contained in zone" + case DNSResponseCodeBadVers: + return "Bad OPT Version" + case DNSResponseCodeBadKey: + return "Key not recognized" + case DNSResponseCodeBadTime: + return "Signature out of time window" + case DNSResponseCodeBadMode: + return "Bad TKEY Mode" + case DNSResponseCodeBadName: + return "Duplicate key name" + case DNSResponseCodeBadAlg: + return "Algorithm not supported" + case DNSResponseCodeBadTruc: + return "Bad Truncation" + } +} + +// DNSOpCode defines a set of different operation types. +type DNSOpCode uint8 + +// DNSOpCode known values. +const ( + DNSOpCodeQuery DNSOpCode = 0 // Query [RFC1035] + DNSOpCodeIQuery DNSOpCode = 1 // Inverse Query Obsolete [RFC3425] + DNSOpCodeStatus DNSOpCode = 2 // Status [RFC1035] + DNSOpCodeNotify DNSOpCode = 4 // Notify [RFC1996] + DNSOpCodeUpdate DNSOpCode = 5 // Update [RFC2136] +) + +func (doc DNSOpCode) String() string { + switch doc { + default: + return "Unknown" + case DNSOpCodeQuery: + return "Query" + case DNSOpCodeIQuery: + return "Inverse Query" + case DNSOpCodeStatus: + return "Status" + case DNSOpCodeNotify: + return "Notify" + case DNSOpCodeUpdate: + return "Update" + } +} + +// DNS is specified in RFC 1034 / RFC 1035 +// +---------------------+ +// | Header | +// +---------------------+ +// | Question | the question for the name server +// +---------------------+ +// | Answer | RRs answering the question +// +---------------------+ +// | Authority | RRs pointing toward an authority +// +---------------------+ +// | Additional | RRs holding additional information +// +---------------------+ +// +// DNS Header +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | ID | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | QDCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | ANCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | NSCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | ARCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// DNS contains data from a single Domain Name Service packet. +type DNS struct { + BaseLayer + + // Header fields + ID uint16 + QR bool + OpCode DNSOpCode + + AA bool // Authoritative answer + TC bool // Truncated + RD bool // Recursion desired + RA bool // Recursion available + Z uint8 // Resrved for future use + + ResponseCode DNSResponseCode + QDCount uint16 // Number of questions to expect + ANCount uint16 // Number of answers to expect + NSCount uint16 // Number of authorities to expect + ARCount uint16 // Number of additional records to expect + + // Entries + Questions []DNSQuestion + Answers []DNSResourceRecord + Authorities []DNSResourceRecord + Additionals []DNSResourceRecord + + // buffer for doing name decoding. We use a single reusable buffer to avoid + // name decoding on a single object via multiple DecodeFromBytes calls + // requiring constant allocation of small byte slices. + buffer []byte +} + +// LayerType returns gopacket.LayerTypeDNS. +func (d *DNS) LayerType() gopacket.LayerType { return LayerTypeDNS } + +// decodeDNS decodes the byte slice into a DNS type. It also +// setups the application Layer in PacketBuilder. +func decodeDNS(data []byte, p gopacket.PacketBuilder) error { + d := &DNS{} + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(d) + p.SetApplicationLayer(d) + return nil +} + +// DecodeFromBytes decodes the slice into the DNS struct. +func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + d.buffer = d.buffer[:0] + + if len(data) < 12 { + df.SetTruncated() + return errors.New("DNS packet too short") + } + + // since there are no further layers, the baselayer's content is + // pointing to this layer + d.BaseLayer = BaseLayer{Contents: data[:len(data)]} + d.ID = binary.BigEndian.Uint16(data[:2]) + d.QR = data[2]&0x80 != 0 + d.OpCode = DNSOpCode(data[2]>>3) & 0x0F + d.AA = data[2]&0x04 != 0 + d.TC = data[2]&0x02 != 0 + d.RD = data[2]&0x01 != 0 + d.RA = data[3]&0x80 != 0 + d.Z = uint8(data[3]>>4) & 0x7 + d.ResponseCode = DNSResponseCode(data[3] & 0xF) + d.QDCount = binary.BigEndian.Uint16(data[4:6]) + d.ANCount = binary.BigEndian.Uint16(data[6:8]) + d.NSCount = binary.BigEndian.Uint16(data[8:10]) + d.ARCount = binary.BigEndian.Uint16(data[10:12]) + + d.Questions = d.Questions[:0] + d.Answers = d.Answers[:0] + d.Authorities = d.Authorities[:0] + d.Additionals = d.Additionals[:0] + + offset := 12 + var err error + for i := 0; i < int(d.QDCount); i++ { + var q DNSQuestion + if offset, err = q.decode(data, offset, df, &d.buffer); err != nil { + return err + } + d.Questions = append(d.Questions, q) + } + + // For some horrible reason, if we do the obvious thing in this loop: + // var r DNSResourceRecord + // if blah := r.decode(blah); err != nil { + // return err + // } + // d.Foo = append(d.Foo, r) + // the Go compiler thinks that 'r' escapes to the heap, causing a malloc for + // every Answer, Authority, and Additional. To get around this, we do + // something really silly: we append an empty resource record to our slice, + // then use the last value in the slice to call decode. Since the value is + // already in the slice, there's no WAY it can escape... on the other hand our + // code is MUCH uglier :( + for i := 0; i < int(d.ANCount); i++ { + d.Answers = append(d.Answers, DNSResourceRecord{}) + if offset, err = d.Answers[i].decode(data, offset, df, &d.buffer); err != nil { + d.Answers = d.Answers[:i] // strip off erroneous value + return err + } + } + for i := 0; i < int(d.NSCount); i++ { + d.Authorities = append(d.Authorities, DNSResourceRecord{}) + if offset, err = d.Authorities[i].decode(data, offset, df, &d.buffer); err != nil { + d.Authorities = d.Authorities[:i] // strip off erroneous value + return err + } + } + for i := 0; i < int(d.ARCount); i++ { + d.Additionals = append(d.Additionals, DNSResourceRecord{}) + if offset, err = d.Additionals[i].decode(data, offset, df, &d.buffer); err != nil { + d.Additionals = d.Additionals[:i] // strip off erroneous value + return err + } + } + + if uint16(len(d.Questions)) != d.QDCount { + return errors.New("Invalid query decoding, not the right number of questions") + } else if uint16(len(d.Answers)) != d.ANCount { + return errors.New("Invalid query decoding, not the right number of answers") + } else if uint16(len(d.Authorities)) != d.NSCount { + return errors.New("Invalid query decoding, not the right number of authorities") + } else if uint16(len(d.Additionals)) != d.ARCount { + return errors.New("Invalid query decoding, not the right number of additionals info") + } + return nil +} + +// CanDecode implements gopacket.DecodingLayer. +func (d *DNS) CanDecode() gopacket.LayerClass { + return LayerTypeDNS +} + +// NextLayerType implements gopacket.DecodingLayer. +func (d *DNS) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// Payload returns nil. +func (d *DNS) Payload() []byte { + return nil +} + +func b2i(b bool) int { + if b { + return 1 + } + return 0 +} + +func recSize(rr *DNSResourceRecord) int { + switch rr.Type { + case DNSTypeA: + return 4 + case DNSTypeAAAA: + return 16 + case DNSTypeNS: + return len(rr.NS) + 2 + case DNSTypeCNAME: + return len(rr.CNAME) + 2 + case DNSTypePTR: + return len(rr.PTR) + 2 + case DNSTypeSOA: + return len(rr.SOA.MName) + 2 + len(rr.SOA.RName) + 2 + 20 + case DNSTypeMX: + return 2 + len(rr.MX.Name) + 2 + case DNSTypeTXT: + l := len(rr.TXTs) + for _, txt := range rr.TXTs { + l += len(txt) + } + return l + case DNSTypeSRV: + return 6 + len(rr.SRV.Name) + 2 + } + + return 0 +} + +func computeSize(recs []DNSResourceRecord) int { + sz := 0 + for _, rr := range recs { + sz += len(rr.Name) + 14 + sz += recSize(&rr) + } + return sz +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + dsz := 0 + for _, q := range d.Questions { + dsz += len(q.Name) + 6 + } + dsz += computeSize(d.Answers) + dsz += computeSize(d.Authorities) + dsz += computeSize(d.Additionals) + + bytes, err := b.PrependBytes(12 + dsz) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, d.ID) + bytes[2] = byte((b2i(d.QR) << 7) | (int(d.OpCode) << 3) | (b2i(d.AA) << 2) | (b2i(d.TC) << 1) | b2i(d.RD)) + bytes[3] = byte((b2i(d.RA) << 7) | (int(d.Z) << 4) | int(d.ResponseCode)) + + if opts.FixLengths { + d.QDCount = uint16(len(d.Questions)) + d.ANCount = uint16(len(d.Answers)) + d.NSCount = uint16(len(d.Authorities)) + d.ARCount = uint16(len(d.Additionals)) + } + binary.BigEndian.PutUint16(bytes[4:], d.QDCount) + binary.BigEndian.PutUint16(bytes[6:], d.ANCount) + binary.BigEndian.PutUint16(bytes[8:], d.NSCount) + binary.BigEndian.PutUint16(bytes[10:], d.ARCount) + + off := 12 + for _, qd := range d.Questions { + n := qd.encode(bytes, off) + off += n + } + + for i := range d.Answers { + // done this way so we can modify DNSResourceRecord to fix + // lengths if requested + qa := &d.Answers[i] + n, err := qa.encode(bytes, off, opts) + if err != nil { + return err + } + off += n + } + + for i := range d.Authorities { + qa := &d.Authorities[i] + n, err := qa.encode(bytes, off, opts) + if err != nil { + return err + } + off += n + } + for i := range d.Additionals { + qa := &d.Additionals[i] + n, err := qa.encode(bytes, off, opts) + if err != nil { + return err + } + off += n + } + + return nil +} + +var errMaxRecursion = errors.New("max DNS recursion level hit") + +const maxRecursionLevel = 255 + +func decodeName(data []byte, offset int, buffer *[]byte, level int) ([]byte, int, error) { + if level > maxRecursionLevel { + return nil, 0, errMaxRecursion + } else if offset >= len(data) { + return nil, 0, errors.New("dns name offset too high") + } else if offset < 0 { + return nil, 0, errors.New("dns name offset is negative") + } + start := len(*buffer) + index := offset + if data[index] == 0x00 { + return nil, index + 1, nil + } +loop: + for data[index] != 0x00 { + switch data[index] & 0xc0 { + default: + /* RFC 1035 + A domain name represented as a sequence of labels, where + each label consists of a length octet followed by that + number of octets. The domain name terminates with the + zero length octet for the null label of the root. Note + that this field may be an odd number of octets; no + padding is used. + */ + index2 := index + int(data[index]) + 1 + if index2-offset > 255 { + return nil, 0, errors.New("dns name is too long") + } else if index2 < index+1 || index2 > len(data) { + return nil, 0, errors.New("dns name uncomputable: invalid index") + } + *buffer = append(*buffer, '.') + *buffer = append(*buffer, data[index+1:index2]...) + index = index2 + + case 0xc0: + /* RFC 1035 + The pointer takes the form of a two octet sequence. + + The first two bits are ones. This allows a pointer to + be distinguished from a label, since the label must + begin with two zero bits because labels are restricted + to 63 octets or less. (The 10 and 01 combinations are + reserved for future use.) The OFFSET field specifies + an offset from the start of the message (i.e., the + first octet of the ID field in the domain header). A + zero offset specifies the first byte of the ID field, + etc. + + The compression scheme allows a domain name in a message to be + represented as either: + - a sequence of labels ending in a zero octet + - a pointer + - a sequence of labels ending with a pointer + */ + if index+2 > len(data) { + return nil, 0, errors.New("dns offset pointer too high") + } + offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff) + if offsetp > len(data) { + return nil, 0, errors.New("dns offset pointer too high") + } + // This looks a little tricky, but actually isn't. Because of how + // decodeName is written, calling it appends the decoded name to the + // current buffer. We already have the start of the buffer, then, so + // once this call is done buffer[start:] will contain our full name. + _, _, err := decodeName(data, offsetp, buffer, level+1) + if err != nil { + return nil, 0, err + } + index++ // pointer is two bytes, so add an extra byte here. + break loop + /* EDNS, or other DNS option ? */ + case 0x40: // RFC 2673 + return nil, 0, fmt.Errorf("qname '0x40' - RFC 2673 unsupported yet (data=%x index=%d)", + data[index], index) + + case 0x80: + return nil, 0, fmt.Errorf("qname '0x80' unsupported yet (data=%x index=%d)", + data[index], index) + } + if index >= len(data) { + return nil, 0, errors.New("dns index walked out of range") + } + } + if len(*buffer) <= start { + return nil, 0, errors.New("no dns data found for name") + } + return (*buffer)[start+1:], index + 1, nil +} + +// DNSQuestion wraps a single request (question) within a DNS query. +type DNSQuestion struct { + Name []byte + Type DNSType + Class DNSClass +} + +func (q *DNSQuestion) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) { + name, endq, err := decodeName(data, offset, buffer, 1) + if err != nil { + return 0, err + } + + q.Name = name + q.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2])) + q.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4])) + + return endq + 4, nil +} + +func (q *DNSQuestion) encode(data []byte, offset int) int { + noff := encodeName(q.Name, data, offset) + binary.BigEndian.PutUint16(data[noff:], uint16(q.Type)) + binary.BigEndian.PutUint16(data[noff+2:], uint16(q.Class)) + return len(q.Name) + 6 +} + +// DNSResourceRecord +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | | +// / / +// / NAME / +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TYPE | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | CLASS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TTL | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | RDLENGTH | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| +// / RDATA / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// DNSResourceRecord wraps the data from a single DNS resource within a +// response. +type DNSResourceRecord struct { + // Header + Name []byte + Type DNSType + Class DNSClass + TTL uint32 + + // RDATA Raw Values + DataLength uint16 + Data []byte + + // RDATA Decoded Values + IP net.IP + NS, CNAME, PTR []byte + TXTs [][]byte + SOA DNSSOA + SRV DNSSRV + MX DNSMX + + // Undecoded TXT for backward compatibility + TXT []byte +} + +// decode decodes the resource record, returning the total length of the record. +func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) { + name, endq, err := decodeName(data, offset, buffer, 1) + if err != nil { + return 0, err + } + + rr.Name = name + rr.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2])) + rr.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4])) + rr.TTL = binary.BigEndian.Uint32(data[endq+4 : endq+8]) + rr.DataLength = binary.BigEndian.Uint16(data[endq+8 : endq+10]) + end := endq + 10 + int(rr.DataLength) + if end > len(data) { + return 0, fmt.Errorf("resource record length exceeds data") + } + rr.Data = data[endq+10 : end] + + if err = rr.decodeRData(data, endq+10, buffer); err != nil { + return 0, err + } + + return endq + 10 + int(rr.DataLength), nil +} + +func encodeName(name []byte, data []byte, offset int) int { + l := 0 + for i := range name { + if name[i] == '.' { + data[offset+i-l] = byte(l) + l = 0 + } else { + // skip one to write the length + data[offset+i+1] = name[i] + l++ + } + } + // length for final portion + data[offset+len(name)-l] = byte(l) + data[offset+len(name)+1] = 0x00 // terminal + return offset + len(name) + 2 +} + +func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.SerializeOptions) (int, error) { + + noff := encodeName(rr.Name, data, offset) + + binary.BigEndian.PutUint16(data[noff:], uint16(rr.Type)) + binary.BigEndian.PutUint16(data[noff+2:], uint16(rr.Class)) + binary.BigEndian.PutUint32(data[noff+4:], uint32(rr.TTL)) + + switch rr.Type { + case DNSTypeA: + copy(data[noff+10:], rr.IP.To4()) + case DNSTypeAAAA: + copy(data[noff+10:], rr.IP) + case DNSTypeNS: + encodeName(rr.NS, data, noff+10) + case DNSTypeCNAME: + encodeName(rr.CNAME, data, noff+10) + case DNSTypePTR: + encodeName(rr.PTR, data, noff+10) + case DNSTypeSOA: + noff2 := encodeName(rr.SOA.MName, data, noff+10) + noff2 = encodeName(rr.SOA.RName, data, noff2) + binary.BigEndian.PutUint32(data[noff2:], rr.SOA.Serial) + binary.BigEndian.PutUint32(data[noff2+4:], rr.SOA.Refresh) + binary.BigEndian.PutUint32(data[noff2+8:], rr.SOA.Retry) + binary.BigEndian.PutUint32(data[noff2+12:], rr.SOA.Expire) + binary.BigEndian.PutUint32(data[noff2+16:], rr.SOA.Minimum) + case DNSTypeMX: + binary.BigEndian.PutUint16(data[noff+10:], rr.MX.Preference) + encodeName(rr.MX.Name, data, noff+12) + case DNSTypeTXT: + noff2 := noff + 10 + for _, txt := range rr.TXTs { + data[noff2] = byte(len(txt)) + copy(data[noff2+1:], txt) + noff2 += 1 + len(txt) + } + case DNSTypeSRV: + binary.BigEndian.PutUint16(data[noff+10:], rr.SRV.Priority) + binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight) + binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port) + encodeName(rr.SRV.Name, data, noff+16) + default: + return 0, fmt.Errorf("serializing resource record of type %v not supported", rr.Type) + } + + // DataLength + dSz := recSize(rr) + binary.BigEndian.PutUint16(data[noff+8:], uint16(dSz)) + + if opts.FixLengths { + rr.DataLength = uint16(dSz) + } + + return len(rr.Name) + 1 + 11 + dSz, nil +} + +func (rr *DNSResourceRecord) String() string { + + if rr.Class == DNSClassIN { + switch rr.Type { + case DNSTypeA, DNSTypeAAAA: + return rr.IP.String() + case DNSTypeNS: + return "NS " + string(rr.NS) + case DNSTypeCNAME: + return "CNAME " + string(rr.CNAME) + case DNSTypePTR: + return "PTR " + string(rr.PTR) + case DNSTypeTXT: + return "TXT " + string(rr.TXT) + } + } + + return fmt.Sprintf("<%v, %v>", rr.Class, rr.Type) +} + +func decodeCharacterStrings(data []byte) ([][]byte, error) { + strings := make([][]byte, 0, 1) + end := len(data) + for index, index2 := 0, 0; index != end; index = index2 { + index2 = index + 1 + int(data[index]) // index increases by 1..256 and does not overflow + if index2 > end { + return nil, errors.New("Insufficient data for a ") + } + strings = append(strings, data[index+1:index2]) + } + return strings, nil +} + +func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte) error { + switch rr.Type { + case DNSTypeA: + rr.IP = rr.Data + case DNSTypeAAAA: + rr.IP = rr.Data + case DNSTypeTXT, DNSTypeHINFO: + rr.TXT = rr.Data + txts, err := decodeCharacterStrings(rr.Data) + if err != nil { + return err + } + rr.TXTs = txts + case DNSTypeNS: + name, _, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.NS = name + case DNSTypeCNAME: + name, _, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.CNAME = name + case DNSTypePTR: + name, _, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.PTR = name + case DNSTypeSOA: + name, endq, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.SOA.MName = name + name, endq, err = decodeName(data, endq, buffer, 1) + if err != nil { + return err + } + rr.SOA.RName = name + rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4]) + rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8]) + rr.SOA.Retry = binary.BigEndian.Uint32(data[endq+8 : endq+12]) + rr.SOA.Expire = binary.BigEndian.Uint32(data[endq+12 : endq+16]) + rr.SOA.Minimum = binary.BigEndian.Uint32(data[endq+16 : endq+20]) + case DNSTypeMX: + rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2]) + name, _, err := decodeName(data, offset+2, buffer, 1) + if err != nil { + return err + } + rr.MX.Name = name + case DNSTypeSRV: + rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2]) + rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4]) + rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6]) + name, _, err := decodeName(data, offset+6, buffer, 1) + if err != nil { + return err + } + rr.SRV.Name = name + } + return nil +} + +// DNSSOA is a Start of Authority record. Each domain requires a SOA record at +// the cutover where a domain is delegated from its parent. +type DNSSOA struct { + MName, RName []byte + Serial, Refresh, Retry, Expire, Minimum uint32 +} + +// DNSSRV is a Service record, defining a location (hostname/port) of a +// server/service. +type DNSSRV struct { + Priority, Weight, Port uint16 + Name []byte +} + +// DNSMX is a mail exchange record, defining a mail server for a recipient's +// domain. +type DNSMX struct { + Preference uint16 + Name []byte +} diff --git a/vendor/github.com/google/gopacket/layers/dns_test.go b/vendor/github.com/google/gopacket/layers/dns_test.go new file mode 100644 index 00000000..264513b1 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dns_test.go @@ -0,0 +1,833 @@ +// Copyright 2012, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "net" + "strings" + "testing" + + "github.com/google/gopacket" +) + +// testPacketDNSRegression is the packet: +// 11:08:05.708342 IP 109.194.160.4.57766 > 95.211.92.14.53: 63000% [1au] A? picslife.ru. (40) +// 0x0000: 0022 19b6 7e22 000f 35bb 0b40 0800 4500 ."..~"..5..@..E. +// 0x0010: 0044 89c4 0000 3811 2f3d 6dc2 a004 5fd3 .D....8./=m..._. +// 0x0020: 5c0e e1a6 0035 0030 a597 f618 0010 0001 \....5.0........ +// 0x0030: 0000 0000 0001 0870 6963 736c 6966 6502 .......picslife. +// 0x0040: 7275 0000 0100 0100 0029 1000 0000 8000 ru.......)...... +// 0x0050: 0000 .. +var testPacketDNSRegression = []byte{ + 0x00, 0x22, 0x19, 0xb6, 0x7e, 0x22, 0x00, 0x0f, 0x35, 0xbb, 0x0b, 0x40, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x44, 0x89, 0xc4, 0x00, 0x00, 0x38, 0x11, 0x2f, 0x3d, 0x6d, 0xc2, 0xa0, 0x04, 0x5f, 0xd3, + 0x5c, 0x0e, 0xe1, 0xa6, 0x00, 0x35, 0x00, 0x30, 0xa5, 0x97, 0xf6, 0x18, 0x00, 0x10, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x70, 0x69, 0x63, 0x73, 0x6c, 0x69, 0x66, 0x65, 0x02, + 0x72, 0x75, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, +} + +func TestPacketDNSRegression(t *testing.T) { + p := gopacket.NewPacket(testPacketDNSRegression, LinkTypeEthernet, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeDNS}, t) +} +func BenchmarkDecodePacketDNSRegression(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDNSRegression, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// response to `dig TXT google.com` over IPv4 link: +var testParseDNSTypeTXTValue = `v=spf1 include:_spf.google.com ~all` +var testParseDNSTypeTXT = []byte{ + 0x02, 0x00, 0x00, 0x00, // PF_INET + 0x45, 0x00, 0x00, 0x73, 0x00, 0x00, 0x40, 0x00, 0x39, 0x11, 0x64, 0x98, 0xd0, 0x43, 0xde, 0xde, + 0x0a, 0xba, 0x23, 0x06, 0x00, 0x35, 0x81, 0xb2, 0x00, 0x5f, 0xdc, 0xb5, 0x98, 0x71, 0x81, 0x80, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x10, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, + 0x0e, 0x10, 0x00, 0x24, 0x23, 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x20, 0x7e, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, +} + +func TestParseDNSTypeTXT(t *testing.T) { + p := gopacket.NewPacket(testParseDNSTypeTXT, LinkTypeNull, testDecodeOptions) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeLoopback, LayerTypeIPv4, LayerTypeUDP, LayerTypeDNS}, t) + answers := p.Layer(LayerTypeDNS).(*DNS).Answers + if len(answers) != 1 { + t.Error("Failed to parse 1 DNS answer") + } + if len(answers[0].TXTs) != 1 { + t.Error("Failed to parse 1 TXT record") + } + txt := string(answers[0].TXTs[0]) + if txt != testParseDNSTypeTXTValue { + t.Errorf("Incorrect TXT value, expected %q, got %q", testParseDNSTypeTXTValue, txt) + } +} + +func testQuestionEqual(t *testing.T, i int, exp, got DNSQuestion) { + if !bytes.Equal(exp.Name, got.Name) { + t.Errorf("expected Questions[%d].Name = %v, got %v", i, string(exp.Name), string(got.Name)) + } + if exp.Type != got.Type { + t.Errorf("expected Questions[%d].Type = %v, got %v", i, exp.Type, got.Type) + } + if exp.Class != got.Class { + t.Errorf("expected Questions[%d].Class = %v, got %v", i, exp.Class, got.Class) + } +} + +func testResourceEqual(t *testing.T, i int, name string, exp, got DNSResourceRecord) { + if !bytes.Equal(exp.Name, got.Name) { + t.Errorf("expected %s[%d].Name = %v, got %v", name, i, string(exp.Name), string(got.Name)) + } + + if exp.Type != got.Type { + t.Errorf("expected %s[%d].Type = %v, got %v", name, i, exp.Type, got.Type) + } + + if exp.Class != got.Class { + t.Errorf("expected %s[%d].Class = %v, got %v", name, i, exp.Class, got.Class) + } + + if exp.TTL != got.TTL { + t.Errorf("expected %s[%d].TTL = %v, got %v", name, i, exp.TTL, got.TTL) + } + if exp.DataLength != got.DataLength { + t.Errorf("expected %s[%d].DataLength = %v, got %v", name, i, exp.DataLength, got.DataLength) + } + + // we don't check .Data + + if !exp.IP.Equal(got.IP) { + t.Errorf("expected %s[%d].IP = %v, got %v", name, i, exp.IP, got.IP) + } + if !bytes.Equal(exp.NS, got.NS) { + t.Errorf("expected %s[%d].NS = %v, got %v", name, i, exp.NS, got.NS) + } + if !bytes.Equal(exp.CNAME, got.CNAME) { + t.Errorf("expected %s[%d].CNAME = %v, got %v", name, i, exp.CNAME, got.CNAME) + } + if !bytes.Equal(exp.PTR, got.PTR) { + t.Errorf("expected %s[%d].PTR = %v, got %v", name, i, exp.PTR, got.PTR) + } + if len(exp.TXTs) != len(got.TXTs) { + t.Errorf("expected %s[%d].TXTs = %v, got %v", name, i, exp.TXTs, got.TXTs) + } + for j := range exp.TXTs { + if !bytes.Equal(exp.TXTs[j], got.TXTs[j]) { + t.Errorf("expected %s[%d].TXTs[%d] = %v, got %v", name, i, j, exp.TXTs[j], got.TXTs[j]) + } + } + + // SOA + if !bytes.Equal(exp.SOA.MName, got.SOA.MName) { + t.Errorf("expected %s[%d].SOA.MName = %v, got %v", name, i, exp.SOA.MName, got.SOA.MName) + } + if !bytes.Equal(exp.SOA.RName, got.SOA.RName) { + t.Errorf("expected %s[%d].SOA.RName = %v, got %v", name, i, exp.SOA.RName, got.SOA.RName) + } + if exp.SOA.Serial != got.SOA.Serial { + t.Errorf("expected %s[%d].SOA.Serial = %v, got %v", name, i, exp.SOA.Serial, got.SOA.Serial) + } + if exp.SOA.Refresh != got.SOA.Refresh { + t.Errorf("expected %s[%d].SOA.Refresh = %v, got %v", name, i, exp.SOA.Refresh, got.SOA.Refresh) + } + if exp.SOA.Retry != got.SOA.Retry { + t.Errorf("expected %s[%d].SOA.Retry = %v, got %v", name, i, exp.SOA.Retry, got.SOA.Retry) + } + if exp.SOA.Expire != got.SOA.Expire { + t.Errorf("expected %s[%d].SOA.Expire = %v, got %v", name, i, exp.SOA.Expire, got.SOA.Expire) + } + if exp.SOA.Minimum != got.SOA.Minimum { + t.Errorf("expected %s[%d].SOA.Minimum = %v, got %v", name, i, exp.SOA.Minimum, got.SOA.Minimum) + } + + // SRV + if !bytes.Equal(exp.SRV.Name, got.SRV.Name) { + t.Errorf("expected %s[%d].SRV.Name = %v, got %v", name, i, exp.SRV.Name, got.SRV.Name) + } + if exp.SRV.Weight != got.SRV.Weight { + t.Errorf("expected %s[%d].SRV.Weight = %v, got %v", name, i, exp.SRV.Weight, got.SRV.Weight) + } + if exp.SRV.Port != got.SRV.Port { + t.Errorf("expected %s[%d].SRV.Port = %v, got %v", name, i, exp.SRV.Port, got.SRV.Port) + } + // MX + if !bytes.Equal(exp.MX.Name, got.MX.Name) { + t.Errorf("expected %s[%d].MX.Name = %v, got %v", name, i, exp.MX.Name, got.MX.Name) + } + if exp.MX.Preference != got.MX.Preference { + t.Errorf("expected %s[%d].MX.Preference = %v, got %v", name, i, exp.MX.Preference, got.MX.Preference) + } +} + +func testDNSEqual(t *testing.T, exp, got *DNS) { + if exp.ID != got.ID { + t.Errorf("expected ID = %v, got %v", exp.ID, got.ID) + } + if exp.AA != got.AA { + t.Errorf("expected AA = %v, got %v", exp.AA, got.AA) + } + if exp.OpCode != got.OpCode { + t.Errorf("expected OpCode = %v, got %v", exp.OpCode, got.OpCode) + } + if exp.AA != got.AA { + t.Errorf("expected AA = %v, got %v", exp.AA, got.AA) + } + if exp.TC != got.TC { + t.Errorf("expected TC = %v, got %v", exp.TC, got.TC) + } + if exp.RD != got.RD { + t.Errorf("expected RD = %v, got %v", exp.RD, got.RD) + } + if exp.RA != got.RA { + t.Errorf("expected RA = %v, got %v", exp.RA, got.RA) + } + if exp.Z != got.Z { + t.Errorf("expected Z = %v, got %v", exp.Z, got.Z) + } + if exp.ResponseCode != got.ResponseCode { + t.Errorf("expected ResponseCode = %v, got %v", exp.ResponseCode, got.ResponseCode) + } + if exp.QDCount != got.QDCount { + t.Errorf("expected QDCount = %v, got %v", exp.QDCount, got.QDCount) + } + if exp.ANCount != got.ANCount { + t.Errorf("expected ANCount = %v, got %v", exp.ANCount, got.ANCount) + } + if exp.ANCount != got.ANCount { + t.Errorf("expected ANCount = %v, got %v", exp.ANCount, got.ANCount) + } + if exp.NSCount != got.NSCount { + t.Errorf("expected NSCount = %v, got %v", exp.NSCount, got.NSCount) + } + if exp.ARCount != got.ARCount { + t.Errorf("expected ARCount = %v, got %v", exp.ARCount, got.ARCount) + } + + if len(exp.Questions) != len(got.Questions) { + t.Errorf("expected %d Questions, got %d", len(exp.Questions), len(got.Questions)) + } + for i := range exp.Questions { + testQuestionEqual(t, i, exp.Questions[i], got.Questions[i]) + } + + if len(exp.Answers) != len(got.Answers) { + t.Errorf("expected %d Answers, got %d", len(exp.Answers), len(got.Answers)) + } + for i := range exp.Answers { + testResourceEqual(t, i, "Answers", exp.Answers[i], got.Answers[i]) + } + + if len(exp.Authorities) != len(got.Authorities) { + t.Errorf("expected %d Answers, got %d", len(exp.Authorities), len(got.Authorities)) + } + for i := range exp.Authorities { + testResourceEqual(t, i, "Authorities", exp.Authorities[i], got.Authorities[i]) + } + + if len(exp.Additionals) != len(got.Additionals) { + t.Errorf("expected %d Additionals, got %d", len(exp.Additionals), len(got.Additionals)) + } + for i := range exp.Additionals { + testResourceEqual(t, i, "Additionals", exp.Additionals[i], got.Additionals[i]) + } +} + +func TestDNSEncodeQuery(t *testing.T) { + dns := &DNS{ID: 1234, OpCode: DNSOpCodeQuery, RD: true} + dns.Questions = append(dns.Questions, + DNSQuestion{ + Name: []byte("example1.com"), + Type: DNSTypeA, + Class: DNSClassIN, + }) + + dns.Questions = append(dns.Questions, + DNSQuestion{ + Name: []byte("example2.com"), + Type: DNSTypeA, + Class: DNSClassIN, + }) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err := gopacket.SerializeLayers(buf, opts, dns) + if err != nil { + t.Fatal(err) + } + if int(dns.QDCount) != len(dns.Questions) { + t.Errorf("fix lengths did not adjust QDCount, expected %d got %d", len(dns.Questions), dns.QDCount) + } + + p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDNS, testDecodeOptions) + dns2 := p2.Layer(LayerTypeDNS).(*DNS) + testDNSEqual(t, dns, dns2) +} + +func TestDNSEncodeResponse(t *testing.T) { + dns := &DNS{ID: 1234, QR: true, OpCode: DNSOpCodeQuery, + AA: true, RD: true, RA: true} + dns.Questions = append(dns.Questions, + DNSQuestion{ + Name: []byte("example1.com"), + Type: DNSTypeA, + Class: DNSClassIN, + }) + dns.Questions = append(dns.Questions, + DNSQuestion{ + Name: []byte("www.example2.com"), + Type: DNSTypeAAAA, + Class: DNSClassIN, + }) + + dns.Answers = append(dns.Answers, + DNSResourceRecord{ + Name: []byte("example1.com"), + Type: DNSTypeA, + Class: DNSClassIN, + TTL: 1024, + IP: net.IP([]byte{1, 2, 3, 4}), + }) + + dns.Answers = append(dns.Answers, + DNSResourceRecord{ + Name: []byte("www.example2.com"), + Type: DNSTypeAAAA, + Class: DNSClassIN, + TTL: 1024, + IP: net.IP([]byte{5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4}), + }) + + dns.Answers = append(dns.Answers, + DNSResourceRecord{ + Name: []byte("www.example2.com"), + Type: DNSTypeCNAME, + Class: DNSClassIN, + TTL: 1024, + CNAME: []byte("example2.com"), + }) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err := gopacket.SerializeLayers(buf, opts, dns) + if err != nil { + t.Fatal(err) + } + if int(dns.ANCount) != len(dns.Answers) { + t.Errorf("fix lengths did not adjust ANCount, expected %d got %d", len(dns.Answers), dns.ANCount) + } + for i, a := range dns.Answers { + if a.DataLength == 0 { + t.Errorf("fix lengths did not adjust Answers[%d].DataLength", i) + } + } + + p2 := gopacket.NewPacket(buf.Bytes(), LayerTypeDNS, testDecodeOptions) + dns2 := p2.Layer(LayerTypeDNS).(*DNS) + testDNSEqual(t, dns, dns2) +} + +// testDNSMalformedPacket is the packet: +// 10:30:00.389666 IP 10.77.43.131.60718 > 10.1.0.17.53: 18245 updateD [b2&3=0x5420] [18516a] [12064q] [21584n] [12081au][|domain] +// 0x0000: 0000 0101 0000 4e96 1476 afa1 0800 4500 ......N..v....E. +// 0x0010: 0039 d431 0000 f311 b3a0 0a4d 2b83 0a01 .9.1.......M+... +// 0x0020: 0011 ed2e 0035 0025 0832 4745 5420 2f20 .....5.%.2GET./. +// 0x0030: 4854 5450 2f31 2e31 0d0a 486f 7374 3a20 HTTP/1.1..Host:. +// 0x0040: 7777 770d 0a0d 0a www.... +var testDNSMalformedPacket = []byte{ + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x4e, 0x96, 0x14, 0x76, 0xaf, 0xa1, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x39, 0xd4, 0x31, 0x00, 0x00, 0xf3, 0x11, 0xb3, 0xa0, 0x0a, 0x4d, 0x2b, 0x83, 0x0a, 0x01, + 0x00, 0x11, 0xed, 0x2e, 0x00, 0x35, 0x00, 0x25, 0x08, 0x32, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, + 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, + 0x77, 0x77, 0x77, 0x0d, 0x0a, 0x0d, 0x0a, +} + +func TestDNSMalformedPacket(t *testing.T) { + p := gopacket.NewPacket(testDNSMalformedPacket, LinkTypeEthernet, testDecodeOptions) + if errLayer := p.ErrorLayer(); errLayer == nil { + t.Error("No error layer on invalid DNS name") + } else if err := errLayer.Error(); !strings.Contains(err.Error(), "invalid index") { + t.Errorf("unexpected error message: %v", err) + } +} + +// testDNSMalformedPacket2 is the packet: +// 15:14:42.056054 IP 10.77.0.245.53 > 10.1.0.45.38769: 12625 zoneInit YXRRSet- [49833q],[|domain] +// 0x0000: 0055 22af c637 0022 55ac deac 0800 4500 .U"..7."U.....E. +// 0x0010: 0079 3767 4000 3911 f49d 0a4d 00f5 0a01 .y7g@.9....M.... +// 0x0020: 002d 0035 9771 0065 6377 3151 f057 c2a9 .-.5.q.ecw1Q.W.. +// 0x0030: fc6e e86a beb0 f7d4 8599 373e b5f8 9db2 .n.j......7>.... +// 0x0040: a399 21a1 9762 def1 def4 f5ab 5675 023e ..!..b......Vu.> +// 0x0050: c9ca 304f 178a c2ad f2fc 677a 0e4c b892 ..0O......gz.L.. +// 0x0060: ab71 09bb 1ea4 f7c4 fe47 7a39 868b 29a0 .q.......Gz9..). +// 0x0070: 62c4 d184 5b4e 8817 4cc0 d1d0 d430 11d3 b...[N..L....0.. +// 0x0080: d147 543f afc7 1a .GT?... +var testDNSMalformedPacket2 = []byte{ + 0x00, 0x55, 0x22, 0xaf, 0xc6, 0x37, 0x00, 0x22, 0x55, 0xac, 0xde, 0xac, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x79, 0x37, 0x67, 0x40, 0x00, 0x39, 0x11, 0xf4, 0x9d, 0x0a, 0x4d, 0x00, 0xf5, 0x0a, 0x01, + 0x00, 0x2d, 0x00, 0x35, 0x97, 0x71, 0x00, 0x65, 0x63, 0x77, 0x31, 0x51, 0xf0, 0x57, 0xc2, 0xa9, + 0xfc, 0x6e, 0xe8, 0x6a, 0xbe, 0xb0, 0xf7, 0xd4, 0x85, 0x99, 0x37, 0x3e, 0xb5, 0xf8, 0x9d, 0xb2, + 0xa3, 0x99, 0x21, 0xa1, 0x97, 0x62, 0xde, 0xf1, 0xde, 0xf4, 0xf5, 0xab, 0x56, 0x75, 0x02, 0x3e, + 0xc9, 0xca, 0x30, 0x4f, 0x17, 0x8a, 0xc2, 0xad, 0xf2, 0xfc, 0x67, 0x7a, 0x0e, 0x4c, 0xb8, 0x92, + 0xab, 0x71, 0x09, 0xbb, 0x1e, 0xa4, 0xf7, 0xc4, 0xfe, 0x47, 0x7a, 0x39, 0x86, 0x8b, 0x29, 0xa0, + 0x62, 0xc4, 0xd1, 0x84, 0x5b, 0x4e, 0x88, 0x17, 0x4c, 0xc0, 0xd1, 0xd0, 0xd4, 0x30, 0x11, 0xd3, + 0xd1, 0x47, 0x54, 0x3f, 0xaf, 0xc7, 0x1a, +} + +func TestDNSMalformedPacket2(t *testing.T) { + p := gopacket.NewPacket(testDNSMalformedPacket2, LinkTypeEthernet, testDecodeOptions) + if errLayer := p.ErrorLayer(); errLayer == nil { + t.Error("No error layer on invalid DNS name") + } else if err := errLayer.Error(); !strings.Contains(err.Error(), "offset pointer too high") { + t.Errorf("unexpected error message: %v", err) + } +} + +// testMalformedRootQuery is the packet: +// 08:31:18.143065 IP 10.77.0.26.53 > 10.1.0.233.65071: 59508- 0/13/3 (508) +// 0x0000: 0055 22af c637 0022 55ac deac 0800 4500 .U"..7."U.....E. +// 0x0010: 0218 76b2 4000 7211 7ad2 0a4d 001a 0a01 ..v.@.r.z..M.... +// 0x0020: 00e9 0035 fe2f 0204 b8f5 e874 8100 0001 ...5./.....t.... +// 0x0030: 0000 000d 0003 0c61 786b 7663 6863 7063 .......axkvchcpc +// 0x0040: 7073 6c0a 7878 7878 7878 7878 7878 036e psl.xxxxxxxxxx.n +// 0x0050: 6574 0000 0100 0100 0002 0001 0000 0e10 et.............. +// 0x0060: 0014 016d 0c72 6f6f 742d 7365 7276 6572 ...m.root-server +// 0x0070: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x0080: 0014 0161 0c72 6f6f 742d 7365 7276 6572 ...a.root-server +// 0x0090: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x00a0: 0014 0169 0c72 6f6f 742d 7365 7276 6572 ...i.root-server +// 0x00b0: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x00c0: 0014 0162 0c72 6f6f 742d 7365 7276 6572 ...b.root-server +// 0x00d0: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x00e0: 0014 016c 0c72 6f6f 742d 7365 7276 6572 ...l.root-server +// 0x00f0: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x0100: 0014 0166 0c72 6f6f 742d 7365 7276 6572 ...f.root-server +// 0x0110: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x0120: 0014 0167 0c72 6f6f 742d 7365 7276 6572 ...g.root-server +// 0x0130: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x0140: 0014 0164 0c72 6f6f 742d 7365 7276 6572 ...d.root-server +// 0x0150: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x0160: 0014 0168 0c72 6f6f 742d 7365 7276 6572 ...h.root-server +// 0x0170: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x0180: 0014 0165 0c72 6f6f 742d 7365 7276 6572 ...e.root-server +// 0x0190: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x01a0: 0014 016a 0c72 6f6f 742d 7365 7276 6572 ...j.root-server +// 0x01b0: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x01c0: 0014 016b 0c72 6f6f 742d 7365 7276 6572 ...k.root-server +// 0x01d0: 7303 6e65 7400 c02d 0002 0001 0000 0e10 s.net..-........ +// 0x01e0: 0014 0163 0c72 6f6f 742d 7365 7276 6572 ...c.root-server +// 0x01f0: 7303 6e65 7400 c038 0001 0001 0000 0e10 s.net..8........ +// 0x0200: 0004 ca0c 1b21 c058 0001 0001 0000 0e10 .....!.X........ +// 0x0210: 0004 c629 0004 c078 0001 0001 0000 0e10 ...)...x........ +// 0x0220: 0004 c024 9411 ...$.. +var testMalformedRootQuery = []byte{ + 0x00, 0x55, 0x22, 0xaf, 0xc6, 0x37, 0x00, 0x22, 0x55, 0xac, 0xde, 0xac, 0x08, 0x00, 0x45, 0x00, + 0x02, 0x18, 0x76, 0xb2, 0x40, 0x00, 0x72, 0x11, 0x7a, 0xd2, 0x0a, 0x4d, 0x00, 0x1a, 0x0a, 0x01, + 0x00, 0xe9, 0x00, 0x35, 0xfe, 0x2f, 0x02, 0x04, 0xb8, 0xf5, 0xe8, 0x74, 0x81, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x03, 0x0c, 0x61, 0x78, 0x6b, 0x76, 0x63, 0x68, 0x63, 0x70, 0x63, + 0x70, 0x73, 0x6c, 0x0a, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x03, 0x6e, + 0x65, 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x6d, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x61, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x69, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x62, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x6c, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x66, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x67, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x64, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x68, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x65, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x6a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x6b, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x2d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x14, 0x01, 0x63, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x38, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x04, 0xca, 0x0c, 0x1b, 0x21, 0xc0, 0x58, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x04, 0xc6, 0x29, 0x00, 0x04, 0xc0, 0x78, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x04, 0xc0, 0x24, 0x94, 0x11, +} + +func TestMalformedRootQuery(t *testing.T) { + p := gopacket.NewPacket(testMalformedRootQuery, LinkTypeEthernet, testDecodeOptions) + if errLayer := p.ErrorLayer(); errLayer == nil { + t.Error("No error layer on invalid DNS name") + } else if err := errLayer.Error(); !strings.Contains(err.Error(), "no dns data found") { + t.Errorf("unexpected error message: %v", err) + } +} + +// testAnotherMalformedDNS is the packet: +// 10:52:13.690904 IP 10.77.0.29.53 > 10.1.0.6.42280: 13491 op6+% [b2&3=0x3313] [11720a] [23583q] [29742n] [52087au] Type22277 (Class 43688)? M- M-<.VM-^KM-wQM-s"M-^E^]M-+^Wx^P^@M-^\^\M-oM-FM-U^F^E7M-tM-^VM-^[M-F^H>G^FM-uM-^KM-_6GM-[M-jM-bM-^H]hM-^J.[|domain] +// 0x0000: 0055 22af c637 0022 55ac deac 0800 4500 .U"..7."U.....E. +// 0x0010: 05c1 2eea 4000 3611 fbd1 0a4d 001d 0a01 ....@.6....M.... +// 0x0020: 0006 0035 a528 05ad 00a2 34b3 3313 5c1f ...5.(....4.3.\. +// 0x0030: 2dc8 742e cb77 2da0 bc2e 568b f751 f322 -.t..w-...V..Q." +// 0x0040: 851d ab17 7810 009c 1cef c6d5 0605 37f4 ....x.........7. +// 0x0050: 969b c65e 483e 4706 f58b df36 47db eae2 ...^H>G....6G... +// 0x0060: 885d 688a c5a5 5705 aaa8 95eb 93a4 d85a .]h...W........Z +// 0x0070: c9af 261f 7816 a354 2d23 d84a 579c 4876 ..&.x..T-#.JW.Hv +// 0x0080: a391 43db 5c41 191a 92b8 dcdd 6839 eef5 ..C.\A......h9.. +// 0x0090: 728e 13e0 0679 6f47 88a0 25b9 44d8 f8e7 r....yoG..%.D... +// 0x00a0: 8afe 0bfa f811 8da5 f8a3 1f8e d23b fe12 .............;.. +// 0x00b0: d943 9327 92ad 4410 183e 688d b06d 5391 .C.'..D..>h..mS. +// 0x00c0: 695b e49f 8f1e c075 d043 afe0 1174 9db0 i[.....u.C...t.. +// 0x00d0: 06b0 f01e b85b 3c84 945e 06d0 b20f 9eaa .....[<..^...... +// 0x00e0: 123d 0ab0 2a55 309c 0ee9 3e5e db2f f377 .=..*U0...>^./.w +// 0x00f0: d7f1 9bae 373d 3316 0796 b80e dd18 5173 ....7=3.......Qs +// 0x0100: b28d 84fd 1812 d87b 42c8 5f11 4db6 b269 .......{B._.M..i +// 0x0110: 1c42 4aea d5a4 644b 6c00 f0c0 fcee 71a7 .BJ...dKl.....q. +// 0x0120: e7f0 719c a207 dc5c a6fa f005 a338 7ff0 ..q....\.....8.. +// 0x0130: 5beb 3b4d 8952 2a46 d47b a5a2 e1fb 9e76 [.;M.R*F.{.....v +// 0x0140: c815 6258 50f4 6997 bad5 b479 2d06 ebbb ..bXP.i....y-... +// 0x0150: 2cac 2ecc e4f0 1f94 ce9f 186c 61da 9681 ,..........la... +// 0x0160: 345c 4d88 efc7 037b fbe3 4402 ea06 2e5d 4\M....{..D....] +// 0x0170: 2e6e 4860 e180 3ef7 c006 0ad1 ebb9 c4ff .nH`..>......... +// 0x0180: dee2 f21c 02c2 751a ded8 ae2e 13a9 3fa2 ......u.......?. +// 0x0190: 392a 8b54 11b2 2b4e 2bf1 4780 db9f 8c10 9*.T..+N+.G..... +// 0x01a0: ac6f 61b0 7b19 423f 07e5 4628 b870 f75d .oa.{.B?..F(.p.] +// 0x01b0: 09a3 63b2 77af 5985 a0ae 51d8 243f a7c8 ..c.w.Y...Q.$?.. +// 0x01c0: ab08 7fc6 0217 c09f c412 0c45 e6aa 96bf ...........E.... +// 0x01d0: 184c 4307 1f1f c4f4 7734 da31 2088 662b .LC.....w4.1..f+ +// 0x01e0: 44c5 096f 1d1d 2dc5 ffd6 867d 9fc5 7b45 D..o..-....}..{E +// 0x01f0: f949 7dd9 38de 0d51 ac2a 32fc f50b 1bbe .I}.8..Q.*2..... +// 0x0200: 1c4b 5441 fbf3 0821 6c28 4530 5676 1d27 .KTA...!l(E0Vv.' +// 0x0210: 5087 466c 3d5b 45a6 af7f 917a 6d43 66c2 P.Fl=[E....zmCf. +// 0x0220: 036a 8bef ca60 9b13 8d29 9fda 82fa 01b1 .j...`...)...... +// 0x0230: df8f 1f83 c71d 630f 349e 508c 9f7a e3da ......c.4.P..z.. +// 0x0240: a114 3622 9df8 9926 4dac 4150 d505 7b3a ..6"...&M.AP..{: +// 0x0250: 6fed fc75 6b4f 2d60 8a89 767d 9af0 896e o..ukO-`..v}...n +// 0x0260: 907d 1ada def3 345c 0d81 283c a24f fcbb .}....4\..(<.O.. +// 0x0270: bbdd b7b3 e3bb 9f1b d966 51b7 8217 7fa0 .........fQ..... +// 0x0280: e828 d3ca a6f1 532f 164e e405 bb3b 0de3 .(....S/.N...;.. +// 0x0290: 985d 6e89 d825 ebc6 d8ba 5190 a114 c6a3 .]n..%....Q..... +// 0x02a0: 18b4 8aa7 181a 01ac cdc0 8048 ea72 a5e3 ...........H.r.. +// 0x02b0: e37a dc57 65cd b787 39e6 c39e 317b 45d8 .z.We...9...1{E. +// 0x02c0: 475c 05ba e8f8 8224 5a85 27b8 1584 8d78 G\.....$Z.'....x +// 0x02d0: 62b6 6495 ac10 338f 1122 f2ff 043e 9e2a b.d...3.."...>.* +// 0x02e0: 1058 a910 5792 6fcd 9a96 6183 6708 8f70 .X..W.o...a.g..p +// 0x02f0: edc6 a67c 64ff 50fa 520b de94 c82c c4d6 ...|d.P.R....,.. +// 0x0300: 7d8f 0fd5 2f0d 9833 7c6c be10 a4e5 dc99 }.../..3|l...... +// 0x0310: a467 ef5f b35b c11c e23c 131a 48b2 9cef .g._.[...<..H... +// 0x0320: 5a2f fece dd9e 2aea 0db9 faf3 a6ef b29d Z/....*......... +// 0x0330: e85d a410 dd6a 6806 3fc6 1694 179f cb4b .]...jh.?......K +// 0x0340: 08c4 86b2 0713 cddb b257 d56b fe82 7d82 .........W.k..}. +// 0x0350: 0d1f 6dc9 67b2 d2a1 6791 4f38 edf9 491f ..m.g...g.O8..I. +// 0x0360: 2c02 35f5 8165 ecc3 bc6a b631 3c7e 1ad4 ,.5..e...j.1<~.. +// 0x0370: 8e27 f962 f942 11b5 1b45 9bac b474 3c6e .'.b.B...E...t. +// 0x0470: 259b d93e f86f 6088 0c4e 357b 5c67 7d93 %..>.o`..N5{\g}. +// 0x0480: a695 1a42 e1e1 ef91 14d7 b7b7 0ca4 2dda ...B..........-. +// 0x0490: 6ac1 771e 25c1 a578 4ca8 6fd8 de04 1c09 j.w.%..xL.o..... +// 0x04a0: df49 f179 6a58 2b45 7231 307f bc67 e5e7 .I.yjX+Er10..g.. +// 0x04b0: c5cd fec0 b021 508e 4fc5 f821 f734 90bc .....!P.O..!.4.. +// 0x04c0: c87f 14f1 2e5c d17b 1818 5b4a 6b68 0212 .....\.{..[Jkh.. +// 0x04d0: 1791 4a30 8518 99a9 b516 67e7 ed56 d1d1 ..J0......g..V.. +// 0x04e0: 239d dfda 11c5 0afe e58a b6e0 fb66 ab5c #............f.\ +// 0x04f0: f590 dcd6 457d 01d1 83f5 a9f0 cdb2 9c14 ....E}.......... +// 0x0500: ff66 f10c d428 a07b 34e3 d600 91f2 aca7 .f...(.{4....... +// 0x0510: 4e1f f3ac a96e 2aa3 ec9b 448c 748d f858 N....n*...D.t..X +// 0x0520: 131c d496 af9b f5f0 d2f5 57ac 0b64 55a1 ..........W..dU. +// 0x0530: 860e 5ad0 3e62 26b5 9e17 f51f 88c1 02e3 ..Z.>b&......... +// 0x0540: 4a38 de70 3216 6f88 5d1e f429 ee19 4121 J8.p2.o.]..)..A! +// 0x0550: f571 84ac 3789 141f 1798 90b1 8373 2499 .q..7........s$. +// 0x0560: c131 b13f f3a3 9a07 aef1 bfe8 8cd7 8c2e .1.?............ +// 0x0570: ba35 dfc5 eb07 013c 7621 6481 bdfb 6233 .5........z.X +// 0x05a0: a1b2 6e08 d06d dc21 1eda 14a0 a2f7 1701 ..n..m.!........ +// 0x05b0: a5e0 dfd7 871f 595d db43 70f5 bab3 b732 ......Y].Cp....2 +// 0x05c0: 6275 da15 b203 dac7 321f 8d61 11bd 30 bu......2..a..0 +var testAnotherMalformedDNS = []byte{ + 0x00, 0x55, 0x22, 0xaf, 0xc6, 0x37, 0x00, 0x22, 0x55, 0xac, 0xde, 0xac, 0x08, 0x00, 0x45, 0x00, + 0x05, 0xc1, 0x2e, 0xea, 0x40, 0x00, 0x36, 0x11, 0xfb, 0xd1, 0x0a, 0x4d, 0x00, 0x1d, 0x0a, 0x01, + 0x00, 0x06, 0x00, 0x35, 0xa5, 0x28, 0x05, 0xad, 0x00, 0xa2, 0x34, 0xb3, 0x33, 0x13, 0x5c, 0x1f, + 0x2d, 0xc8, 0x74, 0x2e, 0xcb, 0x77, 0x2d, 0xa0, 0xbc, 0x2e, 0x56, 0x8b, 0xf7, 0x51, 0xf3, 0x22, + 0x85, 0x1d, 0xab, 0x17, 0x78, 0x10, 0x00, 0x9c, 0x1c, 0xef, 0xc6, 0xd5, 0x06, 0x05, 0x37, 0xf4, + 0x96, 0x9b, 0xc6, 0x5e, 0x48, 0x3e, 0x47, 0x06, 0xf5, 0x8b, 0xdf, 0x36, 0x47, 0xdb, 0xea, 0xe2, + 0x88, 0x5d, 0x68, 0x8a, 0xc5, 0xa5, 0x57, 0x05, 0xaa, 0xa8, 0x95, 0xeb, 0x93, 0xa4, 0xd8, 0x5a, + 0xc9, 0xaf, 0x26, 0x1f, 0x78, 0x16, 0xa3, 0x54, 0x2d, 0x23, 0xd8, 0x4a, 0x57, 0x9c, 0x48, 0x76, + 0xa3, 0x91, 0x43, 0xdb, 0x5c, 0x41, 0x19, 0x1a, 0x92, 0xb8, 0xdc, 0xdd, 0x68, 0x39, 0xee, 0xf5, + 0x72, 0x8e, 0x13, 0xe0, 0x06, 0x79, 0x6f, 0x47, 0x88, 0xa0, 0x25, 0xb9, 0x44, 0xd8, 0xf8, 0xe7, + 0x8a, 0xfe, 0x0b, 0xfa, 0xf8, 0x11, 0x8d, 0xa5, 0xf8, 0xa3, 0x1f, 0x8e, 0xd2, 0x3b, 0xfe, 0x12, + 0xd9, 0x43, 0x93, 0x27, 0x92, 0xad, 0x44, 0x10, 0x18, 0x3e, 0x68, 0x8d, 0xb0, 0x6d, 0x53, 0x91, + 0x69, 0x5b, 0xe4, 0x9f, 0x8f, 0x1e, 0xc0, 0x75, 0xd0, 0x43, 0xaf, 0xe0, 0x11, 0x74, 0x9d, 0xb0, + 0x06, 0xb0, 0xf0, 0x1e, 0xb8, 0x5b, 0x3c, 0x84, 0x94, 0x5e, 0x06, 0xd0, 0xb2, 0x0f, 0x9e, 0xaa, + 0x12, 0x3d, 0x0a, 0xb0, 0x2a, 0x55, 0x30, 0x9c, 0x0e, 0xe9, 0x3e, 0x5e, 0xdb, 0x2f, 0xf3, 0x77, + 0xd7, 0xf1, 0x9b, 0xae, 0x37, 0x3d, 0x33, 0x16, 0x07, 0x96, 0xb8, 0x0e, 0xdd, 0x18, 0x51, 0x73, + 0xb2, 0x8d, 0x84, 0xfd, 0x18, 0x12, 0xd8, 0x7b, 0x42, 0xc8, 0x5f, 0x11, 0x4d, 0xb6, 0xb2, 0x69, + 0x1c, 0x42, 0x4a, 0xea, 0xd5, 0xa4, 0x64, 0x4b, 0x6c, 0x00, 0xf0, 0xc0, 0xfc, 0xee, 0x71, 0xa7, + 0xe7, 0xf0, 0x71, 0x9c, 0xa2, 0x07, 0xdc, 0x5c, 0xa6, 0xfa, 0xf0, 0x05, 0xa3, 0x38, 0x7f, 0xf0, + 0x5b, 0xeb, 0x3b, 0x4d, 0x89, 0x52, 0x2a, 0x46, 0xd4, 0x7b, 0xa5, 0xa2, 0xe1, 0xfb, 0x9e, 0x76, + 0xc8, 0x15, 0x62, 0x58, 0x50, 0xf4, 0x69, 0x97, 0xba, 0xd5, 0xb4, 0x79, 0x2d, 0x06, 0xeb, 0xbb, + 0x2c, 0xac, 0x2e, 0xcc, 0xe4, 0xf0, 0x1f, 0x94, 0xce, 0x9f, 0x18, 0x6c, 0x61, 0xda, 0x96, 0x81, + 0x34, 0x5c, 0x4d, 0x88, 0xef, 0xc7, 0x03, 0x7b, 0xfb, 0xe3, 0x44, 0x02, 0xea, 0x06, 0x2e, 0x5d, + 0x2e, 0x6e, 0x48, 0x60, 0xe1, 0x80, 0x3e, 0xf7, 0xc0, 0x06, 0x0a, 0xd1, 0xeb, 0xb9, 0xc4, 0xff, + 0xde, 0xe2, 0xf2, 0x1c, 0x02, 0xc2, 0x75, 0x1a, 0xde, 0xd8, 0xae, 0x2e, 0x13, 0xa9, 0x3f, 0xa2, + 0x39, 0x2a, 0x8b, 0x54, 0x11, 0xb2, 0x2b, 0x4e, 0x2b, 0xf1, 0x47, 0x80, 0xdb, 0x9f, 0x8c, 0x10, + 0xac, 0x6f, 0x61, 0xb0, 0x7b, 0x19, 0x42, 0x3f, 0x07, 0xe5, 0x46, 0x28, 0xb8, 0x70, 0xf7, 0x5d, + 0x09, 0xa3, 0x63, 0xb2, 0x77, 0xaf, 0x59, 0x85, 0xa0, 0xae, 0x51, 0xd8, 0x24, 0x3f, 0xa7, 0xc8, + 0xab, 0x08, 0x7f, 0xc6, 0x02, 0x17, 0xc0, 0x9f, 0xc4, 0x12, 0x0c, 0x45, 0xe6, 0xaa, 0x96, 0xbf, + 0x18, 0x4c, 0x43, 0x07, 0x1f, 0x1f, 0xc4, 0xf4, 0x77, 0x34, 0xda, 0x31, 0x20, 0x88, 0x66, 0x2b, + 0x44, 0xc5, 0x09, 0x6f, 0x1d, 0x1d, 0x2d, 0xc5, 0xff, 0xd6, 0x86, 0x7d, 0x9f, 0xc5, 0x7b, 0x45, + 0xf9, 0x49, 0x7d, 0xd9, 0x38, 0xde, 0x0d, 0x51, 0xac, 0x2a, 0x32, 0xfc, 0xf5, 0x0b, 0x1b, 0xbe, + 0x1c, 0x4b, 0x54, 0x41, 0xfb, 0xf3, 0x08, 0x21, 0x6c, 0x28, 0x45, 0x30, 0x56, 0x76, 0x1d, 0x27, + 0x50, 0x87, 0x46, 0x6c, 0x3d, 0x5b, 0x45, 0xa6, 0xaf, 0x7f, 0x91, 0x7a, 0x6d, 0x43, 0x66, 0xc2, + 0x03, 0x6a, 0x8b, 0xef, 0xca, 0x60, 0x9b, 0x13, 0x8d, 0x29, 0x9f, 0xda, 0x82, 0xfa, 0x01, 0xb1, + 0xdf, 0x8f, 0x1f, 0x83, 0xc7, 0x1d, 0x63, 0x0f, 0x34, 0x9e, 0x50, 0x8c, 0x9f, 0x7a, 0xe3, 0xda, + 0xa1, 0x14, 0x36, 0x22, 0x9d, 0xf8, 0x99, 0x26, 0x4d, 0xac, 0x41, 0x50, 0xd5, 0x05, 0x7b, 0x3a, + 0x6f, 0xed, 0xfc, 0x75, 0x6b, 0x4f, 0x2d, 0x60, 0x8a, 0x89, 0x76, 0x7d, 0x9a, 0xf0, 0x89, 0x6e, + 0x90, 0x7d, 0x1a, 0xda, 0xde, 0xf3, 0x34, 0x5c, 0x0d, 0x81, 0x28, 0x3c, 0xa2, 0x4f, 0xfc, 0xbb, + 0xbb, 0xdd, 0xb7, 0xb3, 0xe3, 0xbb, 0x9f, 0x1b, 0xd9, 0x66, 0x51, 0xb7, 0x82, 0x17, 0x7f, 0xa0, + 0xe8, 0x28, 0xd3, 0xca, 0xa6, 0xf1, 0x53, 0x2f, 0x16, 0x4e, 0xe4, 0x05, 0xbb, 0x3b, 0x0d, 0xe3, + 0x98, 0x5d, 0x6e, 0x89, 0xd8, 0x25, 0xeb, 0xc6, 0xd8, 0xba, 0x51, 0x90, 0xa1, 0x14, 0xc6, 0xa3, + 0x18, 0xb4, 0x8a, 0xa7, 0x18, 0x1a, 0x01, 0xac, 0xcd, 0xc0, 0x80, 0x48, 0xea, 0x72, 0xa5, 0xe3, + 0xe3, 0x7a, 0xdc, 0x57, 0x65, 0xcd, 0xb7, 0x87, 0x39, 0xe6, 0xc3, 0x9e, 0x31, 0x7b, 0x45, 0xd8, + 0x47, 0x5c, 0x05, 0xba, 0xe8, 0xf8, 0x82, 0x24, 0x5a, 0x85, 0x27, 0xb8, 0x15, 0x84, 0x8d, 0x78, + 0x62, 0xb6, 0x64, 0x95, 0xac, 0x10, 0x33, 0x8f, 0x11, 0x22, 0xf2, 0xff, 0x04, 0x3e, 0x9e, 0x2a, + 0x10, 0x58, 0xa9, 0x10, 0x57, 0x92, 0x6f, 0xcd, 0x9a, 0x96, 0x61, 0x83, 0x67, 0x08, 0x8f, 0x70, + 0xed, 0xc6, 0xa6, 0x7c, 0x64, 0xff, 0x50, 0xfa, 0x52, 0x0b, 0xde, 0x94, 0xc8, 0x2c, 0xc4, 0xd6, + 0x7d, 0x8f, 0x0f, 0xd5, 0x2f, 0x0d, 0x98, 0x33, 0x7c, 0x6c, 0xbe, 0x10, 0xa4, 0xe5, 0xdc, 0x99, + 0xa4, 0x67, 0xef, 0x5f, 0xb3, 0x5b, 0xc1, 0x1c, 0xe2, 0x3c, 0x13, 0x1a, 0x48, 0xb2, 0x9c, 0xef, + 0x5a, 0x2f, 0xfe, 0xce, 0xdd, 0x9e, 0x2a, 0xea, 0x0d, 0xb9, 0xfa, 0xf3, 0xa6, 0xef, 0xb2, 0x9d, + 0xe8, 0x5d, 0xa4, 0x10, 0xdd, 0x6a, 0x68, 0x06, 0x3f, 0xc6, 0x16, 0x94, 0x17, 0x9f, 0xcb, 0x4b, + 0x08, 0xc4, 0x86, 0xb2, 0x07, 0x13, 0xcd, 0xdb, 0xb2, 0x57, 0xd5, 0x6b, 0xfe, 0x82, 0x7d, 0x82, + 0x0d, 0x1f, 0x6d, 0xc9, 0x67, 0xb2, 0xd2, 0xa1, 0x67, 0x91, 0x4f, 0x38, 0xed, 0xf9, 0x49, 0x1f, + 0x2c, 0x02, 0x35, 0xf5, 0x81, 0x65, 0xec, 0xc3, 0xbc, 0x6a, 0xb6, 0x31, 0x3c, 0x7e, 0x1a, 0xd4, + 0x8e, 0x27, 0xf9, 0x62, 0xf9, 0x42, 0x11, 0xb5, 0x1b, 0x45, 0x9b, 0xac, 0xb4, 0x74, 0x3c, 0x6e, + 0x68, 0x32, 0x30, 0x75, 0xbe, 0x6d, 0xac, 0x0d, 0xa8, 0xa0, 0x7d, 0x47, 0xa6, 0xef, 0x4e, 0x43, + 0x6b, 0x9a, 0x30, 0x97, 0x8a, 0x8b, 0x82, 0xa3, 0x95, 0x15, 0x36, 0x2c, 0xf7, 0xd6, 0xa3, 0x7f, + 0x73, 0x13, 0x11, 0x99, 0xa5, 0xf3, 0x03, 0xdc, 0xbc, 0xc9, 0xfb, 0x10, 0xc2, 0x3d, 0xee, 0xb9, + 0x78, 0xff, 0xc8, 0xf3, 0x0d, 0x38, 0x9f, 0x74, 0xce, 0xec, 0xb7, 0xae, 0x63, 0xe3, 0x34, 0x24, + 0xb7, 0x83, 0xf1, 0x06, 0x01, 0x1f, 0x66, 0x6b, 0xbf, 0x2d, 0xab, 0xc8, 0xea, 0x10, 0x57, 0xa1, + 0x7c, 0xf2, 0x4a, 0x3f, 0x57, 0xca, 0x13, 0x86, 0xbf, 0xba, 0x27, 0xe5, 0x46, 0x62, 0x81, 0xc8, + 0x04, 0x1e, 0x18, 0x20, 0xb3, 0xd5, 0xc3, 0x99, 0xcd, 0x4d, 0x22, 0x2f, 0x29, 0xf0, 0xb9, 0x94, + 0x86, 0x5a, 0xe6, 0xe2, 0x16, 0x86, 0x32, 0x61, 0xb0, 0xcd, 0xca, 0xaf, 0x07, 0xec, 0xd0, 0xbc, + 0xaf, 0xb8, 0x3c, 0xf0, 0x51, 0xc1, 0x6c, 0x7a, 0x63, 0x83, 0x6b, 0x3a, 0xff, 0x47, 0x95, 0x51, + 0x10, 0x99, 0x52, 0x5f, 0x35, 0x5e, 0x46, 0x84, 0xbd, 0x34, 0xec, 0x12, 0x88, 0xc9, 0xdc, 0xc2, + 0xd1, 0x1c, 0x82, 0x6d, 0xf1, 0xdf, 0x37, 0xe6, 0xf0, 0x8f, 0x6c, 0xe8, 0x81, 0x7d, 0xbd, 0xc3, + 0x20, 0xb9, 0xa2, 0x74, 0xc6, 0x45, 0xc6, 0x7d, 0xf2, 0x99, 0xfe, 0xf9, 0x28, 0x7f, 0x09, 0xee, + 0xac, 0x67, 0x68, 0x72, 0xa1, 0x26, 0xb1, 0xd3, 0x92, 0x2c, 0x4c, 0x2a, 0x0e, 0xc9, 0xb6, 0xd4, + 0xfb, 0x59, 0x61, 0x63, 0xd1, 0xc4, 0x17, 0x08, 0x8d, 0x94, 0xbc, 0x3d, 0xbe, 0x5e, 0xae, 0x29, + 0x51, 0xff, 0xa7, 0x65, 0x9d, 0xf6, 0xae, 0x35, 0xed, 0x6b, 0x05, 0x55, 0x93, 0x3f, 0x3e, 0xd6, + 0x25, 0x9b, 0xd9, 0x3e, 0xf8, 0x6f, 0x60, 0x88, 0x0c, 0x4e, 0x35, 0x7b, 0x5c, 0x67, 0x7d, 0x93, + 0xa6, 0x95, 0x1a, 0x42, 0xe1, 0xe1, 0xef, 0x91, 0x14, 0xd7, 0xb7, 0xb7, 0x0c, 0xa4, 0x2d, 0xda, + 0x6a, 0xc1, 0x77, 0x1e, 0x25, 0xc1, 0xa5, 0x78, 0x4c, 0xa8, 0x6f, 0xd8, 0xde, 0x04, 0x1c, 0x09, + 0xdf, 0x49, 0xf1, 0x79, 0x6a, 0x58, 0x2b, 0x45, 0x72, 0x31, 0x30, 0x7f, 0xbc, 0x67, 0xe5, 0xe7, + 0xc5, 0xcd, 0xfe, 0xc0, 0xb0, 0x21, 0x50, 0x8e, 0x4f, 0xc5, 0xf8, 0x21, 0xf7, 0x34, 0x90, 0xbc, + 0xc8, 0x7f, 0x14, 0xf1, 0x2e, 0x5c, 0xd1, 0x7b, 0x18, 0x18, 0x5b, 0x4a, 0x6b, 0x68, 0x02, 0x12, + 0x17, 0x91, 0x4a, 0x30, 0x85, 0x18, 0x99, 0xa9, 0xb5, 0x16, 0x67, 0xe7, 0xed, 0x56, 0xd1, 0xd1, + 0x23, 0x9d, 0xdf, 0xda, 0x11, 0xc5, 0x0a, 0xfe, 0xe5, 0x8a, 0xb6, 0xe0, 0xfb, 0x66, 0xab, 0x5c, + 0xf5, 0x90, 0xdc, 0xd6, 0x45, 0x7d, 0x01, 0xd1, 0x83, 0xf5, 0xa9, 0xf0, 0xcd, 0xb2, 0x9c, 0x14, + 0xff, 0x66, 0xf1, 0x0c, 0xd4, 0x28, 0xa0, 0x7b, 0x34, 0xe3, 0xd6, 0x00, 0x91, 0xf2, 0xac, 0xa7, + 0x4e, 0x1f, 0xf3, 0xac, 0xa9, 0x6e, 0x2a, 0xa3, 0xec, 0x9b, 0x44, 0x8c, 0x74, 0x8d, 0xf8, 0x58, + 0x13, 0x1c, 0xd4, 0x96, 0xaf, 0x9b, 0xf5, 0xf0, 0xd2, 0xf5, 0x57, 0xac, 0x0b, 0x64, 0x55, 0xa1, + 0x86, 0x0e, 0x5a, 0xd0, 0x3e, 0x62, 0x26, 0xb5, 0x9e, 0x17, 0xf5, 0x1f, 0x88, 0xc1, 0x02, 0xe3, + 0x4a, 0x38, 0xde, 0x70, 0x32, 0x16, 0x6f, 0x88, 0x5d, 0x1e, 0xf4, 0x29, 0xee, 0x19, 0x41, 0x21, + 0xf5, 0x71, 0x84, 0xac, 0x37, 0x89, 0x14, 0x1f, 0x17, 0x98, 0x90, 0xb1, 0x83, 0x73, 0x24, 0x99, + 0xc1, 0x31, 0xb1, 0x3f, 0xf3, 0xa3, 0x9a, 0x07, 0xae, 0xf1, 0xbf, 0xe8, 0x8c, 0xd7, 0x8c, 0x2e, + 0xba, 0x35, 0xdf, 0xc5, 0xeb, 0x07, 0x01, 0x3c, 0x76, 0x21, 0x64, 0x81, 0xbd, 0xfb, 0x62, 0x33, + 0x22, 0xe2, 0x0f, 0x05, 0x7e, 0x15, 0x04, 0x17, 0x67, 0xe4, 0x26, 0x32, 0x52, 0x07, 0x28, 0xa6, + 0x8e, 0x88, 0x94, 0x23, 0xde, 0x54, 0x54, 0x12, 0xb5, 0x3e, 0xfd, 0x8d, 0xd4, 0x7a, 0xde, 0x58, + 0xa1, 0xb2, 0x6e, 0x08, 0xd0, 0x6d, 0xdc, 0x21, 0x1e, 0xda, 0x14, 0xa0, 0xa2, 0xf7, 0x17, 0x01, + 0xa5, 0xe0, 0xdf, 0xd7, 0x87, 0x1f, 0x59, 0x5d, 0xdb, 0x43, 0x70, 0xf5, 0xba, 0xb3, 0xb7, 0x32, + 0x62, 0x75, 0xda, 0x15, 0xb2, 0x03, 0xda, 0xc7, 0x32, 0x1f, 0x8d, 0x61, 0x11, 0xbd, 0x30, +} + +func TestAnotherMalformedDNS(t *testing.T) { + p := gopacket.NewPacket(testAnotherMalformedDNS, LinkTypeEthernet, testDecodeOptions) + if errLayer := p.ErrorLayer(); errLayer == nil { + t.Error("No error layer on invalid DNS name") + } else if err := errLayer.Error(); !strings.Contains(err.Error(), "offset too high") { + t.Errorf("unexpected error message: %v", err) + } +} + +// testMalformedDNSAgain is the packet: +// 12:14:52.702061 IP 10.77.0.4.53 > 10.1.0.41.61610: 12529 updateDA [b2&3=0x5cad] [38274a] [61303q] [1718n] [14913au][|domain] +// 0x0000: 0055 22af c637 0022 55ac deac 0800 4500 .U"..7."U.....E. +// 0x0010: 0091 2dff 0000 7811 ffe2 0a4d 0004 0a01 ..-...x....M.... +// 0x0020: 0029 0035 f0aa 007d 5b53 30f1 5cad ef77 .).5...}[S0.\..w +// 0x0030: 9582 06b6 3a41 357a 8cef cdc0 a732 b800 ....:A5z.....2.. +// 0x0040: 466e 1c30 2e75 95ac c03d 1ed4 8635 2d09 Fn.0.u...=...5-. +// 0x0050: 2fee 3a82 b4f0 427e 2b6b f870 cc7f c9a1 /.:...B~+k.p.... +// 0x0060: e6f1 a761 97ec 2ff7 d248 4d95 321c 6e4e ...a../..HM.2.nN +// 0x0070: 57fa 6d3d 9ec0 fe3a 6f1e e634 4396 b494 W.m=...:o..4C... +// 0x0080: 8b7a a929 d7e1 da7c c346 ca77 4890 6bf3 .z.)...|.F.wH.k. +// 0x0090: 5ecb 7e97 c49d 3564 984f bf7c 8ac1 dd ^.~...5d.O.|... +var testMalformedDNSAgain = []byte{ + 0x00, 0x55, 0x22, 0xaf, 0xc6, 0x37, 0x00, 0x22, 0x55, 0xac, 0xde, 0xac, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x91, 0x2d, 0xff, 0x00, 0x00, 0x78, 0x11, 0xff, 0xe2, 0x0a, 0x4d, 0x00, 0x04, 0x0a, 0x01, + 0x00, 0x29, 0x00, 0x35, 0xf0, 0xaa, 0x00, 0x7d, 0x5b, 0x53, 0x30, 0xf1, 0x5c, 0xad, 0xef, 0x77, + 0x95, 0x82, 0x06, 0xb6, 0x3a, 0x41, 0x35, 0x7a, 0x8c, 0xef, 0xcd, 0xc0, 0xa7, 0x32, 0xb8, 0x00, + 0x46, 0x6e, 0x1c, 0x30, 0x2e, 0x75, 0x95, 0xac, 0xc0, 0x3d, 0x1e, 0xd4, 0x86, 0x35, 0x2d, 0x09, + 0x2f, 0xee, 0x3a, 0x82, 0xb4, 0xf0, 0x42, 0x7e, 0x2b, 0x6b, 0xf8, 0x70, 0xcc, 0x7f, 0xc9, 0xa1, + 0xe6, 0xf1, 0xa7, 0x61, 0x97, 0xec, 0x2f, 0xf7, 0xd2, 0x48, 0x4d, 0x95, 0x32, 0x1c, 0x6e, 0x4e, + 0x57, 0xfa, 0x6d, 0x3d, 0x9e, 0xc0, 0xfe, 0x3a, 0x6f, 0x1e, 0xe6, 0x34, 0x43, 0x96, 0xb4, 0x94, + 0x8b, 0x7a, 0xa9, 0x29, 0xd7, 0xe1, 0xda, 0x7c, 0xc3, 0x46, 0xca, 0x77, 0x48, 0x90, 0x6b, 0xf3, + 0x5e, 0xcb, 0x7e, 0x97, 0xc4, 0x9d, 0x35, 0x64, 0x98, 0x4f, 0xbf, 0x7c, 0x8a, 0xc1, 0xdd, +} + +func TestMalformedDNSAgain(t *testing.T) { + p := gopacket.NewPacket(testMalformedDNSAgain, LinkTypeEthernet, testDecodeOptions) + if errLayer := p.ErrorLayer(); errLayer == nil { + t.Error("No error layer on invalid DNS name") + } else if err := errLayer.Error(); !strings.Contains(err.Error(), "walked out of range") { + t.Errorf("unexpected error message: %v", err) + } +} + +// testMalformedDNSOhGodMakeItStop is the packet: +// 15:08:24.430906 IP 10.77.0.19.53 > 10.1.0.19.50635: 12397 zoneInit% [b2&3=0x7232] [47729a] [46283q] [60247n] [61718au][|domain] +// 0x0000: 0055 22af c637 0022 55ac deac 0800 4500 .U"..7."U.....E. +// 0x0010: 0079 c51c 4000 3511 6be4 0a4d 0013 0a01 .y..@.5.k..M.... +// 0x0020: 0013 0035 c5cb 0065 ef45 306d 7232 b4cb ...5...e.E0mr2.. +// 0x0030: ba71 eb57 f116 3994 e000 4626 0534 66cc .q.W..9...F&.4f. +// 0x0040: 7b32 24f2 eece bca7 20e2 9a2a e1ce e737 {2$........*...7 +// 0x0050: ac39 5fae 72ec c3ec 284f ca4a 171f 466d .9_.r...(O.J..Fm +// 0x0060: f6c6 84d7 e795 310f 26df 9b59 6db9 21cf ......1.&..Ym.!. +// 0x0070: 15cb 30a3 c4cf df23 805a ed1a 0584 4fc3 ..0....#.Z....O. +// 0x0080: 7fa3 3cb4 e04f e9 ..<..O. +var testMalformedDNSOhGodMakeItStop = []byte{ + 0x00, 0x55, 0x22, 0xaf, 0xc6, 0x37, 0x00, 0x22, 0x55, 0xac, 0xde, 0xac, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x79, 0xc5, 0x1c, 0x40, 0x00, 0x35, 0x11, 0x6b, 0xe4, 0x0a, 0x4d, 0x00, 0x13, 0x0a, 0x01, + 0x00, 0x13, 0x00, 0x35, 0xc5, 0xcb, 0x00, 0x65, 0xef, 0x45, 0x30, 0x6d, 0x72, 0x32, 0xb4, 0xcb, + 0xba, 0x71, 0xeb, 0x57, 0xf1, 0x16, 0x39, 0x94, 0xe0, 0x00, 0x46, 0x26, 0x05, 0x34, 0x66, 0xcc, + 0x7b, 0x32, 0x24, 0xf2, 0xee, 0xce, 0xbc, 0xa7, 0x20, 0xe2, 0x9a, 0x2a, 0xe1, 0xce, 0xe7, 0x37, + 0xac, 0x39, 0x5f, 0xae, 0x72, 0xec, 0xc3, 0xec, 0x28, 0x4f, 0xca, 0x4a, 0x17, 0x1f, 0x46, 0x6d, + 0xf6, 0xc6, 0x84, 0xd7, 0xe7, 0x95, 0x31, 0x0f, 0x26, 0xdf, 0x9b, 0x59, 0x6d, 0xb9, 0x21, 0xcf, + 0x15, 0xcb, 0x30, 0xa3, 0xc4, 0xcf, 0xdf, 0x23, 0x80, 0x5a, 0xed, 0x1a, 0x05, 0x84, 0x4f, 0xc3, + 0x7f, 0xa3, 0x3c, 0xb4, 0xe0, 0x4f, 0xe9, +} + +func TestMalformedDNSOhGodMakeItStop(t *testing.T) { + p := gopacket.NewPacket(testMalformedDNSOhGodMakeItStop, LinkTypeEthernet, testDecodeOptions) + if errLayer := p.ErrorLayer(); errLayer == nil { + t.Error("No error layer on invalid DNS name") + } else if err := errLayer.Error(); !strings.Contains(err.Error(), "offset pointer too high") { + t.Errorf("unexpected error message: %v", err) + } +} + +// testPacketDNSPanic7 is the packet: +// 07:56:25.174747 IP 10.77.0.11.53 > 10.1.0.67.55777: 41808*-| 3/7/0 TXT "google-site-verification=DC2uC-T8kD33lINhNzfo0bNBrw-vrCXs5BPF5BXY56g", TXT "v=spf1 include:spf-a.outlook.com include:spf-b.outlook.com ip4:157.55.9.128/25 include:spf.protection.outlook.com include:spf-a.hotmail.com include:_spf-ssg-b.microsoft.com include:_spf-ssg-c.microsoft.com ~all", TXT "google-site-verification=0iLWhIMhXEkeWwWfFU4ursTn-_OvoOjaA0Lr7Pg1sEM" (512) +// 0x0000: 0055 22af c637 0022 55ac deac 0800 4500 .U"..7."U.....E. +// 0x0010: 021c b5ca 4000 fa11 b46a 0a4d 000b 0a01 ....@....j.M.... +// 0x0020: 0043 0035 d9e1 0208 afd6 a350 8600 0001 .C.5.......P.... +// 0x0030: 0003 0007 0000 076f 7574 6c6f 6f6b 0363 .......outlook.c +// 0x0040: 6f6d 0000 1000 01c0 0c00 1000 0100 0001 om.............. +// 0x0050: 2c00 4544 676f 6f67 6c65 2d73 6974 652d ,.EDgoogle-site- +// 0x0060: 7665 7269 6669 6361 7469 6f6e 3d44 4332 verification=DC2 +// 0x0070: 7543 2d54 386b 4433 336c 494e 684e 7a66 uC-T8kD33lINhNzf +// 0x0080: 6f30 624e 4272 772d 7672 4358 7335 4250 o0bNBrw-vrCXs5BP +// 0x0090: 4635 4258 5935 3667 c00c 0010 0001 0000 F5BXY56g........ +// 0x00a0: 012c 00d3 d276 3d73 7066 3120 696e 636c .,...v=spf1.incl +// 0x00b0: 7564 653a 7370 662d 612e 6f75 746c 6f6f ude:spf-a.outloo +// 0x00c0: 6b2e 636f 6d20 696e 636c 7564 653a 7370 k.com.include:sp +// 0x00d0: 662d 622e 6f75 746c 6f6f 6b2e 636f 6d20 f-b.outlook.com. +// 0x00e0: 6970 343a 3135 372e 3535 2e39 2e31 3238 ip4:157.55.9.128 +// 0x00f0: 2f32 3520 696e 636c 7564 653a 7370 662e /25.include:spf. +// 0x0100: 7072 6f74 6563 7469 6f6e 2e6f 7574 6c6f protection.outlo +// 0x0110: 6f6b 2e63 6f6d 2069 6e63 6c75 6465 3a73 ok.com.include:s +// 0x0120: 7066 2d61 2e68 6f74 6d61 696c 2e63 6f6d pf-a.hotmail.com +// 0x0130: 2069 6e63 6c75 6465 3a5f 7370 662d 7373 .include:_spf-ss +// 0x0140: 672d 622e 6d69 6372 6f73 6f66 742e 636f g-b.microsoft.co +// 0x0150: 6d20 696e 636c 7564 653a 5f73 7066 2d73 m.include:_spf-s +// 0x0160: 7367 2d63 2e6d 6963 726f 736f 6674 2e63 sg-c.microsoft.c +// 0x0170: 6f6d 207e 616c 6cc0 0c00 1000 0100 0001 om.~all......... +// 0x0180: 2c00 4544 676f 6f67 6c65 2d73 6974 652d ,.EDgoogle-site- +// 0x0190: 7665 7269 6669 6361 7469 6f6e 3d30 694c verification=0iL +// 0x01a0: 5768 494d 6858 456b 6557 7757 6646 5534 WhIMhXEkeWwWfFU4 +// 0x01b0: 7572 7354 6e2d 5f4f 766f 4f6a 6141 304c ursTn-_OvoOjaA0L +// 0x01c0: 7237 5067 3173 454d c00c 0002 0001 0002 r7Pg1sEM........ +// 0x01d0: a300 000e 036e 7332 046d 7366 7403 6e65 .....ns2.msft.ne +// 0x01e0: 7400 c00c 0002 0001 0002 a300 0006 036e t..............n +// 0x01f0: 7334 c1ae c00c 0002 0001 0002 a300 0006 s4.............. +// 0x0200: 036e 7331 c1ae c00c 0002 0001 0002 a300 .ns1............ +// 0x0210: 0006 036e 7333 c1ae c00c 0002 0001 0002 ...ns3.......... +// 0x0220: a300 0015 046e 7331 610d .....ns1a. +var testPacketDNSPanic7 = []byte{ + 0x00, 0x55, 0x22, 0xaf, 0xc6, 0x37, 0x00, 0x22, 0x55, 0xac, 0xde, 0xac, 0x08, 0x00, 0x45, 0x00, + 0x02, 0x1c, 0xb5, 0xca, 0x40, 0x00, 0xfa, 0x11, 0xb4, 0x6a, 0x0a, 0x4d, 0x00, 0x0b, 0x0a, 0x01, + 0x00, 0x43, 0x00, 0x35, 0xd9, 0xe1, 0x02, 0x08, 0xaf, 0xd6, 0xa3, 0x50, 0x86, 0x00, 0x00, 0x01, + 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x07, 0x6f, 0x75, 0x74, 0x6c, 0x6f, 0x6f, 0x6b, 0x03, 0x63, + 0x6f, 0x6d, 0x00, 0x00, 0x10, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x2c, 0x00, 0x45, 0x44, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2d, 0x73, 0x69, 0x74, 0x65, 0x2d, + 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x44, 0x43, 0x32, + 0x75, 0x43, 0x2d, 0x54, 0x38, 0x6b, 0x44, 0x33, 0x33, 0x6c, 0x49, 0x4e, 0x68, 0x4e, 0x7a, 0x66, + 0x6f, 0x30, 0x62, 0x4e, 0x42, 0x72, 0x77, 0x2d, 0x76, 0x72, 0x43, 0x58, 0x73, 0x35, 0x42, 0x50, + 0x46, 0x35, 0x42, 0x58, 0x59, 0x35, 0x36, 0x67, 0xc0, 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x2c, 0x00, 0xd3, 0xd2, 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x3a, 0x73, 0x70, 0x66, 0x2d, 0x61, 0x2e, 0x6f, 0x75, 0x74, 0x6c, 0x6f, 0x6f, + 0x6b, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x73, 0x70, + 0x66, 0x2d, 0x62, 0x2e, 0x6f, 0x75, 0x74, 0x6c, 0x6f, 0x6f, 0x6b, 0x2e, 0x63, 0x6f, 0x6d, 0x20, + 0x69, 0x70, 0x34, 0x3a, 0x31, 0x35, 0x37, 0x2e, 0x35, 0x35, 0x2e, 0x39, 0x2e, 0x31, 0x32, 0x38, + 0x2f, 0x32, 0x35, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x73, 0x70, 0x66, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6f, 0x75, 0x74, 0x6c, 0x6f, + 0x6f, 0x6b, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x73, + 0x70, 0x66, 0x2d, 0x61, 0x2e, 0x68, 0x6f, 0x74, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, + 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2d, 0x73, 0x73, + 0x67, 0x2d, 0x62, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2d, 0x73, + 0x73, 0x67, 0x2d, 0x63, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x20, 0x7e, 0x61, 0x6c, 0x6c, 0xc0, 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x2c, 0x00, 0x45, 0x44, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2d, 0x73, 0x69, 0x74, 0x65, 0x2d, + 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x30, 0x69, 0x4c, + 0x57, 0x68, 0x49, 0x4d, 0x68, 0x58, 0x45, 0x6b, 0x65, 0x57, 0x77, 0x57, 0x66, 0x46, 0x55, 0x34, + 0x75, 0x72, 0x73, 0x54, 0x6e, 0x2d, 0x5f, 0x4f, 0x76, 0x6f, 0x4f, 0x6a, 0x61, 0x41, 0x30, 0x4c, + 0x72, 0x37, 0x50, 0x67, 0x31, 0x73, 0x45, 0x4d, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, + 0xa3, 0x00, 0x00, 0x0e, 0x03, 0x6e, 0x73, 0x32, 0x04, 0x6d, 0x73, 0x66, 0x74, 0x03, 0x6e, 0x65, + 0x74, 0x00, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, 0x03, 0x6e, + 0x73, 0x34, 0xc1, 0xae, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, + 0x03, 0x6e, 0x73, 0x31, 0xc1, 0xae, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, + 0x00, 0x06, 0x03, 0x6e, 0x73, 0x33, 0xc1, 0xae, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, + 0xa3, 0x00, 0x00, 0x15, 0x04, 0x6e, 0x73, 0x31, 0x61, 0x0d, +} + +func TestPacketDNSPanic7(t *testing.T) { + p := gopacket.NewPacket(testPacketDNSPanic7, LinkTypeEthernet, testDecodeOptions) + if errLayer := p.ErrorLayer(); errLayer == nil { + t.Error("No error layer on invalid DNS name") + } else if err := errLayer.Error(); !strings.Contains(err.Error(), "resource record length exceeds data") { + t.Errorf("unexpected error message: %v", err) + } +} diff --git a/vendor/github.com/google/gopacket/layers/doc.go b/vendor/github.com/google/gopacket/layers/doc.go new file mode 100644 index 00000000..3c882c3f --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/doc.go @@ -0,0 +1,61 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/* +Package layers provides decoding layers for many common protocols. + +The layers package contains decode implementations for a number of different +types of packet layers. Users of gopacket will almost always want to also use +layers to actually decode packet data into useful pieces. To see the set of +protocols that gopacket/layers is currently able to decode, +look at the set of LayerTypes defined in the Variables sections. The +layers package also defines endpoints for many of the common packet layers +that have source/destination addresses associated with them, for example IPv4/6 +(IPs) and TCP/UDP (ports). +Finally, layers contains a number of useful enumerations (IPProtocol, +EthernetType, LinkType, PPPType, etc...). Many of these implement the +gopacket.Decoder interface, so they can be passed into gopacket as decoders. + +Most common protocol layers are named using acronyms or other industry-common +names (IPv4, TCP, PPP). Some of the less common ones have their names expanded +(CiscoDiscoveryProtocol). +For certain protocols, sub-parts of the protocol are split out into their own +layers (SCTP, for example). This is done mostly in cases where portions of the +protocol may fulfill the capabilities of interesting layers (SCTPData implements +ApplicationLayer, while base SCTP implements TransportLayer), or possibly +because splitting a protocol into a few layers makes decoding easier. + +This package is meant to be used with its parent, +http://github.com/google/gopacket. + +Port Types + +Instead of using raw uint16 or uint8 values for ports, we use a different port +type for every protocol, for example TCPPort and UDPPort. This allows us to +override string behavior for each port, which we do by setting up port name +maps (TCPPortNames, UDPPortNames, etc...). Well-known ports are annotated with +their protocol names, and their String function displays these names: + + p := TCPPort(80) + fmt.Printf("Number: %d String: %v", p, p) + // Prints: "Number: 80 String: 80(http)" + +Modifying Decode Behavior + +layers links together decoding through its enumerations. For example, after +decoding layer type Ethernet, it uses Ethernet.EthernetType as its next decoder. +All enumerations that act as decoders, like EthernetType, can be modified by +users depending on their preferences. For example, if you have a spiffy new +IPv4 decoder that works way better than the one built into layers, you can do +this: + + var mySpiffyIPv4Decoder gopacket.Decoder = ... + layers.EthernetTypeMetadata[EthernetTypeIPv4].DecodeWith = mySpiffyIPv4Decoder + +This will make all future ethernet packets use your new decoder to decode IPv4 +packets, instead of the built-in decoder used by gopacket. +*/ +package layers diff --git a/vendor/github.com/google/gopacket/layers/dot11.go b/vendor/github.com/google/gopacket/layers/dot11.go new file mode 100644 index 00000000..1b53026f --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dot11.go @@ -0,0 +1,1430 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// See http://standards.ieee.org/findstds/standard/802.11-2012.html for info on +// all of the layers in this file. + +package layers + +import ( + "bytes" + "encoding/binary" + "fmt" + "hash/crc32" + "net" + + "github.com/google/gopacket" +) + +// Dot11Flags contains the set of 8 flags in the IEEE 802.11 frame control +// header, all in one place. +type Dot11Flags uint8 + +const ( + Dot11FlagsToDS Dot11Flags = 1 << iota + Dot11FlagsFromDS + Dot11FlagsMF + Dot11FlagsRetry + Dot11FlagsPowerManagement + Dot11FlagsMD + Dot11FlagsWEP + Dot11FlagsOrder +) + +func (d Dot11Flags) ToDS() bool { + return d&Dot11FlagsToDS != 0 +} +func (d Dot11Flags) FromDS() bool { + return d&Dot11FlagsFromDS != 0 +} +func (d Dot11Flags) MF() bool { + return d&Dot11FlagsMF != 0 +} +func (d Dot11Flags) Retry() bool { + return d&Dot11FlagsRetry != 0 +} +func (d Dot11Flags) PowerManagement() bool { + return d&Dot11FlagsPowerManagement != 0 +} +func (d Dot11Flags) MD() bool { + return d&Dot11FlagsMD != 0 +} +func (d Dot11Flags) WEP() bool { + return d&Dot11FlagsWEP != 0 +} +func (d Dot11Flags) Order() bool { + return d&Dot11FlagsOrder != 0 +} + +// String provides a human readable string for Dot11Flags. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Flags value, not its string. +func (a Dot11Flags) String() string { + var out bytes.Buffer + if a.ToDS() { + out.WriteString("TO-DS,") + } + if a.FromDS() { + out.WriteString("FROM-DS,") + } + if a.MF() { + out.WriteString("MF,") + } + if a.Retry() { + out.WriteString("Retry,") + } + if a.PowerManagement() { + out.WriteString("PowerManagement,") + } + if a.MD() { + out.WriteString("MD,") + } + if a.WEP() { + out.WriteString("WEP,") + } + if a.Order() { + out.WriteString("Order,") + } + + if length := out.Len(); length > 0 { + return string(out.Bytes()[:length-1]) // strip final comma + } + return "" +} + +type Dot11Reason uint16 + +// TODO: Verify these reasons, and append more reasons if necessary. + +const ( + Dot11ReasonReserved Dot11Reason = 1 + Dot11ReasonUnspecified Dot11Reason = 2 + Dot11ReasonAuthExpired Dot11Reason = 3 + Dot11ReasonDeauthStLeaving Dot11Reason = 4 + Dot11ReasonInactivity Dot11Reason = 5 + Dot11ReasonApFull Dot11Reason = 6 + Dot11ReasonClass2FromNonAuth Dot11Reason = 7 + Dot11ReasonClass3FromNonAss Dot11Reason = 8 + Dot11ReasonDisasStLeaving Dot11Reason = 9 + Dot11ReasonStNotAuth Dot11Reason = 10 +) + +// String provides a human readable string for Dot11Reason. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Reason value, not its string. +func (a Dot11Reason) String() string { + switch a { + case Dot11ReasonReserved: + return "Reserved" + case Dot11ReasonUnspecified: + return "Unspecified" + case Dot11ReasonAuthExpired: + return "Auth. expired" + case Dot11ReasonDeauthStLeaving: + return "Deauth. st. leaving" + case Dot11ReasonInactivity: + return "Inactivity" + case Dot11ReasonApFull: + return "Ap. full" + case Dot11ReasonClass2FromNonAuth: + return "Class2 from non auth." + case Dot11ReasonClass3FromNonAss: + return "Class3 from non ass." + case Dot11ReasonDisasStLeaving: + return "Disass st. leaving" + case Dot11ReasonStNotAuth: + return "St. not auth." + default: + return "Unknown reason" + } +} + +type Dot11Status uint16 + +const ( + Dot11StatusSuccess Dot11Status = 0 + Dot11StatusFailure Dot11Status = 1 // Unspecified failure + Dot11StatusCannotSupportAllCapabilities Dot11Status = 10 // Cannot support all requested capabilities in the Capability Information field + Dot11StatusInabilityExistsAssociation Dot11Status = 11 // Reassociation denied due to inability to confirm that association exists + Dot11StatusAssociationDenied Dot11Status = 12 // Association denied due to reason outside the scope of this standard + Dot11StatusAlgorithmUnsupported Dot11Status = 13 // Responding station does not support the specified authentication algorithm + Dot11StatusOufOfExpectedSequence Dot11Status = 14 // Received an Authentication frame with authentication transaction sequence number out of expected sequence + Dot11StatusChallengeFailure Dot11Status = 15 // Authentication rejected because of challenge failure + Dot11StatusTimeout Dot11Status = 16 // Authentication rejected due to timeout waiting for next frame in sequence + Dot11StatusAPUnableToHandle Dot11Status = 17 // Association denied because AP is unable to handle additional associated stations + Dot11StatusRateUnsupported Dot11Status = 18 // Association denied due to requesting station not supporting all of the data rates in the BSSBasicRateSet parameter +) + +// String provides a human readable string for Dot11Status. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Status value, not its string. +func (a Dot11Status) String() string { + switch a { + case Dot11StatusSuccess: + return "success" + case Dot11StatusFailure: + return "failure" + case Dot11StatusCannotSupportAllCapabilities: + return "cannot-support-all-capabilities" + case Dot11StatusInabilityExistsAssociation: + return "inability-exists-association" + case Dot11StatusAssociationDenied: + return "association-denied" + case Dot11StatusAlgorithmUnsupported: + return "algorithm-unsupported" + case Dot11StatusOufOfExpectedSequence: + return "out-of-expected-sequence" + case Dot11StatusChallengeFailure: + return "challenge-failure" + case Dot11StatusTimeout: + return "timeout" + case Dot11StatusAPUnableToHandle: + return "ap-unable-to-handle" + case Dot11StatusRateUnsupported: + return "rate-unsupported" + default: + return "unknown status" + } +} + +type Dot11AckPolicy uint8 + +const ( + Dot11AckPolicyNormal Dot11AckPolicy = 0 + Dot11AckPolicyNone Dot11AckPolicy = 1 + Dot11AckPolicyNoExplicit Dot11AckPolicy = 2 + Dot11AckPolicyBlock Dot11AckPolicy = 3 +) + +// String provides a human readable string for Dot11AckPolicy. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11AckPolicy value, not its string. +func (a Dot11AckPolicy) String() string { + switch a { + case Dot11AckPolicyNormal: + return "normal-ack" + case Dot11AckPolicyNone: + return "no-ack" + case Dot11AckPolicyNoExplicit: + return "no-explicit-ack" + case Dot11AckPolicyBlock: + return "block-ack" + default: + return "unknown-ack-policy" + } +} + +type Dot11Algorithm uint16 + +const ( + Dot11AlgorithmOpen Dot11Algorithm = 0 + Dot11AlgorithmSharedKey Dot11Algorithm = 1 +) + +// String provides a human readable string for Dot11Algorithm. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Algorithm value, not its string. +func (a Dot11Algorithm) String() string { + switch a { + case Dot11AlgorithmOpen: + return "open" + case Dot11AlgorithmSharedKey: + return "shared-key" + default: + return "unknown-algorithm" + } +} + +type Dot11InformationElementID uint8 + +// TODO: Verify these element ids, and append more ids if more. + +const ( + Dot11InformationElementIDSSID Dot11InformationElementID = 0 + Dot11InformationElementIDRates Dot11InformationElementID = 1 + Dot11InformationElementIDFHSet Dot11InformationElementID = 2 + Dot11InformationElementIDDSSet Dot11InformationElementID = 3 + Dot11InformationElementIDCFSet Dot11InformationElementID = 4 + Dot11InformationElementIDTIM Dot11InformationElementID = 5 + Dot11InformationElementIDIBSSSet Dot11InformationElementID = 6 + Dot11InformationElementIDChallenge Dot11InformationElementID = 16 + Dot11InformationElementIDERPInfo Dot11InformationElementID = 42 + Dot11InformationElementIDQOSCapability Dot11InformationElementID = 46 + Dot11InformationElementIDERPInfo2 Dot11InformationElementID = 47 + Dot11InformationElementIDRSNInfo Dot11InformationElementID = 48 + Dot11InformationElementIDESRates Dot11InformationElementID = 50 + Dot11InformationElementIDVendor Dot11InformationElementID = 221 + Dot11InformationElementIDReserved Dot11InformationElementID = 68 +) + +// String provides a human readable string for Dot11InformationElementID. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11InformationElementID value, +// not its string. +func (a Dot11InformationElementID) String() string { + switch a { + case Dot11InformationElementIDSSID: + return "SSID" + case Dot11InformationElementIDRates: + return "Rates" + case Dot11InformationElementIDFHSet: + return "FHset" + case Dot11InformationElementIDDSSet: + return "DSset" + case Dot11InformationElementIDCFSet: + return "CFset" + case Dot11InformationElementIDTIM: + return "TIM" + case Dot11InformationElementIDIBSSSet: + return "IBSSset" + case Dot11InformationElementIDChallenge: + return "Challenge" + case Dot11InformationElementIDERPInfo: + return "ERPinfo" + case Dot11InformationElementIDQOSCapability: + return "QOS capability" + case Dot11InformationElementIDERPInfo2: + return "ERPinfo2" + case Dot11InformationElementIDRSNInfo: + return "RSNinfo" + case Dot11InformationElementIDESRates: + return "ESrates" + case Dot11InformationElementIDVendor: + return "Vendor" + case Dot11InformationElementIDReserved: + return "Reserved" + default: + return "Unknown information element id" + } +} + +// Dot11 provides an IEEE 802.11 base packet header. +// See http://standards.ieee.org/findstds/standard/802.11-2012.html +// for excrutiating detail. +type Dot11 struct { + BaseLayer + Type Dot11Type + Proto uint8 + Flags Dot11Flags + DurationID uint16 + Address1 net.HardwareAddr + Address2 net.HardwareAddr + Address3 net.HardwareAddr + Address4 net.HardwareAddr + SequenceNumber uint16 + FragmentNumber uint16 + Checksum uint32 +} + +func decodeDot11(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11) LayerType() gopacket.LayerType { return LayerTypeDot11 } +func (m *Dot11) CanDecode() gopacket.LayerClass { return LayerTypeDot11 } +func (m *Dot11) NextLayerType() gopacket.LayerType { + if m.Flags.WEP() { + return (LayerTypeDot11WEP) + } + + return m.Type.LayerType() +} + +func (m *Dot11) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 10 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), 10) + } + m.Type = Dot11Type((data[0])&0xFC) >> 2 + + m.Proto = uint8(data[0]) & 0x0003 + m.Flags = Dot11Flags(data[1]) + m.DurationID = binary.LittleEndian.Uint16(data[2:4]) + m.Address1 = net.HardwareAddr(data[4:10]) + + offset := 10 + + mainType := m.Type.MainType() + + switch mainType { + case Dot11TypeCtrl: + switch m.Type { + case Dot11TypeCtrlRTS, Dot11TypeCtrlPowersavePoll, Dot11TypeCtrlCFEnd, Dot11TypeCtrlCFEndAck: + if len(data) < offset+6 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6) + } + m.Address2 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + } + case Dot11TypeMgmt, Dot11TypeData: + if len(data) < offset+14 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+14) + } + m.Address2 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + m.Address3 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + + m.SequenceNumber = (binary.LittleEndian.Uint16(data[offset:offset+2]) & 0xFFF0) >> 4 + m.FragmentNumber = (binary.LittleEndian.Uint16(data[offset:offset+2]) & 0x000F) + offset += 2 + } + + if mainType == Dot11TypeData && m.Flags.FromDS() && m.Flags.ToDS() { + if len(data) < offset+6 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6) + } + m.Address4 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + } + + m.BaseLayer = BaseLayer{Contents: data[0:offset], Payload: data[offset : len(data)-4]} + m.Checksum = binary.LittleEndian.Uint32(data[len(data)-4 : len(data)]) + return nil +} + +func (m *Dot11) ChecksumValid() bool { + // only for CTRL and MGMT frames + h := crc32.NewIEEE() + h.Write(m.Contents) + h.Write(m.Payload) + return m.Checksum == h.Sum32() +} + +func (m Dot11) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(24) + + if err != nil { + return err + } + + buf[0] = (uint8(m.Type) << 2) | m.Proto + buf[1] = uint8(m.Flags) + + binary.LittleEndian.PutUint16(buf[2:4], m.DurationID) + + copy(buf[4:10], m.Address1) + + offset := 10 + + switch m.Type.MainType() { + case Dot11TypeCtrl: + switch m.Type { + case Dot11TypeCtrlRTS, Dot11TypeCtrlPowersavePoll, Dot11TypeCtrlCFEnd, Dot11TypeCtrlCFEndAck: + copy(buf[offset:offset+6], m.Address2) + offset += 6 + } + case Dot11TypeMgmt, Dot11TypeData: + copy(buf[offset:offset+6], m.Address2) + offset += 6 + copy(buf[offset:offset+6], m.Address3) + offset += 6 + + binary.LittleEndian.PutUint16(buf[offset:offset+2], (m.SequenceNumber<<4)|m.FragmentNumber) + offset += 2 + } + + if m.Type.MainType() == Dot11TypeData && m.Flags.FromDS() && m.Flags.ToDS() { + copy(buf[offset:offset+6], m.Address4) + offset += 6 + } + + return nil +} + +// Dot11Mgmt is a base for all IEEE 802.11 management layers. +type Dot11Mgmt struct { + BaseLayer +} + +func (m *Dot11Mgmt) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } +func (m *Dot11Mgmt) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +// Dot11Ctrl is a base for all IEEE 802.11 control layers. +type Dot11Ctrl struct { + BaseLayer +} + +func (m *Dot11Ctrl) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } + +func (m *Dot11Ctrl) LayerType() gopacket.LayerType { return LayerTypeDot11Ctrl } +func (m *Dot11Ctrl) CanDecode() gopacket.LayerClass { return LayerTypeDot11Ctrl } +func (m *Dot11Ctrl) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeDot11Ctrl(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11Ctrl{} + return decodingLayerDecoder(d, data, p) +} + +// Dot11WEP contains WEP encrpted IEEE 802.11 data. +type Dot11WEP struct { + BaseLayer +} + +func (m *Dot11WEP) NextLayerType() gopacket.LayerType { return LayerTypeLLC } + +func (m *Dot11WEP) LayerType() gopacket.LayerType { return LayerTypeDot11WEP } +func (m *Dot11WEP) CanDecode() gopacket.LayerClass { return LayerTypeDot11WEP } +func (m *Dot11WEP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeDot11WEP(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11WEP{} + return decodingLayerDecoder(d, data, p) +} + +// Dot11Data is a base for all IEEE 802.11 data layers. +type Dot11Data struct { + BaseLayer +} + +func (m *Dot11Data) NextLayerType() gopacket.LayerType { return LayerTypeLLC } + +func (m *Dot11Data) LayerType() gopacket.LayerType { return LayerTypeDot11Data } +func (m *Dot11Data) CanDecode() gopacket.LayerClass { return LayerTypeDot11Data } +func (m *Dot11Data) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Payload = data + return nil +} + +func decodeDot11Data(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11Data{} + return decodingLayerDecoder(d, data, p) +} + +type Dot11DataCFAck struct { + Dot11Data +} + +func decodeDot11DataCFAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFAck } +func (m *Dot11DataCFAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAck } +func (m *Dot11DataCFAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFPoll struct { + Dot11Data +} + +func decodeDot11DataCFPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFPoll) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPoll } +func (m *Dot11DataCFPoll) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFPoll } +func (m *Dot11DataCFPoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFAckPoll struct { + Dot11Data +} + +func decodeDot11DataCFAckPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAckPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAckPoll) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFAckPoll } +func (m *Dot11DataCFAckPoll) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAckPoll } +func (m *Dot11DataCFAckPoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataNull struct { + Dot11Data +} + +func decodeDot11DataNull(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataNull{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataNull) LayerType() gopacket.LayerType { return LayerTypeDot11DataNull } +func (m *Dot11DataNull) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataNull } +func (m *Dot11DataNull) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFAckNoData struct { + Dot11Data +} + +func decodeDot11DataCFAckNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAckNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAckNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFAckNoData } +func (m *Dot11DataCFAckNoData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAckNoData } +func (m *Dot11DataCFAckNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFPollNoData struct { + Dot11Data +} + +func decodeDot11DataCFPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFPollNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPollNoData } +func (m *Dot11DataCFPollNoData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFPollNoData } +func (m *Dot11DataCFPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFAckPollNoData struct { + Dot11Data +} + +func decodeDot11DataCFAckPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAckPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAckPollNoData) LayerType() gopacket.LayerType { + return LayerTypeDot11DataCFAckPollNoData +} +func (m *Dot11DataCFAckPollNoData) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataCFAckPollNoData +} +func (m *Dot11DataCFAckPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataQOS struct { + Dot11Ctrl + TID uint8 /* Traffic IDentifier */ + EOSP bool /* End of service period */ + AckPolicy Dot11AckPolicy + TXOP uint8 +} + +func (m *Dot11DataQOS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("Dot11DataQOS length %v too short, %v required", len(data), 4) + } + m.TID = (uint8(data[0]) & 0x0F) + m.EOSP = (uint8(data[0]) & 0x10) == 0x10 + m.AckPolicy = Dot11AckPolicy((uint8(data[0]) & 0x60) >> 5) + m.TXOP = uint8(data[1]) + // TODO: Mesh Control bytes 2:4 + m.BaseLayer = BaseLayer{Contents: data[0:4], Payload: data[4:]} + return nil +} + +type Dot11DataQOSData struct { + Dot11DataQOS +} + +func decodeDot11DataQOSData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSData) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSData } +func (m *Dot11DataQOSData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSData } + +func (m *Dot11DataQOSData) NextLayerType() gopacket.LayerType { + return LayerTypeDot11Data +} + +type Dot11DataQOSDataCFAck struct { + Dot11DataQOS +} + +func decodeDot11DataQOSDataCFAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSDataCFAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSDataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSDataCFAck } +func (m *Dot11DataQOSDataCFAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSDataCFAck } +func (m *Dot11DataQOSDataCFAck) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFAck } + +type Dot11DataQOSDataCFPoll struct { + Dot11DataQOS +} + +func decodeDot11DataQOSDataCFPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSDataCFPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSDataCFPoll) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSDataCFPoll +} +func (m *Dot11DataQOSDataCFPoll) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSDataCFPoll +} +func (m *Dot11DataQOSDataCFPoll) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFPoll } + +type Dot11DataQOSDataCFAckPoll struct { + Dot11DataQOS +} + +func decodeDot11DataQOSDataCFAckPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSDataCFAckPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSDataCFAckPoll) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSDataCFAckPoll +} +func (m *Dot11DataQOSDataCFAckPoll) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSDataCFAckPoll +} +func (m *Dot11DataQOSDataCFAckPoll) NextLayerType() gopacket.LayerType { + return LayerTypeDot11DataCFAckPoll +} + +type Dot11DataQOSNull struct { + Dot11DataQOS +} + +func decodeDot11DataQOSNull(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSNull{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSNull) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSNull } +func (m *Dot11DataQOSNull) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSNull } +func (m *Dot11DataQOSNull) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataNull } + +type Dot11DataQOSCFPollNoData struct { + Dot11DataQOS +} + +func decodeDot11DataQOSCFPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSCFPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSCFPollNoData) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSCFPollNoData +} +func (m *Dot11DataQOSCFPollNoData) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSCFPollNoData +} +func (m *Dot11DataQOSCFPollNoData) NextLayerType() gopacket.LayerType { + return LayerTypeDot11DataCFPollNoData +} + +type Dot11DataQOSCFAckPollNoData struct { + Dot11DataQOS +} + +func decodeDot11DataQOSCFAckPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSCFAckPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSCFAckPollNoData) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSCFAckPollNoData +} +func (m *Dot11DataQOSCFAckPollNoData) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSCFAckPollNoData +} +func (m *Dot11DataQOSCFAckPollNoData) NextLayerType() gopacket.LayerType { + return LayerTypeDot11DataCFAckPollNoData +} + +type Dot11InformationElement struct { + BaseLayer + ID Dot11InformationElementID + Length uint8 + OUI []byte + Info []byte +} + +func (m *Dot11InformationElement) LayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11InformationElement) CanDecode() gopacket.LayerClass { + return LayerTypeDot11InformationElement +} + +func (m *Dot11InformationElement) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +func (m *Dot11InformationElement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), 2) + } + m.ID = Dot11InformationElementID(data[0]) + m.Length = data[1] + offset := int(2) + + if len(data) < offset+int(m.Length) { + df.SetTruncated() + return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), offset+int(m.Length)) + } + if m.ID == 221 { + // Vendor extension + m.OUI = data[offset : offset+4] + m.Info = data[offset+4 : offset+int(m.Length)] + } else { + m.Info = data[offset : offset+int(m.Length)] + } + + offset += int(m.Length) + + m.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]} + return nil +} + +func (d *Dot11InformationElement) String() string { + if d.ID == 0 { + return fmt.Sprintf("802.11 Information Element (SSID: %v)", string(d.Info)) + } else if d.ID == 1 { + rates := "" + for i := 0; i < len(d.Info); i++ { + if d.Info[i]&0x80 == 0 { + rates += fmt.Sprintf("%.1f ", float32(d.Info[i])*0.5) + } else { + rates += fmt.Sprintf("%.1f* ", float32(d.Info[i]&0x7F)*0.5) + } + } + return fmt.Sprintf("802.11 Information Element (Rates: %s Mbit)", rates) + } else if d.ID == 221 { + return fmt.Sprintf("802.11 Information Element (Vendor: ID: %v, Length: %v, OUI: %X, Info: %X)", d.ID, d.Length, d.OUI, d.Info) + } else { + return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, Info: %X)", d.ID, d.Length, d.Info) + } +} + +func (m Dot11InformationElement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + length := len(m.Info) + len(m.OUI) + if buf, err := b.PrependBytes(2 + length); err != nil { + return err + } else { + buf[0] = uint8(m.ID) + buf[1] = uint8(length) + copy(buf[2:], m.OUI) + copy(buf[2+len(m.OUI):], m.Info) + } + return nil +} + +func decodeDot11InformationElement(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11InformationElement{} + return decodingLayerDecoder(d, data, p) +} + +type Dot11CtrlCTS struct { + Dot11Ctrl +} + +func decodeDot11CtrlCTS(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlCTS{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlCTS) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlCTS +} +func (m *Dot11CtrlCTS) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlCTS +} +func (m *Dot11CtrlCTS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlRTS struct { + Dot11Ctrl +} + +func decodeDot11CtrlRTS(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlRTS{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlRTS) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlRTS +} +func (m *Dot11CtrlRTS) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlRTS +} +func (m *Dot11CtrlRTS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlBlockAckReq struct { + Dot11Ctrl +} + +func decodeDot11CtrlBlockAckReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlBlockAckReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlBlockAckReq) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlBlockAckReq +} +func (m *Dot11CtrlBlockAckReq) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlBlockAckReq +} +func (m *Dot11CtrlBlockAckReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlBlockAck struct { + Dot11Ctrl +} + +func decodeDot11CtrlBlockAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlBlockAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlBlockAck) LayerType() gopacket.LayerType { return LayerTypeDot11CtrlBlockAck } +func (m *Dot11CtrlBlockAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11CtrlBlockAck } +func (m *Dot11CtrlBlockAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlPowersavePoll struct { + Dot11Ctrl +} + +func decodeDot11CtrlPowersavePoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlPowersavePoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlPowersavePoll) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlPowersavePoll +} +func (m *Dot11CtrlPowersavePoll) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlPowersavePoll +} +func (m *Dot11CtrlPowersavePoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlAck struct { + Dot11Ctrl +} + +func decodeDot11CtrlAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlAck) LayerType() gopacket.LayerType { return LayerTypeDot11CtrlAck } +func (m *Dot11CtrlAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11CtrlAck } +func (m *Dot11CtrlAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlCFEnd struct { + Dot11Ctrl +} + +func decodeDot11CtrlCFEnd(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlCFEnd{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlCFEnd) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlCFEnd +} +func (m *Dot11CtrlCFEnd) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlCFEnd +} +func (m *Dot11CtrlCFEnd) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlCFEndAck struct { + Dot11Ctrl +} + +func decodeDot11CtrlCFEndAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlCFEndAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlCFEndAck) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlCFEndAck +} +func (m *Dot11CtrlCFEndAck) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlCFEndAck +} +func (m *Dot11CtrlCFEndAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11MgmtAssociationReq struct { + Dot11Mgmt + CapabilityInfo uint16 + ListenInterval uint16 +} + +func decodeDot11MgmtAssociationReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAssociationReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAssociationReq) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtAssociationReq +} +func (m *Dot11MgmtAssociationReq) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtAssociationReq +} +func (m *Dot11MgmtAssociationReq) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtAssociationReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtAssociationReq length %v too short, %v required", len(data), 4) + } + m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2]) + m.ListenInterval = binary.LittleEndian.Uint16(data[2:4]) + m.Payload = data[4:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtAssociationReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(4) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo) + binary.LittleEndian.PutUint16(buf[2:4], m.ListenInterval) + + return nil +} + +type Dot11MgmtAssociationResp struct { + Dot11Mgmt + CapabilityInfo uint16 + Status Dot11Status + AID uint16 +} + +func decodeDot11MgmtAssociationResp(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAssociationResp{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAssociationResp) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtAssociationResp +} +func (m *Dot11MgmtAssociationResp) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtAssociationResp +} +func (m *Dot11MgmtAssociationResp) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtAssociationResp) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 6 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtAssociationResp length %v too short, %v required", len(data), 6) + } + m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2]) + m.Status = Dot11Status(binary.LittleEndian.Uint16(data[2:4])) + m.AID = binary.LittleEndian.Uint16(data[4:6]) + m.Payload = data[6:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtAssociationResp) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(6) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo) + binary.LittleEndian.PutUint16(buf[2:4], uint16(m.Status)) + binary.LittleEndian.PutUint16(buf[4:6], m.AID) + + return nil +} + +type Dot11MgmtReassociationReq struct { + Dot11Mgmt + CapabilityInfo uint16 + ListenInterval uint16 + CurrentApAddress net.HardwareAddr +} + +func decodeDot11MgmtReassociationReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtReassociationReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtReassociationReq) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtReassociationReq +} +func (m *Dot11MgmtReassociationReq) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtReassociationReq +} +func (m *Dot11MgmtReassociationReq) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtReassociationReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 10 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtReassociationReq length %v too short, %v required", len(data), 10) + } + m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2]) + m.ListenInterval = binary.LittleEndian.Uint16(data[2:4]) + m.CurrentApAddress = net.HardwareAddr(data[4:10]) + m.Payload = data[10:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtReassociationReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(10) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo) + binary.LittleEndian.PutUint16(buf[2:4], m.ListenInterval) + + copy(buf[4:10], m.CurrentApAddress) + + return nil +} + +type Dot11MgmtReassociationResp struct { + Dot11Mgmt +} + +func decodeDot11MgmtReassociationResp(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtReassociationResp{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtReassociationResp) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtReassociationResp +} +func (m *Dot11MgmtReassociationResp) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtReassociationResp +} +func (m *Dot11MgmtReassociationResp) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +type Dot11MgmtProbeReq struct { + Dot11Mgmt +} + +func decodeDot11MgmtProbeReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtProbeReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtProbeReq) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtProbeReq } +func (m *Dot11MgmtProbeReq) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtProbeReq } +func (m *Dot11MgmtProbeReq) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +type Dot11MgmtProbeResp struct { + Dot11Mgmt + Timestamp uint64 + Interval uint16 + Flags uint16 +} + +func decodeDot11MgmtProbeResp(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtProbeResp{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtProbeResp) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtProbeResp } +func (m *Dot11MgmtProbeResp) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtProbeResp } +func (m *Dot11MgmtProbeResp) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 12 { + df.SetTruncated() + + return fmt.Errorf("Dot11MgmtProbeResp length %v too short, %v required", len(data), 12) + } + + m.Timestamp = binary.LittleEndian.Uint64(data[0:8]) + m.Interval = binary.LittleEndian.Uint16(data[8:10]) + m.Flags = binary.LittleEndian.Uint16(data[10:12]) + m.Payload = data[12:] + + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m *Dot11MgmtProbeResp) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +func (m Dot11MgmtProbeResp) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(12) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint64(buf[0:8], m.Timestamp) + binary.LittleEndian.PutUint16(buf[8:10], m.Interval) + binary.LittleEndian.PutUint16(buf[10:12], m.Flags) + + return nil +} + +type Dot11MgmtMeasurementPilot struct { + Dot11Mgmt +} + +func decodeDot11MgmtMeasurementPilot(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtMeasurementPilot{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtMeasurementPilot) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtMeasurementPilot +} +func (m *Dot11MgmtMeasurementPilot) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtMeasurementPilot +} + +type Dot11MgmtBeacon struct { + Dot11Mgmt + Timestamp uint64 + Interval uint16 + Flags uint16 +} + +func decodeDot11MgmtBeacon(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtBeacon{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtBeacon) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtBeacon } +func (m *Dot11MgmtBeacon) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtBeacon } +func (m *Dot11MgmtBeacon) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 12 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtBeacon length %v too short, %v required", len(data), 12) + } + m.Timestamp = binary.LittleEndian.Uint64(data[0:8]) + m.Interval = binary.LittleEndian.Uint16(data[8:10]) + m.Flags = binary.LittleEndian.Uint16(data[10:12]) + m.Payload = data[12:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m *Dot11MgmtBeacon) NextLayerType() gopacket.LayerType { return LayerTypeDot11InformationElement } + +func (m Dot11MgmtBeacon) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(12) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint64(buf[0:8], m.Timestamp) + binary.LittleEndian.PutUint16(buf[8:10], m.Interval) + binary.LittleEndian.PutUint16(buf[10:12], m.Flags) + + return nil +} + +type Dot11MgmtATIM struct { + Dot11Mgmt +} + +func decodeDot11MgmtATIM(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtATIM{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtATIM) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtATIM } +func (m *Dot11MgmtATIM) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtATIM } + +type Dot11MgmtDisassociation struct { + Dot11Mgmt + Reason Dot11Reason +} + +func decodeDot11MgmtDisassociation(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtDisassociation{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtDisassociation) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtDisassociation +} +func (m *Dot11MgmtDisassociation) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtDisassociation +} +func (m *Dot11MgmtDisassociation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtDisassociation length %v too short, %v required", len(data), 2) + } + m.Reason = Dot11Reason(binary.LittleEndian.Uint16(data[0:2])) + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtDisassociation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(2) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Reason)) + + return nil +} + +type Dot11MgmtAuthentication struct { + Dot11Mgmt + Algorithm Dot11Algorithm + Sequence uint16 + Status Dot11Status +} + +func decodeDot11MgmtAuthentication(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAuthentication{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAuthentication) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtAuthentication +} +func (m *Dot11MgmtAuthentication) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtAuthentication +} +func (m *Dot11MgmtAuthentication) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtAuthentication) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 6 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtAuthentication length %v too short, %v required", len(data), 6) + } + m.Algorithm = Dot11Algorithm(binary.LittleEndian.Uint16(data[0:2])) + m.Sequence = binary.LittleEndian.Uint16(data[2:4]) + m.Status = Dot11Status(binary.LittleEndian.Uint16(data[4:6])) + m.Payload = data[6:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtAuthentication) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(6) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Algorithm)) + binary.LittleEndian.PutUint16(buf[2:4], m.Sequence) + binary.LittleEndian.PutUint16(buf[4:6], uint16(m.Status)) + + return nil +} + +type Dot11MgmtDeauthentication struct { + Dot11Mgmt + Reason Dot11Reason +} + +func decodeDot11MgmtDeauthentication(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtDeauthentication{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtDeauthentication) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtDeauthentication +} +func (m *Dot11MgmtDeauthentication) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtDeauthentication +} +func (m *Dot11MgmtDeauthentication) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtDeauthentication length %v too short, %v required", len(data), 2) + } + m.Reason = Dot11Reason(binary.LittleEndian.Uint16(data[0:2])) + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtDeauthentication) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(2) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Reason)) + + return nil +} + +type Dot11MgmtAction struct { + Dot11Mgmt +} + +func decodeDot11MgmtAction(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAction{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAction) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtAction } +func (m *Dot11MgmtAction) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtAction } + +type Dot11MgmtActionNoAck struct { + Dot11Mgmt +} + +func decodeDot11MgmtActionNoAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtActionNoAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtActionNoAck) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtActionNoAck } +func (m *Dot11MgmtActionNoAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtActionNoAck } + +type Dot11MgmtArubaWLAN struct { + Dot11Mgmt +} + +func decodeDot11MgmtArubaWLAN(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtArubaWLAN{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtArubaWLAN) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtArubaWLAN } +func (m *Dot11MgmtArubaWLAN) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtArubaWLAN } diff --git a/vendor/github.com/google/gopacket/layers/dot11_test.go b/vendor/github.com/google/gopacket/layers/dot11_test.go new file mode 100644 index 00000000..b0299fc6 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dot11_test.go @@ -0,0 +1,495 @@ +// Copyright 2014, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "net" + "reflect" + "testing" + + "github.com/google/gopacket" +) + +// Generator: python layers/test_creator.py --layerType=LayerTypeRadioTap --linkType=LinkTypeIEEE80211Radio --name=Dot11%s ~/Downloads/mesh.pcap +// http://wiki.wireshark.org/SampleCaptures#Sample_Captures + +// testPacketDot11CtrlCTS is the packet: +// 09:28:41.830560 20604983us tsft short preamble 24.0 Mb/s 5240 MHz 11a -79dB signal -92dB noise antenna 1 Clear-To-Send RA:d8:a2:5e:97:61:c1 +// 0x0000: 0000 1900 6f08 0000 3768 3a01 0000 0000 ....o...7h:..... +// 0x0010: 1230 7814 4001 b1a4 01c4 0094 00d8 a25e .0x.@..........^ +// 0x0020: 9761 c136 5095 8e .a.6P.. + +var testPacketDot11CtrlCTS = []byte{ + 0x00, 0x00, 0x19, 0x00, 0x6f, 0x08, 0x00, 0x00, 0x37, 0x68, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x30, 0x78, 0x14, 0x40, 0x01, 0xb1, 0xa4, 0x01, 0xc4, 0x00, 0x94, 0x00, 0xd8, 0xa2, 0x5e, + 0x97, 0x61, 0xc1, 0x36, 0x50, 0x95, 0x8e, +} + +func TestPacketDot11CtrlCTS(t *testing.T) { + p := gopacket.NewPacket(testPacketDot11CtrlCTS, LinkTypeIEEE80211Radio, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11}, t) + + if got, ok := p.Layer(LayerTypeRadioTap).(*RadioTap); ok { + want := &RadioTap{ + BaseLayer: BaseLayer{ + Contents: []uint8{0x0, 0x0, 0x19, 0x0, 0x6f, 0x8, 0x0, 0x0, 0x37, 0x68, 0x3a, 0x1, 0x0, 0x0, 0x0, 0x0, 0x12, 0x30, 0x78, 0x14, 0x40, 0x1, 0xb1, 0xa4, 0x1}, + Payload: []uint8{0xc4, 0x0, 0x94, 0x0, 0xd8, 0xa2, 0x5e, 0x97, 0x61, 0xc1, 0x36, 0x50, 0x95, 0x8e}, + }, + Version: 0x0, + Length: 0x19, + Present: 0x86f, + TSFT: 0x13a6837, + Flags: 0x12, + Rate: 0x30, + ChannelFrequency: 0x1478, + ChannelFlags: 0x140, + FHSS: 0x0, + DBMAntennaSignal: -79, + DBMAntennaNoise: -92, + LockQuality: 0x0, + TxAttenuation: 0x0, + DBTxAttenuation: 0x0, + DBMTxPower: 0, + Antenna: 1, + DBAntennaSignal: 0x0, + DBAntennaNoise: 0x0, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("RadioTap packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } + + if got, ok := p.Layer(LayerTypeDot11).(*Dot11); ok { + if !got.ChecksumValid() { + t.Errorf("Dot11 packet processing failed:\nchecksum failed. got :\n%#v\n\n", got) + } + + want := &Dot11{ + BaseLayer: BaseLayer{ + Contents: []uint8{0xc4, 0x0, 0x94, 0x0, 0xd8, 0xa2, 0x5e, 0x97, 0x61, 0xc1}, + Payload: []uint8{}, + }, + Type: Dot11TypeCtrlCTS, + Proto: 0x0, + Flags: 0x0, + DurationID: 0x94, + Address1: net.HardwareAddr{0xd8, 0xa2, 0x5e, 0x97, 0x61, 0xc1}, // check + Address2: net.HardwareAddr(nil), + Address3: net.HardwareAddr(nil), + Address4: net.HardwareAddr(nil), + Checksum: 0x8e955036, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Dot11 packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } +} + +func BenchmarkDecodePacketDot11CtrlCTS(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDot11CtrlCTS, LinkTypeIEEE80211Radio, gopacket.NoCopy) + } +} + +// testPacketDot11MgmtBeacon is the packet: +// 15:44:56.531833 6.0 Mb/s 2412 MHz 11g -81dB signal antenna 5 Beacon (Wi2) [6.0* 9.0 12.0* 18.0 24.0* 36.0 48.0 54.0 Mbit] ESS CH: 1 +// 0x0000: 0000 1200 2e48 0000 100c 6c09 c000 af05 .....H....l..... +// 0x0010: 0000 8000 0000 ffff ffff ffff c08a de01 ................ +// 0x0020: 11b8 c08a de01 11b8 f097 80f1 30bc 1300 ............0... +// 0x0030: 0000 6400 2104 0003 5769 3201 088c 1298 ..d.!...Wi2..... +// 0x0040: 24b0 4860 6c03 0101 0504 0001 0000 2a01 $.H`l.........*. +// 0x0050: 00dd 1800 50f2 0201 0181 0007 a400 0023 ....P..........# +// 0x0060: a400 0042 435e 0062 322f 00dd 1e00 904c ...BC^.b2/.....L +// 0x0070: 338c 011b ffff 0000 0000 0000 0000 0000 3............... +// 0x0080: 1000 0000 0000 0000 0000 002d 1a8c 011b ...........-.... +// 0x0090: ffff 0000 0000 0000 0000 0000 1000 0000 ................ +// 0x00a0: 0000 0000 0000 00dd 1a00 904c 3401 0000 ...........L4... +// 0x00b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x00c0: 0000 003d 1601 0000 0000 0000 0000 0000 ...=............ +// 0x00d0: 0000 0000 0000 0000 0000 007f 0400 0000 ................ +// 0x00e0: 00dd 0800 1392 0100 0185 0094 0b90 15 ............... +var testPacketDot11MgmtBeacon = []byte{ + 0x00, 0x00, 0x12, 0x00, 0x2e, 0x48, 0x00, 0x00, 0x10, 0x0c, 0x6c, 0x09, 0xc0, 0x00, 0xaf, 0x05, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x8a, 0xde, 0x01, + 0x11, 0xb8, 0xc0, 0x8a, 0xde, 0x01, 0x11, 0xb8, 0xf0, 0x97, 0x80, 0xf1, 0x30, 0xbc, 0x13, 0x00, + 0x00, 0x00, 0x64, 0x00, 0x21, 0x04, 0x00, 0x03, 0x57, 0x69, 0x32, 0x01, 0x08, 0x8c, 0x12, 0x98, + 0x24, 0xb0, 0x48, 0x60, 0x6c, 0x03, 0x01, 0x01, 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x2a, 0x01, + 0x00, 0xdd, 0x18, 0x00, 0x50, 0xf2, 0x02, 0x01, 0x01, 0x81, 0x00, 0x07, 0xa4, 0x00, 0x00, 0x23, + 0xa4, 0x00, 0x00, 0x42, 0x43, 0x5e, 0x00, 0x62, 0x32, 0x2f, 0x00, 0xdd, 0x1e, 0x00, 0x90, 0x4c, + 0x33, 0x8c, 0x01, 0x1b, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x1a, 0x8c, 0x01, 0x1b, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x1a, 0x00, 0x90, 0x4c, 0x34, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3d, 0x16, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x04, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x08, 0x00, 0x13, 0x92, 0x01, 0x00, 0x01, 0x85, 0x00, 0x94, 0x0b, 0x90, 0x15, +} + +func TestPacketDot11MgmtBeacon(t *testing.T) { + p := gopacket.NewPacket(testPacketDot11MgmtBeacon, LinkTypeIEEE80211Radio, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + expectedLayers := []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11, LayerTypeDot11MgmtBeacon} + for i := 0; i < 12; i++ { + expectedLayers = append(expectedLayers, LayerTypeDot11InformationElement) + } + checkLayers(p, expectedLayers, t) + + if p.Layer(LayerTypeDot11).(*Dot11).SequenceNumber != 2431 { + t.Error("dot11 invalid sequence number") + } + if p.Layer(LayerTypeDot11).(*Dot11).FragmentNumber != 0 { + t.Error("dot11 invalid fragment number") + } + if _, ok := p.Layer(LayerTypeDot11MgmtBeacon).(*Dot11MgmtBeacon); !ok { + t.Errorf("dot11 management beacon frame was expected") + } +} + +func BenchmarkDecodePacketDot11MgmtBeacon(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDot11MgmtBeacon, LinkTypeIEEE80211Radio, gopacket.NoCopy) + } +} + +// testPacketDot11DataQOSData is the packet: +// 06:14:27.838634 638790765us tsft short preamble 54.0 Mb/s -51dB signal -96dB noise antenna 2 5180 MHz 11a CF +QoS ARP, Request who-has 140.180.51.68 tell 169.254.247.0, length 28 +// 0x0000: 0000 2000 6708 0400 6d2c 1326 0000 0000 ....g...m,.&.... +// 0x0010: 226c cda0 0200 0000 4001 0000 3c14 2411 "l......@...<.$. +// 0x0020: 8801 2c00 0603 7f07 a016 0019 e3d3 5352 ..,...........SR +// 0x0030: ffff ffff ffff 5064 0000 50aa aaaa 0300 ......Pd..P..... +// 0x0040: 0000 0806 0001 0800 0604 0001 0019 e3d3 ................ +// 0x0050: 5352 a9fe f700 0000 0000 0000 8cb4 3344 SR............3D +var testPacketDot11DataQOSData = []byte{ + 0x00, 0x00, 0x20, 0x00, 0x67, 0x08, 0x04, 0x00, 0x6d, 0x2c, 0x13, 0x26, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x6c, 0xcd, 0xa0, 0x02, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x3c, 0x14, 0x24, 0x11, + 0x88, 0x01, 0x2c, 0x00, 0x06, 0x03, 0x7f, 0x07, 0xa0, 0x16, 0x00, 0x19, 0xe3, 0xd3, 0x53, 0x52, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x50, 0x64, 0x00, 0x00, 0x50, 0xaa, 0xaa, 0xaa, 0x03, 0x00, + 0x00, 0x00, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x19, 0xe3, 0xd3, + 0x53, 0x52, 0xa9, 0xfe, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xb4, 0x33, 0x44, +} + +func TestPacketDot11DataQOSData(t *testing.T) { + p := gopacket.NewPacket(testPacketDot11DataQOSData, LinkTypeIEEE80211Radio, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11, LayerTypeDot11DataQOSData, LayerTypeDot11Data, LayerTypeLLC, LayerTypeSNAP, LayerTypeARP}, t) + + if got, ok := p.Layer(LayerTypeARP).(*ARP); ok { + want := &ARP{BaseLayer: BaseLayer{ + Contents: []uint8{0x0, 0x1, 0x8, 0x0, 0x6, 0x4, 0x0, 0x1, 0x0, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0xa9, 0xfe, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0xb4, 0x33, 0x44}, + Payload: []uint8{}, + }, + AddrType: 0x1, + Protocol: 0x800, + HwAddressSize: 0x6, + ProtAddressSize: 0x4, + Operation: 0x1, + SourceHwAddress: []uint8{0x0, 0x19, 0xe3, 0xd3, 0x53, 0x52}, + SourceProtAddress: []uint8{0xa9, 0xfe, 0xf7, 0x0}, + DstHwAddress: []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + DstProtAddress: []uint8{0x8c, 0xb4, 0x33, 0x44}, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("ARP packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } +} +func BenchmarkDecodePacketDot11DataQOSData(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDot11DataQOSData, LinkTypeIEEE80211Radio, gopacket.NoCopy) + } +} + +// testPacketDot11MgmtAction is the packet: +// 15:54:43.236460 1.0 Mb/s 2412 MHz 11b -67dB signal antenna 5 Action (8e:3a:e3:44:ac:c6): Spectrum Management Act#4 +// 0x0000: 0000 1200 2e48 0000 1002 6c09 a000 bd05 .....H....l..... +// 0x0010: 0000 d000 0000 ffff ffff ffff 8e3a e344 .............:.D +// 0x0020: acc6 8e3a e344 acc6 001b 0004 2503 0001 ...:.D......%... +// 0x0030: 0055 39f0 33 .U9.3 +var testPacketDot11MgmtAction = []byte{ + 0x00, 0x00, 0x12, 0x00, 0x2e, 0x48, 0x00, 0x00, 0x10, 0x02, 0x6c, 0x09, 0xa0, 0x00, 0xbd, 0x05, + 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e, 0x3a, 0xe3, 0x44, + 0xac, 0xc6, 0x8e, 0x3a, 0xe3, 0x44, 0xac, 0xc6, 0x00, 0x1b, 0x00, 0x04, 0x25, 0x03, 0x00, 0x01, + 0x00, 0x55, 0x39, 0xf0, 0x33, +} + +func TestPacketDot11MgmtAction(t *testing.T) { + p := gopacket.NewPacket(testPacketDot11MgmtAction, LinkTypeIEEE80211Radio, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11, LayerTypeDot11MgmtAction}, t) + if got, ok := p.Layer(LayerTypeDot11).(*Dot11); !ok { + t.Errorf("dot11 frame was not parsed") + } else if !got.ChecksumValid() { + t.Errorf("Dot11 packet processing failed: checksum failed") + } + if got, ok := p.Layer(LayerTypeDot11MgmtAction).(*Dot11MgmtAction); !ok { + t.Errorf("management action frame was not parsed") + } else if got.Contents[0] != 0 { + t.Errorf("action category was not spectrum management") + } +} + +func BenchmarkDecodePacketDot11MgmtAction(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDot11MgmtAction, LinkTypeIEEE80211Radio, gopacket.NoCopy) + } +} + +// testPacketDot11CtrlAck is the packet: +// 06:14:27.838669 638758038us tsft short preamble 24.0 Mb/s -39dB signal -96dB noise antenna 2 5180 MHz 11a Acknowledgment RA:00:19:e3:d3:53:52 +// 0x0000: 0000 2000 6708 0400 96ac 1226 0000 0000 ....g......&.... +// 0x0010: 2230 d9a0 0200 0000 4001 0000 3c14 2411 "0......@...<.$. +// 0x0020: d400 0000 0019 e3d3 5352 46e9 7687 ........SRF.v. +var testPacketDot11CtrlAck = []byte{ + 0x00, 0x00, 0x20, 0x00, 0x67, 0x08, 0x04, 0x00, 0x96, 0xac, 0x12, 0x26, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x30, 0xd9, 0xa0, 0x02, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x3c, 0x14, 0x24, 0x11, + 0xd4, 0x00, 0x00, 0x00, 0x00, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0x46, 0xe9, 0x76, 0x87, +} + +func TestPacketDot11CtrlAck(t *testing.T) { + p := gopacket.NewPacket(testPacketDot11CtrlAck, LinkTypeIEEE80211Radio, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11}, t) + + if got, ok := p.Layer(LayerTypeDot11).(*Dot11); ok { + if !got.ChecksumValid() { + t.Errorf("Dot11 packet processing failed:\nchecksum failed. got :\n%#v\n\n", got) + } + } + + if got, ok := p.Layer(LayerTypeDot11).(*Dot11); ok { + if !got.ChecksumValid() { + t.Errorf("Dot11 packet processing failed:\nchecksum failed. got :\n%#v\n\n", got) + } + want := &Dot11{ + BaseLayer: BaseLayer{ + Contents: []uint8{0xd4, 0x0, 0x0, 0x0, 0x0, 0x19, 0xe3, 0xd3, 0x53, 0x52}, + Payload: []uint8{}, + }, + Type: Dot11TypeCtrlAck, + Proto: 0x0, + Flags: 0x0, + DurationID: 0x0, + Address1: net.HardwareAddr{0x0, 0x19, 0xe3, 0xd3, 0x53, 0x52}, + Address2: net.HardwareAddr(nil), + Address3: net.HardwareAddr(nil), + Address4: net.HardwareAddr(nil), + Checksum: 0x8776e946, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Dot11 packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } +} +func BenchmarkDecodePacketDot11CtrlAck(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDot11CtrlAck, LinkTypeIEEE80211Radio, gopacket.NoCopy) + } +} + +// testPacketDot11DataARP is the packet: +// 06:14:11.512316 622463798us tsft short preamble 6.0 Mb/s -39dB signal -96dB noise antenna 2 5180 MHz 11a ARP, Request who-has 67.8.14.54 tell 169.254.247.0, length 28 +// 0x0000: 0000 2000 6708 0400 360b 1a25 0000 0000 ....g...6..%.... +// 0x0010: 220c d9a0 0200 0000 4001 0000 3c14 2411 ".......@...<.$. +// 0x0020: 0802 0000 ffff ffff ffff 0603 7f07 a016 ................ +// 0x0030: 0019 e3d3 5352 e07f aaaa 0300 0000 0806 ....SR.......... +// 0x0040: 0001 0800 0604 0001 0019 e3d3 5352 a9fe ............SR.. +// 0x0050: f700 0000 0000 0000 4308 0e36 ........C..6 +var testPacketDot11DataARP = []byte{ + 0x00, 0x00, 0x20, 0x00, 0x67, 0x08, 0x04, 0x00, 0x36, 0x0b, 0x1a, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x0c, 0xd9, 0xa0, 0x02, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x3c, 0x14, 0x24, 0x11, + 0x08, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x03, 0x7f, 0x07, 0xa0, 0x16, + 0x00, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0xe0, 0x7f, 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x06, + 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0xa9, 0xfe, + 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x08, 0x0e, 0x36, +} + +func TestPacketDot11DataARP(t *testing.T) { + p := gopacket.NewPacket(testPacketDot11DataARP, LinkTypeIEEE80211Radio, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11, LayerTypeDot11Data, LayerTypeLLC, LayerTypeSNAP, LayerTypeARP}, t) + + if got, ok := p.Layer(LayerTypeARP).(*ARP); ok { + want := &ARP{ + BaseLayer: BaseLayer{ + Contents: []uint8{0x0, 0x1, 0x8, 0x0, 0x6, 0x4, 0x0, 0x1, 0x0, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0xa9, 0xfe, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x43, 0x8, 0xe, 0x36}, + Payload: []uint8{}, + }, + AddrType: 0x1, + Protocol: 0x800, + HwAddressSize: 0x6, + ProtAddressSize: 0x4, + Operation: 0x1, + SourceHwAddress: []uint8{0x0, 0x19, 0xe3, 0xd3, 0x53, 0x52}, + SourceProtAddress: []uint8{0xa9, 0xfe, 0xf7, 0x0}, + DstHwAddress: []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + DstProtAddress: []uint8{0x43, 0x8, 0xe, 0x36}, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("ARP packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } +} + +func BenchmarkDecodePacketDot11DataARP(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDot11DataARP, LinkTypeIEEE80211Radio, gopacket.NoCopy) + } +} + +// testPacketDot11DataIP is the packet: +// 06:14:21.388622 632340487us tsft short preamble 6.0 Mb/s -40dB signal -96dB noise antenna 1 5180 MHz 11a IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:19:e3:d3:53:52, length 300 +// 0x0000: 0000 2000 6708 0400 07c0 b025 0000 0000 ....g......%.... +// 0x0010: 220c d8a0 0100 0000 4001 0000 3c14 2411 ".......@...<.$. +// 0x0020: 0802 0000 ffff ffff ffff 0603 7f07 a016 ................ +// 0x0030: 0019 e3d3 5352 4095 aaaa 0300 0000 0800 ....SR@......... +// 0x0040: 4500 0148 c514 0000 ff11 f590 0000 0000 E..H............ +// 0x0050: ffff ffff 0044 0043 0134 2b39 0101 0600 .....D.C.4+9.... +// 0x0060: 131f 8c43 003c 0000 0000 0000 0000 0000 ...C.<.......... +// 0x0070: 0000 0000 0000 0000 0019 e3d3 5352 0000 ............SR.. +// 0x0080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x0090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x00a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x00b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x00c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x00d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x00e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x00f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x0100: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x0110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x0120: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x0130: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +// 0x0140: 0000 0000 0000 0000 6382 5363 3501 0137 ........c.Sc5..7 +// 0x0150: 0a01 0306 0f77 5ffc 2c2e 2f39 0205 dc3d .....w_.,./9...= +// 0x0160: 0701 0019 e3d3 5352 3304 0076 a700 0c0b ......SR3..v.... +// 0x0170: 4d61 6369 6e74 6f73 682d 34ff 0000 0000 Macintosh-4..... +// 0x0180: 0000 0000 0000 0000 ........ +var testPacketDot11DataIP = []byte{ + 0x00, 0x00, 0x20, 0x00, 0x67, 0x08, 0x04, 0x00, 0x07, 0xc0, 0xb0, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x0c, 0xd8, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x3c, 0x14, 0x24, 0x11, + 0x08, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x03, 0x7f, 0x07, 0xa0, 0x16, + 0x00, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0x40, 0x95, 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x45, 0x00, 0x01, 0x48, 0xc5, 0x14, 0x00, 0x00, 0xff, 0x11, 0xf5, 0x90, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x44, 0x00, 0x43, 0x01, 0x34, 0x2b, 0x39, 0x01, 0x01, 0x06, 0x00, + 0x13, 0x1f, 0x8c, 0x43, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x01, 0x37, + 0x0a, 0x01, 0x03, 0x06, 0x0f, 0x77, 0x5f, 0xfc, 0x2c, 0x2e, 0x2f, 0x39, 0x02, 0x05, 0xdc, 0x3d, + 0x07, 0x01, 0x00, 0x19, 0xe3, 0xd3, 0x53, 0x52, 0x33, 0x04, 0x00, 0x76, 0xa7, 0x00, 0x0c, 0x0b, + 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x2d, 0x34, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestPacketDot11DataIP(t *testing.T) { + p := gopacket.NewPacket(testPacketDot11DataIP, LinkTypeIEEE80211Radio, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11, LayerTypeDot11Data, LayerTypeLLC, LayerTypeSNAP, LayerTypeIPv4, LayerTypeUDP, LayerTypeDHCPv4}, t) +} +func BenchmarkDecodePacketDot11DataIP(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketDot11DataIP, LinkTypeIEEE80211Radio, gopacket.NoCopy) + } +} + +// Encrypted + +/// testPacketP6196 is the packet: +// 09:28:41.830631 20605036us tsft wep -69dB signal -92dB noise antenna 1 5240 MHz 11a ht/40- 162.0 Mb/s MCS 12 40 MHz lon GI mixed BCC FEC [bit 20] CF +QoS Data IV:50a9 Pad 20 KeyID 0 +// 0x0000: 0000 3000 6b08 1c00 6c68 3a01 0000 0000 ..0.k...lh:..... +// 0x0010: 1400 7814 4001 bba4 0160 0e1a 4001 0400 ..x.@....`..@... +// 0x0020: 7814 3022 1f01 0cff b10d 0000 0400 0000 x.0"............ +// 0x0030: 8841 2c00 0025 9c42 c262 d8a2 5e97 61c1 .A,..%.B.b..^.a. +// 0x0040: 0025 9c42 c25f 10db 0000 a950 0020 0000 .%.B._.....P.... +// 0x0050: 0000 f8ab a97e 3fbd d6e1 785b 0040 5f15 .....~?...x[.@_. +// 0x0060: 7123 8711 bd1f ffb9 e5b3 84bb ec2a 0a90 q#...........*.. +// 0x0070: d0a0 1a6f 9033 1083 5179 a0da f833 3a00 ...o.3..Qy...3:. +// 0x0080: 5471 f596 539b 1823 a33c 4908 545c 266a Tq..S..#.> 5 + d.DropEligible = data[0]&0x10 != 0 + d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF + d.Type = EthernetType(binary.BigEndian.Uint16(data[2:4])) + d.BaseLayer = BaseLayer{Contents: data[:4], Payload: data[4:]} + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (d *Dot1Q) CanDecode() gopacket.LayerClass { + return LayerTypeDot1Q +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (d *Dot1Q) NextLayerType() gopacket.LayerType { + return d.Type.LayerType() +} + +func decodeDot1Q(data []byte, p gopacket.PacketBuilder) error { + d := &Dot1Q{} + return decodingLayerDecoder(d, data, p) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *Dot1Q) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + if d.VLANIdentifier > 0xFFF { + return fmt.Errorf("vlan identifier %v is too high", d.VLANIdentifier) + } + firstBytes := uint16(d.Priority)<<13 | d.VLANIdentifier + if d.DropEligible { + firstBytes |= 0x1000 + } + binary.BigEndian.PutUint16(bytes, firstBytes) + binary.BigEndian.PutUint16(bytes[2:], uint16(d.Type)) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/dot1q_test.go b/vendor/github.com/google/gopacket/layers/dot1q_test.go new file mode 100644 index 00000000..4a409c7b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dot1q_test.go @@ -0,0 +1,62 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +package layers + +import ( + "fmt" + "reflect" + "testing" + + "github.com/google/gopacket" +) + +// test harness to ensure the dot1q layer can be encoded/decoded properly +// return error if decoded data not match. +func testEncodeDecodeDot1Q(dot1Q *Dot1Q) error { + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} + expectedDot1Q := dot1Q + + err := dot1Q.SerializeTo(buf, opts) + if err != nil { + return err + } + + newDot1q := &Dot1Q{} + err = newDot1q.DecodeFromBytes(buf.Bytes(), gopacket.NilDecodeFeedback) + if err != nil { + return err + } + newDot1q.BaseLayer = BaseLayer{} + + if !reflect.DeepEqual(expectedDot1Q, newDot1q) { + return fmt.Errorf("Expect %v actual %v", expectedDot1Q, newDot1q) + } + return nil + +} + +// Test to ensure what has been encode can be decoded +func TestEncodeDecodeDot1Q(t *testing.T) { + dot1Qs := []*Dot1Q{ + &Dot1Q{ + Priority: uint8(3), + VLANIdentifier: uint16(30), + }, + &Dot1Q{ + Priority: uint8(0x07), + DropEligible: true, + VLANIdentifier: uint16(0xFFF), + }, + } + + for i, curTest := range dot1Qs { + err := testEncodeDecodeDot1Q(curTest) + if err != nil { + t.Error("Error with item ", i, " with error message :", err) + } + } +} diff --git a/vendor/github.com/google/gopacket/layers/eap.go b/vendor/github.com/google/gopacket/layers/eap.go new file mode 100644 index 00000000..250f8571 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/eap.go @@ -0,0 +1,106 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +type EAPCode uint8 +type EAPType uint8 + +const ( + EAPCodeRequest EAPCode = 1 + EAPCodeResponse EAPCode = 2 + EAPCodeSuccess EAPCode = 3 + EAPCodeFailure EAPCode = 4 + + // EAPTypeNone means that this EAP layer has no Type or TypeData. + // Success and Failure EAPs will have this set. + EAPTypeNone EAPType = 0 + + EAPTypeIdentity EAPType = 1 + EAPTypeNotification EAPType = 2 + EAPTypeNACK EAPType = 3 + EAPTypeOTP EAPType = 4 + EAPTypeTokenCard EAPType = 5 +) + +// EAP defines an Extensible Authentication Protocol (rfc 3748) layer. +type EAP struct { + BaseLayer + Code EAPCode + Id uint8 + Length uint16 + Type EAPType + TypeData []byte +} + +// LayerType returns LayerTypeEAP. +func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP } + +// DecodeFromBytes decodes the given bytes into this layer. +func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + e.Code = EAPCode(data[0]) + e.Id = data[1] + e.Length = binary.BigEndian.Uint16(data[2:4]) + switch { + case e.Length > 4: + e.Type = EAPType(data[4]) + e.TypeData = data[5:] + case e.Length == 4: + e.Type = 0 + e.TypeData = nil + default: + return fmt.Errorf("invalid EAP length %d", e.Length) + } + e.BaseLayer.Contents = data[:e.Length] + e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if opts.FixLengths { + e.Length = uint16(len(e.TypeData) + 1) + } + size := len(e.TypeData) + 4 + if size > 4 { + size++ + } + bytes, err := b.PrependBytes(size) + if err != nil { + return err + } + bytes[0] = byte(e.Code) + bytes[1] = e.Id + binary.BigEndian.PutUint16(bytes[2:], e.Length) + if size > 4 { + bytes[4] = byte(e.Type) + copy(bytes[5:], e.TypeData) + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (e *EAP) CanDecode() gopacket.LayerClass { + return LayerTypeEAP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (e *EAP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +func decodeEAP(data []byte, p gopacket.PacketBuilder) error { + e := &EAP{} + return decodingLayerDecoder(e, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/eapol.go b/vendor/github.com/google/gopacket/layers/eapol.go new file mode 100644 index 00000000..041cd594 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/eapol.go @@ -0,0 +1,57 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// EAPOL defines an EAP over LAN (802.1x) layer. +type EAPOL struct { + BaseLayer + Version uint8 + Type EAPOLType + Length uint16 +} + +// LayerType returns LayerTypeEAPOL. +func (e *EAPOL) LayerType() gopacket.LayerType { return LayerTypeEAPOL } + +// DecodeFromBytes decodes the given bytes into this layer. +func (e *EAPOL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + e.Version = data[0] + e.Type = EAPOLType(data[1]) + e.Length = binary.BigEndian.Uint16(data[2:4]) + e.BaseLayer = BaseLayer{data[:4], data[4:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer +func (e *EAPOL) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, _ := b.PrependBytes(4) + bytes[0] = e.Version + bytes[1] = byte(e.Type) + binary.BigEndian.PutUint16(bytes[2:], e.Length) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (e *EAPOL) CanDecode() gopacket.LayerClass { + return LayerTypeEAPOL +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (e *EAPOL) NextLayerType() gopacket.LayerType { + return e.Type.LayerType() +} + +func decodeEAPOL(data []byte, p gopacket.PacketBuilder) error { + e := &EAPOL{} + return decodingLayerDecoder(e, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/endpoints.go b/vendor/github.com/google/gopacket/layers/endpoints.go new file mode 100644 index 00000000..4c91cc33 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/endpoints.go @@ -0,0 +1,97 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" + "net" + "strconv" +) + +var ( + // We use two different endpoint types for IPv4 vs IPv6 addresses, so that + // ordering with endpointA.LessThan(endpointB) sanely groups all IPv4 + // addresses and all IPv6 addresses, such that IPv6 > IPv4 for all addresses. + EndpointIPv4 = gopacket.RegisterEndpointType(1, gopacket.EndpointTypeMetadata{Name: "IPv4", Formatter: func(b []byte) string { + return net.IP(b).String() + }}) + EndpointIPv6 = gopacket.RegisterEndpointType(2, gopacket.EndpointTypeMetadata{Name: "IPv6", Formatter: func(b []byte) string { + return net.IP(b).String() + }}) + + EndpointMAC = gopacket.RegisterEndpointType(3, gopacket.EndpointTypeMetadata{Name: "MAC", Formatter: func(b []byte) string { + return net.HardwareAddr(b).String() + }}) + EndpointTCPPort = gopacket.RegisterEndpointType(4, gopacket.EndpointTypeMetadata{Name: "TCP", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointUDPPort = gopacket.RegisterEndpointType(5, gopacket.EndpointTypeMetadata{Name: "UDP", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointSCTPPort = gopacket.RegisterEndpointType(6, gopacket.EndpointTypeMetadata{Name: "SCTP", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointRUDPPort = gopacket.RegisterEndpointType(7, gopacket.EndpointTypeMetadata{Name: "RUDP", Formatter: func(b []byte) string { + return strconv.Itoa(int(b[0])) + }}) + EndpointUDPLitePort = gopacket.RegisterEndpointType(8, gopacket.EndpointTypeMetadata{Name: "UDPLite", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointPPP = gopacket.RegisterEndpointType(9, gopacket.EndpointTypeMetadata{Name: "PPP", Formatter: func([]byte) string { + return "point" + }}) +) + +// NewIPEndpoint creates a new IP (v4 or v6) endpoint from a net.IP address. +// It returns gopacket.InvalidEndpoint if the IP address is invalid. +func NewIPEndpoint(a net.IP) gopacket.Endpoint { + ipv4 := a.To4() + if ipv4 != nil { + return gopacket.NewEndpoint(EndpointIPv4, []byte(ipv4)) + } + + ipv6 := a.To16() + if ipv6 != nil { + return gopacket.NewEndpoint(EndpointIPv6, []byte(ipv6)) + } + + return gopacket.InvalidEndpoint +} + +// NewMACEndpoint returns a new MAC address endpoint. +func NewMACEndpoint(a net.HardwareAddr) gopacket.Endpoint { + return gopacket.NewEndpoint(EndpointMAC, []byte(a)) +} +func newPortEndpoint(t gopacket.EndpointType, p uint16) gopacket.Endpoint { + return gopacket.NewEndpoint(t, []byte{byte(p >> 8), byte(p)}) +} + +// NewTCPPortEndpoint returns an endpoint based on a TCP port. +func NewTCPPortEndpoint(p TCPPort) gopacket.Endpoint { + return newPortEndpoint(EndpointTCPPort, uint16(p)) +} + +// NewUDPPortEndpoint returns an endpoint based on a UDP port. +func NewUDPPortEndpoint(p UDPPort) gopacket.Endpoint { + return newPortEndpoint(EndpointUDPPort, uint16(p)) +} + +// NewSCTPPortEndpoint returns an endpoint based on a SCTP port. +func NewSCTPPortEndpoint(p SCTPPort) gopacket.Endpoint { + return newPortEndpoint(EndpointSCTPPort, uint16(p)) +} + +// NewRUDPPortEndpoint returns an endpoint based on a RUDP port. +func NewRUDPPortEndpoint(p RUDPPort) gopacket.Endpoint { + return gopacket.NewEndpoint(EndpointRUDPPort, []byte{byte(p)}) +} + +// NewUDPLitePortEndpoint returns an endpoint based on a UDPLite port. +func NewUDPLitePortEndpoint(p UDPLitePort) gopacket.Endpoint { + return newPortEndpoint(EndpointUDPLitePort, uint16(p)) +} diff --git a/vendor/github.com/google/gopacket/layers/endpoints_test.go b/vendor/github.com/google/gopacket/layers/endpoints_test.go new file mode 100644 index 00000000..906762ac --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/endpoints_test.go @@ -0,0 +1,37 @@ +// Copyright 2017, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "net" + "testing" + + "github.com/google/gopacket" +) + +func TestNewIPEndpoint(t *testing.T) { + cases := []struct { + ip net.IP + endpointType gopacket.EndpointType + }{ + {net.ParseIP("192.168.0.1").To4(), EndpointIPv4}, + {net.ParseIP("192.168.0.1").To16(), EndpointIPv4}, + {net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), EndpointIPv6}, + } + + for _, c := range cases { + endpoint := NewIPEndpoint(c.ip) + if endpoint == gopacket.InvalidEndpoint { + t.Errorf("Failed to create an IP endpoint for %s (%d-bytes)", + c.ip, len(c.ip)) + } + if endpoint.EndpointType() != c.endpointType { + t.Errorf("Wrong endpoint type created for %s (%d-bytes): expected %s, got %s", + c.ip, len(c.ip), c.endpointType, endpoint.EndpointType()) + } + } +} diff --git a/vendor/github.com/google/gopacket/layers/enums.go b/vendor/github.com/google/gopacket/layers/enums.go new file mode 100644 index 00000000..f28bcbd6 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/enums.go @@ -0,0 +1,562 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// EnumMetadata keeps track of a set of metadata for each enumeration value +// for protocol enumerations. +type EnumMetadata struct { + // DecodeWith is the decoder to use to decode this protocol's data. + DecodeWith gopacket.Decoder + // Name is the name of the enumeration value. + Name string + // LayerType is the layer type implied by the given enum. + LayerType gopacket.LayerType +} + +// errorFunc returns a decoder that spits out a specific error message. +func errorFunc(msg string) gopacket.Decoder { + var e = errors.New(msg) + return gopacket.DecodeFunc(func([]byte, gopacket.PacketBuilder) error { + return e + }) +} + +// EthernetType is an enumeration of ethernet type values, and acts as a decoder +// for any type it supports. +type EthernetType uint16 + +const ( + // EthernetTypeLLC is not an actual ethernet type. It is instead a + // placeholder we use in Ethernet frames that use the 802.3 standard of + // srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype. + EthernetTypeLLC EthernetType = 0 + EthernetTypeIPv4 EthernetType = 0x0800 + EthernetTypeARP EthernetType = 0x0806 + EthernetTypeIPv6 EthernetType = 0x86DD + EthernetTypeCiscoDiscovery EthernetType = 0x2000 + EthernetTypeNortelDiscovery EthernetType = 0x01a2 + EthernetTypeTransparentEthernetBridging EthernetType = 0x6558 + EthernetTypeDot1Q EthernetType = 0x8100 + EthernetTypePPPoEDiscovery EthernetType = 0x8863 + EthernetTypePPPoESession EthernetType = 0x8864 + EthernetTypeMPLSUnicast EthernetType = 0x8847 + EthernetTypeMPLSMulticast EthernetType = 0x8848 + EthernetTypeEAPOL EthernetType = 0x888e + EthernetTypeQinQ EthernetType = 0x88a8 + EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc + EthernetTypeEthernetCTP EthernetType = 0x9000 +) + +// IPProtocol is an enumeration of IP protocol values, and acts as a decoder +// for any type it supports. +type IPProtocol uint8 + +const ( + IPProtocolIPv6HopByHop IPProtocol = 0 + IPProtocolICMPv4 IPProtocol = 1 + IPProtocolIGMP IPProtocol = 2 + IPProtocolIPv4 IPProtocol = 4 + IPProtocolTCP IPProtocol = 6 + IPProtocolUDP IPProtocol = 17 + IPProtocolRUDP IPProtocol = 27 + IPProtocolIPv6 IPProtocol = 41 + IPProtocolIPv6Routing IPProtocol = 43 + IPProtocolIPv6Fragment IPProtocol = 44 + IPProtocolGRE IPProtocol = 47 + IPProtocolESP IPProtocol = 50 + IPProtocolAH IPProtocol = 51 + IPProtocolICMPv6 IPProtocol = 58 + IPProtocolNoNextHeader IPProtocol = 59 + IPProtocolIPv6Destination IPProtocol = 60 + IPProtocolIPIP IPProtocol = 94 + IPProtocolEtherIP IPProtocol = 97 + IPProtocolVRRP IPProtocol = 112 + IPProtocolSCTP IPProtocol = 132 + IPProtocolUDPLite IPProtocol = 136 + IPProtocolMPLSInIP IPProtocol = 137 +) + +// LinkType is an enumeration of link types, and acts as a decoder for any +// link type it supports. +type LinkType uint8 + +const ( + // According to pcap-linktype(7) and http://www.tcpdump.org/linktypes.html + LinkTypeNull LinkType = 0 + LinkTypeEthernet LinkType = 1 + LinkTypeAX25 LinkType = 3 + LinkTypeTokenRing LinkType = 6 + LinkTypeArcNet LinkType = 7 + LinkTypeSLIP LinkType = 8 + LinkTypePPP LinkType = 9 + LinkTypeFDDI LinkType = 10 + LinkTypePPP_HDLC LinkType = 50 + LinkTypePPPEthernet LinkType = 51 + LinkTypeATM_RFC1483 LinkType = 100 + LinkTypeRaw LinkType = 101 + LinkTypeC_HDLC LinkType = 104 + LinkTypeIEEE802_11 LinkType = 105 + LinkTypeFRelay LinkType = 107 + LinkTypeLoop LinkType = 108 + LinkTypeLinuxSLL LinkType = 113 + LinkTypeLTalk LinkType = 114 + LinkTypePFLog LinkType = 117 + LinkTypePrismHeader LinkType = 119 + LinkTypeIPOverFC LinkType = 122 + LinkTypeSunATM LinkType = 123 + LinkTypeIEEE80211Radio LinkType = 127 + LinkTypeARCNetLinux LinkType = 129 + LinkTypeIPOver1394 LinkType = 138 + LinkTypeMTP2Phdr LinkType = 139 + LinkTypeMTP2 LinkType = 140 + LinkTypeMTP3 LinkType = 141 + LinkTypeSCCP LinkType = 142 + LinkTypeDOCSIS LinkType = 143 + LinkTypeLinuxIRDA LinkType = 144 + LinkTypeLinuxLAPD LinkType = 177 + LinkTypeLinuxUSB LinkType = 220 + LinkTypeIPv4 LinkType = 228 + LinkTypeIPv6 LinkType = 229 +) + +// PPPoECode is the PPPoE code enum, taken from http://tools.ietf.org/html/rfc2516 +type PPPoECode uint8 + +const ( + PPPoECodePADI PPPoECode = 0x09 + PPPoECodePADO PPPoECode = 0x07 + PPPoECodePADR PPPoECode = 0x19 + PPPoECodePADS PPPoECode = 0x65 + PPPoECodePADT PPPoECode = 0xA7 + PPPoECodeSession PPPoECode = 0x00 +) + +// PPPType is an enumeration of PPP type values, and acts as a decoder for any +// type it supports. +type PPPType uint16 + +const ( + PPPTypeIPv4 PPPType = 0x0021 + PPPTypeIPv6 PPPType = 0x0057 + PPPTypeMPLSUnicast PPPType = 0x0281 + PPPTypeMPLSMulticast PPPType = 0x0283 +) + +// SCTPChunkType is an enumeration of chunk types inside SCTP packets. +type SCTPChunkType uint8 + +const ( + SCTPChunkTypeData SCTPChunkType = 0 + SCTPChunkTypeInit SCTPChunkType = 1 + SCTPChunkTypeInitAck SCTPChunkType = 2 + SCTPChunkTypeSack SCTPChunkType = 3 + SCTPChunkTypeHeartbeat SCTPChunkType = 4 + SCTPChunkTypeHeartbeatAck SCTPChunkType = 5 + SCTPChunkTypeAbort SCTPChunkType = 6 + SCTPChunkTypeShutdown SCTPChunkType = 7 + SCTPChunkTypeShutdownAck SCTPChunkType = 8 + SCTPChunkTypeError SCTPChunkType = 9 + SCTPChunkTypeCookieEcho SCTPChunkType = 10 + SCTPChunkTypeCookieAck SCTPChunkType = 11 + SCTPChunkTypeShutdownComplete SCTPChunkType = 14 +) + +// FDDIFrameControl is an enumeration of FDDI frame control bytes. +type FDDIFrameControl uint8 + +const ( + FDDIFrameControlLLC FDDIFrameControl = 0x50 +) + +// EAPOLType is an enumeration of EAPOL packet types. +type EAPOLType uint8 + +const ( + EAPOLTypeEAP EAPOLType = 0 + EAPOLTypeStart EAPOLType = 1 + EAPOLTypeLogOff EAPOLType = 2 + EAPOLTypeKey EAPOLType = 3 + EAPOLTypeASFAlert EAPOLType = 4 +) + +// ProtocolFamily is the set of values defined as PF_* in sys/socket.h +type ProtocolFamily uint8 + +const ( + ProtocolFamilyIPv4 ProtocolFamily = 2 + // BSDs use different values for INET6... glory be. These values taken from + // tcpdump 4.3.0. + ProtocolFamilyIPv6BSD ProtocolFamily = 24 + ProtocolFamilyIPv6FreeBSD ProtocolFamily = 28 + ProtocolFamilyIPv6Darwin ProtocolFamily = 30 + ProtocolFamilyIPv6Linux ProtocolFamily = 10 +) + +// Dot11Type is a combination of IEEE 802.11 frame's Type and Subtype fields. +// By combining these two fields together into a single type, we're able to +// provide a String function that correctly displays the subtype given the +// top-level type. +// +// If you just care about the top-level type, use the MainType function. +type Dot11Type uint8 + +// MainType strips the subtype information from the given type, +// returning just the overarching type (Mgmt, Ctrl, Data, Reserved). +func (d Dot11Type) MainType() Dot11Type { + return d & dot11TypeMask +} + +const ( + Dot11TypeMgmt Dot11Type = 0x00 + Dot11TypeCtrl Dot11Type = 0x01 + Dot11TypeData Dot11Type = 0x02 + Dot11TypeReserved Dot11Type = 0x03 + dot11TypeMask = 0x03 + + // The following are type/subtype conglomerations. + + // Management + Dot11TypeMgmtAssociationReq Dot11Type = 0x00 + Dot11TypeMgmtAssociationResp Dot11Type = 0x04 + Dot11TypeMgmtReassociationReq Dot11Type = 0x08 + Dot11TypeMgmtReassociationResp Dot11Type = 0x0c + Dot11TypeMgmtProbeReq Dot11Type = 0x10 + Dot11TypeMgmtProbeResp Dot11Type = 0x14 + Dot11TypeMgmtMeasurementPilot Dot11Type = 0x18 + Dot11TypeMgmtBeacon Dot11Type = 0x20 + Dot11TypeMgmtATIM Dot11Type = 0x24 + Dot11TypeMgmtDisassociation Dot11Type = 0x28 + Dot11TypeMgmtAuthentication Dot11Type = 0x2c + Dot11TypeMgmtDeauthentication Dot11Type = 0x30 + Dot11TypeMgmtAction Dot11Type = 0x34 + Dot11TypeMgmtActionNoAck Dot11Type = 0x38 + + // Control + Dot11TypeCtrlWrapper Dot11Type = 0x1d + Dot11TypeCtrlBlockAckReq Dot11Type = 0x21 + Dot11TypeCtrlBlockAck Dot11Type = 0x25 + Dot11TypeCtrlPowersavePoll Dot11Type = 0x29 + Dot11TypeCtrlRTS Dot11Type = 0x2d + Dot11TypeCtrlCTS Dot11Type = 0x31 + Dot11TypeCtrlAck Dot11Type = 0x35 + Dot11TypeCtrlCFEnd Dot11Type = 0x39 + Dot11TypeCtrlCFEndAck Dot11Type = 0x3d + + // Data + Dot11TypeDataCFAck Dot11Type = 0x06 + Dot11TypeDataCFPoll Dot11Type = 0x0a + Dot11TypeDataCFAckPoll Dot11Type = 0x0e + Dot11TypeDataNull Dot11Type = 0x12 + Dot11TypeDataCFAckNoData Dot11Type = 0x16 + Dot11TypeDataCFPollNoData Dot11Type = 0x1a + Dot11TypeDataCFAckPollNoData Dot11Type = 0x1e + Dot11TypeDataQOSData Dot11Type = 0x22 + Dot11TypeDataQOSDataCFAck Dot11Type = 0x26 + Dot11TypeDataQOSDataCFPoll Dot11Type = 0x2a + Dot11TypeDataQOSDataCFAckPoll Dot11Type = 0x2e + Dot11TypeDataQOSNull Dot11Type = 0x32 + Dot11TypeDataQOSCFPollNoData Dot11Type = 0x3a + Dot11TypeDataQOSCFAckPollNoData Dot11Type = 0x3e +) + +var ( + // Each of the following arrays contains mappings of how to handle enum + // values for various enum types in gopacket/layers. + // + // So, EthernetTypeMetadata[2] contains information on how to handle EthernetType + // 2, including which name to give it and which decoder to use to decode + // packet data of that type. These arrays are filled by default with all of the + // protocols gopacket/layers knows how to handle, but users of the library can + // add new decoders or override existing ones. For example, if you write a better + // TCP decoder, you can override IPProtocolMetadata[IPProtocolTCP].DecodeWith + // with your new decoder, and all gopacket/layers decoding will use your new + // decoder whenever they encounter that IPProtocol. + EthernetTypeMetadata [65536]EnumMetadata + IPProtocolMetadata [256]EnumMetadata + SCTPChunkTypeMetadata [256]EnumMetadata + PPPTypeMetadata [65536]EnumMetadata + PPPoECodeMetadata [256]EnumMetadata + LinkTypeMetadata [256]EnumMetadata + FDDIFrameControlMetadata [256]EnumMetadata + EAPOLTypeMetadata [256]EnumMetadata + ProtocolFamilyMetadata [256]EnumMetadata + Dot11TypeMetadata [256]EnumMetadata + USBTypeMetadata [256]EnumMetadata +) + +func (a EthernetType) Decode(data []byte, p gopacket.PacketBuilder) error { + return EthernetTypeMetadata[a].DecodeWith.Decode(data, p) +} +func (a EthernetType) String() string { + return EthernetTypeMetadata[a].Name +} +func (a EthernetType) LayerType() gopacket.LayerType { + return EthernetTypeMetadata[a].LayerType +} +func (a IPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error { + return IPProtocolMetadata[a].DecodeWith.Decode(data, p) +} +func (a IPProtocol) String() string { + return IPProtocolMetadata[a].Name +} +func (a IPProtocol) LayerType() gopacket.LayerType { + return IPProtocolMetadata[a].LayerType +} +func (a SCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error { + return SCTPChunkTypeMetadata[a].DecodeWith.Decode(data, p) +} +func (a SCTPChunkType) String() string { + return SCTPChunkTypeMetadata[a].Name +} +func (a PPPType) Decode(data []byte, p gopacket.PacketBuilder) error { + return PPPTypeMetadata[a].DecodeWith.Decode(data, p) +} +func (a PPPType) String() string { + return PPPTypeMetadata[a].Name +} +func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error { + return LinkTypeMetadata[a].DecodeWith.Decode(data, p) +} +func (a LinkType) String() string { + return LinkTypeMetadata[a].Name +} +func (a PPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error { + return PPPoECodeMetadata[a].DecodeWith.Decode(data, p) +} +func (a PPPoECode) String() string { + return PPPoECodeMetadata[a].Name +} +func (a FDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error { + return FDDIFrameControlMetadata[a].DecodeWith.Decode(data, p) +} +func (a FDDIFrameControl) String() string { + return FDDIFrameControlMetadata[a].Name +} +func (a EAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error { + return EAPOLTypeMetadata[a].DecodeWith.Decode(data, p) +} +func (a EAPOLType) String() string { + return EAPOLTypeMetadata[a].Name +} +func (a EAPOLType) LayerType() gopacket.LayerType { + return EAPOLTypeMetadata[a].LayerType +} +func (a ProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error { + return ProtocolFamilyMetadata[a].DecodeWith.Decode(data, p) +} +func (a ProtocolFamily) String() string { + return ProtocolFamilyMetadata[a].Name +} +func (a ProtocolFamily) LayerType() gopacket.LayerType { + return ProtocolFamilyMetadata[a].LayerType +} +func (a Dot11Type) Decode(data []byte, p gopacket.PacketBuilder) error { + return Dot11TypeMetadata[a].DecodeWith.Decode(data, p) +} +func (a Dot11Type) String() string { + return Dot11TypeMetadata[a].Name +} +func (a Dot11Type) LayerType() gopacket.LayerType { + return Dot11TypeMetadata[a].LayerType +} + +// Decode a raw v4 or v6 IP packet. +func decodeIPv4or6(data []byte, p gopacket.PacketBuilder) error { + version := data[0] >> 4 + switch version { + case 4: + return decodeIPv4(data, p) + case 6: + return decodeIPv6(data, p) + } + return fmt.Errorf("Invalid IP packet version %v", version) +} + +func init() { + // Here we link up all enumerations with their respective names and decoders. + for i := 0; i < 65536; i++ { + EthernetTypeMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode ethernet type %d", i)), + Name: fmt.Sprintf("UnknownEthernetType(%d)", i), + } + PPPTypeMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode PPP type %d", i)), + Name: fmt.Sprintf("UnknownPPPType(%d)", i), + } + } + for i := 0; i < 256; i++ { + IPProtocolMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode IP protocol %d", i)), + Name: fmt.Sprintf("UnknownIPProtocol(%d)", i), + } + SCTPChunkTypeMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode SCTP chunk type %d", i)), + Name: fmt.Sprintf("UnknownSCTPChunkType(%d)", i), + } + PPPoECodeMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode PPPoE code %d", i)), + Name: fmt.Sprintf("UnknownPPPoECode(%d)", i), + } + LinkTypeMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode link type %d", i)), + Name: fmt.Sprintf("UnknownLinkType(%d)", i), + } + FDDIFrameControlMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode FDDI frame control %d", i)), + Name: fmt.Sprintf("UnknownFDDIFrameControl(%d)", i), + } + EAPOLTypeMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode EAPOL type %d", i)), + Name: fmt.Sprintf("UnknownEAPOLType(%d)", i), + } + ProtocolFamilyMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode protocol family %d", i)), + Name: fmt.Sprintf("UnknownProtocolFamily(%d)", i), + } + Dot11TypeMetadata[i] = EnumMetadata{ + DecodeWith: errorFunc(fmt.Sprintf("Unable to decode Dot11 type %d", i)), + Name: fmt.Sprintf("UnknownDot11Type(%d)", i), + } + } + + EthernetTypeMetadata[EthernetTypeLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC", LayerType: LayerTypeLLC} + EthernetTypeMetadata[EthernetTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + EthernetTypeMetadata[EthernetTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + EthernetTypeMetadata[EthernetTypeARP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeARP), Name: "ARP", LayerType: LayerTypeARP} + EthernetTypeMetadata[EthernetTypeDot1Q] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q} + EthernetTypeMetadata[EthernetTypePPPoEDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoEDiscovery", LayerType: LayerTypePPPoE} + EthernetTypeMetadata[EthernetTypePPPoESession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoESession", LayerType: LayerTypePPPoE} + EthernetTypeMetadata[EthernetTypeEthernetCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernetCTP), Name: "EthernetCTP", LayerType: LayerTypeEthernetCTP} + EthernetTypeMetadata[EthernetTypeCiscoDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeCiscoDiscovery), Name: "CiscoDiscovery", LayerType: LayerTypeCiscoDiscovery} + EthernetTypeMetadata[EthernetTypeNortelDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeNortelDiscovery), Name: "NortelDiscovery", LayerType: LayerTypeNortelDiscovery} + EthernetTypeMetadata[EthernetTypeLinkLayerDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinkLayerDiscovery), Name: "LinkLayerDiscovery", LayerType: LayerTypeLinkLayerDiscovery} + EthernetTypeMetadata[EthernetTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast", LayerType: LayerTypeMPLS} + EthernetTypeMetadata[EthernetTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast", LayerType: LayerTypeMPLS} + EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL} + EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q} + EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet} + + IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP} + IPProtocolMetadata[IPProtocolUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDP), Name: "UDP", LayerType: LayerTypeUDP} + IPProtocolMetadata[IPProtocolICMPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv4), Name: "ICMPv4", LayerType: LayerTypeICMPv4} + IPProtocolMetadata[IPProtocolICMPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv6), Name: "ICMPv6", LayerType: LayerTypeICMPv6} + IPProtocolMetadata[IPProtocolSCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTP), Name: "SCTP", LayerType: LayerTypeSCTP} + IPProtocolMetadata[IPProtocolIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + IPProtocolMetadata[IPProtocolIPIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + IPProtocolMetadata[IPProtocolEtherIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEtherIP), Name: "EtherIP", LayerType: LayerTypeEtherIP} + IPProtocolMetadata[IPProtocolRUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRUDP), Name: "RUDP", LayerType: LayerTypeRUDP} + IPProtocolMetadata[IPProtocolGRE] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeGRE), Name: "GRE", LayerType: LayerTypeGRE} + IPProtocolMetadata[IPProtocolIPv6HopByHop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6HopByHop), Name: "IPv6HopByHop", LayerType: LayerTypeIPv6HopByHop} + IPProtocolMetadata[IPProtocolIPv6Routing] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Routing), Name: "IPv6Routing", LayerType: LayerTypeIPv6Routing} + IPProtocolMetadata[IPProtocolIPv6Fragment] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Fragment), Name: "IPv6Fragment", LayerType: LayerTypeIPv6Fragment} + IPProtocolMetadata[IPProtocolIPv6Destination] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Destination), Name: "IPv6Destination", LayerType: LayerTypeIPv6Destination} + IPProtocolMetadata[IPProtocolAH] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecAH), Name: "IPSecAH", LayerType: LayerTypeIPSecAH} + IPProtocolMetadata[IPProtocolESP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecESP), Name: "IPSecESP", LayerType: LayerTypeIPSecESP} + IPProtocolMetadata[IPProtocolUDPLite] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDPLite), Name: "UDPLite", LayerType: LayerTypeUDPLite} + IPProtocolMetadata[IPProtocolMPLSInIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLS", LayerType: LayerTypeMPLS} + IPProtocolMetadata[IPProtocolNoNextHeader] = EnumMetadata{DecodeWith: gopacket.DecodePayload, Name: "NoNextHeader", LayerType: gopacket.LayerTypePayload} + IPProtocolMetadata[IPProtocolIGMP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIGMP), Name: "IGMP", LayerType: LayerTypeIGMP} + IPProtocolMetadata[IPProtocolVRRP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeVRRP), Name: "VRRP", LayerType: LayerTypeVRRP} + + SCTPChunkTypeMetadata[SCTPChunkTypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPData), Name: "Data"} + SCTPChunkTypeMetadata[SCTPChunkTypeInit] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "Init"} + SCTPChunkTypeMetadata[SCTPChunkTypeInitAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "InitAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeSack] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPSack), Name: "Sack"} + SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeat] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "Heartbeat"} + SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeatAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "HeartbeatAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeAbort] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Abort"} + SCTPChunkTypeMetadata[SCTPChunkTypeError] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Error"} + SCTPChunkTypeMetadata[SCTPChunkTypeShutdown] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdown), Name: "Shutdown"} + SCTPChunkTypeMetadata[SCTPChunkTypeShutdownAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdownAck), Name: "ShutdownAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeCookieEcho] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPCookieEcho), Name: "CookieEcho"} + SCTPChunkTypeMetadata[SCTPChunkTypeCookieAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "CookieAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeShutdownComplete] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "ShutdownComplete"} + + PPPTypeMetadata[PPPTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4"} + PPPTypeMetadata[PPPTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6"} + PPPTypeMetadata[PPPTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast"} + PPPTypeMetadata[PPPTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast"} + + PPPoECodeMetadata[PPPoECodeSession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"} + + LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"} + LinkTypeMetadata[LinkTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"} + LinkTypeMetadata[LinkTypeFDDI] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeFDDI), Name: "FDDI"} + LinkTypeMetadata[LinkTypeNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Null"} + LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "Dot11"} + LinkTypeMetadata[LinkTypeLoop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Loop"} + LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "802.11"} + LinkTypeMetadata[LinkTypeRaw] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"} + LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"} + LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"} + LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"} + LinkTypeMetadata[LinkTypeLinuxSLL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinuxSLL), Name: "Linux SLL"} + LinkTypeMetadata[LinkTypePrismHeader] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePrismHeader), Name: "Prism"} + + FDDIFrameControlMetadata[FDDIFrameControlLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC"} + + EAPOLTypeMetadata[EAPOLTypeEAP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAP), Name: "EAP", LayerType: LayerTypeEAP} + + ProtocolFamilyMetadata[ProtocolFamilyIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + ProtocolFamilyMetadata[ProtocolFamilyIPv6BSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + ProtocolFamilyMetadata[ProtocolFamilyIPv6FreeBSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + ProtocolFamilyMetadata[ProtocolFamilyIPv6Darwin] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + ProtocolFamilyMetadata[ProtocolFamilyIPv6Linux] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + + Dot11TypeMetadata[Dot11TypeMgmtAssociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq), Name: "MgmtAssociationReq", LayerType: LayerTypeDot11MgmtAssociationReq} + Dot11TypeMetadata[Dot11TypeMgmtAssociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp), Name: "MgmtAssociationResp", LayerType: LayerTypeDot11MgmtAssociationResp} + Dot11TypeMetadata[Dot11TypeMgmtReassociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq), Name: "MgmtReassociationReq", LayerType: LayerTypeDot11MgmtReassociationReq} + Dot11TypeMetadata[Dot11TypeMgmtReassociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp), Name: "MgmtReassociationResp", LayerType: LayerTypeDot11MgmtReassociationResp} + Dot11TypeMetadata[Dot11TypeMgmtProbeReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeReq), Name: "MgmtProbeReq", LayerType: LayerTypeDot11MgmtProbeReq} + Dot11TypeMetadata[Dot11TypeMgmtProbeResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeResp), Name: "MgmtProbeResp", LayerType: LayerTypeDot11MgmtProbeResp} + Dot11TypeMetadata[Dot11TypeMgmtMeasurementPilot] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot), Name: "MgmtMeasurementPilot", LayerType: LayerTypeDot11MgmtMeasurementPilot} + Dot11TypeMetadata[Dot11TypeMgmtBeacon] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtBeacon), Name: "MgmtBeacon", LayerType: LayerTypeDot11MgmtBeacon} + Dot11TypeMetadata[Dot11TypeMgmtATIM] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtATIM), Name: "MgmtATIM", LayerType: LayerTypeDot11MgmtATIM} + Dot11TypeMetadata[Dot11TypeMgmtDisassociation] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDisassociation), Name: "MgmtDisassociation", LayerType: LayerTypeDot11MgmtDisassociation} + Dot11TypeMetadata[Dot11TypeMgmtAuthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAuthentication), Name: "MgmtAuthentication", LayerType: LayerTypeDot11MgmtAuthentication} + Dot11TypeMetadata[Dot11TypeMgmtDeauthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication), Name: "MgmtDeauthentication", LayerType: LayerTypeDot11MgmtDeauthentication} + Dot11TypeMetadata[Dot11TypeMgmtAction] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAction), Name: "MgmtAction", LayerType: LayerTypeDot11MgmtAction} + Dot11TypeMetadata[Dot11TypeMgmtActionNoAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck), Name: "MgmtActionNoAck", LayerType: LayerTypeDot11MgmtActionNoAck} + Dot11TypeMetadata[Dot11TypeCtrl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "Ctrl", LayerType: LayerTypeDot11Ctrl} + Dot11TypeMetadata[Dot11TypeCtrlWrapper] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "CtrlWrapper", LayerType: LayerTypeDot11Ctrl} + Dot11TypeMetadata[Dot11TypeCtrlBlockAckReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq), Name: "CtrlBlockAckReq", LayerType: LayerTypeDot11CtrlBlockAckReq} + Dot11TypeMetadata[Dot11TypeCtrlBlockAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAck), Name: "CtrlBlockAck", LayerType: LayerTypeDot11CtrlBlockAck} + Dot11TypeMetadata[Dot11TypeCtrlPowersavePoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll), Name: "CtrlPowersavePoll", LayerType: LayerTypeDot11CtrlPowersavePoll} + Dot11TypeMetadata[Dot11TypeCtrlRTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlRTS), Name: "CtrlRTS", LayerType: LayerTypeDot11CtrlRTS} + Dot11TypeMetadata[Dot11TypeCtrlCTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCTS), Name: "CtrlCTS", LayerType: LayerTypeDot11CtrlCTS} + Dot11TypeMetadata[Dot11TypeCtrlAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlAck), Name: "CtrlAck", LayerType: LayerTypeDot11CtrlAck} + Dot11TypeMetadata[Dot11TypeCtrlCFEnd] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEnd), Name: "CtrlCFEnd", LayerType: LayerTypeDot11CtrlCFEnd} + Dot11TypeMetadata[Dot11TypeCtrlCFEndAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck), Name: "CtrlCFEndAck", LayerType: LayerTypeDot11CtrlCFEndAck} + Dot11TypeMetadata[Dot11TypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Data), Name: "Data", LayerType: LayerTypeDot11Data} + Dot11TypeMetadata[Dot11TypeDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAck), Name: "DataCFAck", LayerType: LayerTypeDot11DataCFAck} + Dot11TypeMetadata[Dot11TypeDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPoll), Name: "DataCFPoll", LayerType: LayerTypeDot11DataCFPoll} + Dot11TypeMetadata[Dot11TypeDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPoll), Name: "DataCFAckPoll", LayerType: LayerTypeDot11DataCFAckPoll} + Dot11TypeMetadata[Dot11TypeDataNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataNull), Name: "DataNull", LayerType: LayerTypeDot11DataNull} + Dot11TypeMetadata[Dot11TypeDataCFAckNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckNoData), Name: "DataCFAckNoData", LayerType: LayerTypeDot11DataCFAckNoData} + Dot11TypeMetadata[Dot11TypeDataCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPollNoData), Name: "DataCFPollNoData", LayerType: LayerTypeDot11DataCFPollNoData} + Dot11TypeMetadata[Dot11TypeDataCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPollNoData), Name: "DataCFAckPollNoData", LayerType: LayerTypeDot11DataCFAckPollNoData} + Dot11TypeMetadata[Dot11TypeDataQOSData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSData), Name: "DataQOSData", LayerType: LayerTypeDot11DataQOSData} + Dot11TypeMetadata[Dot11TypeDataQOSDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck), Name: "DataQOSDataCFAck", LayerType: LayerTypeDot11DataQOSDataCFAck} + Dot11TypeMetadata[Dot11TypeDataQOSDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll), Name: "DataQOSDataCFPoll", LayerType: LayerTypeDot11DataQOSDataCFPoll} + Dot11TypeMetadata[Dot11TypeDataQOSDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll), Name: "DataQOSDataCFAckPoll", LayerType: LayerTypeDot11DataQOSDataCFAckPoll} + Dot11TypeMetadata[Dot11TypeDataQOSNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSNull), Name: "DataQOSNull", LayerType: LayerTypeDot11DataQOSNull} + Dot11TypeMetadata[Dot11TypeDataQOSCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData), Name: "DataQOSCFPollNoData", LayerType: LayerTypeDot11DataQOSCFPollNoData} + Dot11TypeMetadata[Dot11TypeDataQOSCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData), Name: "DataQOSCFAckPollNoData", LayerType: LayerTypeDot11DataQOSCFAckPollNoData} + + USBTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt} + USBTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl} + USBTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk} +} diff --git a/vendor/github.com/google/gopacket/layers/etherip.go b/vendor/github.com/google/gopacket/layers/etherip.go new file mode 100644 index 00000000..5b7b7229 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/etherip.go @@ -0,0 +1,45 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// EtherIP is the struct for storing RFC 3378 EtherIP packet headers. +type EtherIP struct { + BaseLayer + Version uint8 + Reserved uint16 +} + +// LayerType returns gopacket.LayerTypeEtherIP. +func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP } + +// DecodeFromBytes decodes the given bytes into this layer. +func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + e.Version = data[0] >> 4 + e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff + e.BaseLayer = BaseLayer{data[:2], data[2:]} + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (e *EtherIP) CanDecode() gopacket.LayerClass { + return LayerTypeEtherIP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (e *EtherIP) NextLayerType() gopacket.LayerType { + return LayerTypeEthernet +} + +func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error { + e := &EtherIP{} + return decodingLayerDecoder(e, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/ethernet.go b/vendor/github.com/google/gopacket/layers/ethernet.go new file mode 100644 index 00000000..4eebf8c6 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ethernet.go @@ -0,0 +1,122 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "github.com/google/gopacket" + "net" +) + +// EthernetBroadcast is the broadcast MAC address used by Ethernet. +var EthernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + +// Ethernet is the layer for Ethernet frame headers. +type Ethernet struct { + BaseLayer + SrcMAC, DstMAC net.HardwareAddr + EthernetType EthernetType + // Length is only set if a length field exists within this header. Ethernet + // headers follow two different standards, one that uses an EthernetType, the + // other which defines a length the follows with a LLC header (802.3). If the + // former is the case, we set EthernetType and Length stays 0. In the latter + // case, we set Length and EthernetType = EthernetTypeLLC. + Length uint16 +} + +// LayerType returns LayerTypeEthernet +func (e *Ethernet) LayerType() gopacket.LayerType { return LayerTypeEthernet } + +func (e *Ethernet) LinkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointMAC, e.SrcMAC, e.DstMAC) +} + +func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 14 { + return errors.New("Ethernet packet too small") + } + eth.DstMAC = net.HardwareAddr(data[0:6]) + eth.SrcMAC = net.HardwareAddr(data[6:12]) + eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14])) + eth.BaseLayer = BaseLayer{data[:14], data[14:]} + if eth.EthernetType < 0x0600 { + eth.Length = uint16(eth.EthernetType) + eth.EthernetType = EthernetTypeLLC + if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 { + df.SetTruncated() + } else if cmp > 0 { + // Strip off bytes at the end, since we have too many bytes + eth.Payload = eth.Payload[:len(eth.Payload)-cmp] + } + // fmt.Println(eth) + } + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (eth *Ethernet) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if len(eth.DstMAC) != 6 { + return fmt.Errorf("invalid dst MAC: %v", eth.DstMAC) + } + if len(eth.SrcMAC) != 6 { + return fmt.Errorf("invalid src MAC: %v", eth.SrcMAC) + } + payload := b.Bytes() + bytes, err := b.PrependBytes(14) + if err != nil { + return err + } + copy(bytes, eth.DstMAC) + copy(bytes[6:], eth.SrcMAC) + if eth.Length != 0 || eth.EthernetType == EthernetTypeLLC { + if opts.FixLengths { + eth.Length = uint16(len(payload)) + } + if eth.EthernetType != EthernetTypeLLC { + return fmt.Errorf("ethernet type %v not compatible with length value %v", eth.EthernetType, eth.Length) + } else if eth.Length > 0x0600 { + return fmt.Errorf("invalid ethernet length %v", eth.Length) + } + binary.BigEndian.PutUint16(bytes[12:], eth.Length) + } else { + binary.BigEndian.PutUint16(bytes[12:], uint16(eth.EthernetType)) + } + length := len(b.Bytes()) + if length < 60 { + // Pad out to 60 bytes. + padding, err := b.AppendBytes(60 - length) + if err != nil { + return err + } + copy(padding, lotsOfZeros[:]) + } + return nil +} + +func (eth *Ethernet) CanDecode() gopacket.LayerClass { + return LayerTypeEthernet +} + +func (eth *Ethernet) NextLayerType() gopacket.LayerType { + return eth.EthernetType.LayerType() +} + +func decodeEthernet(data []byte, p gopacket.PacketBuilder) error { + eth := &Ethernet{} + err := eth.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(eth) + p.SetLinkLayer(eth) + return p.NextDecoder(eth.EthernetType) +} diff --git a/vendor/github.com/google/gopacket/layers/fddi.go b/vendor/github.com/google/gopacket/layers/fddi.go new file mode 100644 index 00000000..ed9e1957 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/fddi.go @@ -0,0 +1,41 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" + "net" +) + +// FDDI contains the header for FDDI frames. +type FDDI struct { + BaseLayer + FrameControl FDDIFrameControl + Priority uint8 + SrcMAC, DstMAC net.HardwareAddr +} + +// LayerType returns LayerTypeFDDI. +func (f *FDDI) LayerType() gopacket.LayerType { return LayerTypeFDDI } + +// LinkFlow returns a new flow of type EndpointMAC. +func (f *FDDI) LinkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointMAC, f.SrcMAC, f.DstMAC) +} + +func decodeFDDI(data []byte, p gopacket.PacketBuilder) error { + f := &FDDI{ + FrameControl: FDDIFrameControl(data[0] & 0xF8), + Priority: data[0] & 0x07, + SrcMAC: net.HardwareAddr(data[1:7]), + DstMAC: net.HardwareAddr(data[7:13]), + BaseLayer: BaseLayer{data[:13], data[13:]}, + } + p.SetLinkLayer(f) + p.AddLayer(f) + return p.NextDecoder(f.FrameControl) +} diff --git a/vendor/github.com/google/gopacket/layers/gen.go b/vendor/github.com/google/gopacket/layers/gen.go new file mode 100644 index 00000000..ab7a0c00 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/gen.go @@ -0,0 +1,109 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build ignore + +// This binary pulls known ports from IANA, and uses them to populate +// iana_ports.go's TCPPortNames and UDPPortNames maps. +// +// go run gen.go | gofmt > iana_ports.go +package main + +import ( + "bytes" + "encoding/xml" + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "strconv" + "time" +) + +const fmtString = `// Copyright 2012 Google, Inc. All rights reserved. + +package layers + +// Created by gen.go, don't edit manually +// Generated at %s +// Fetched from %q + +// TCPPortNames contains the port names for all TCP ports. +var TCPPortNames = tcpPortNames + +// UDPPortNames contains the port names for all UDP ports. +var UDPPortNames = udpPortNames + +// SCTPPortNames contains the port names for all SCTP ports. +var SCTPPortNames = sctpPortNames + +var tcpPortNames = map[TCPPort]string{ +%s} +var udpPortNames = map[UDPPort]string{ +%s} +var sctpPortNames = map[SCTPPort]string{ +%s} +` + +var url = flag.String("url", "http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml", "URL to grab port numbers from") + +func main() { + fmt.Fprintf(os.Stderr, "Fetching ports from %q\n", *url) + resp, err := http.Get(*url) + if err != nil { + panic(err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err) + } + fmt.Fprintln(os.Stderr, "Parsing XML") + var registry struct { + Records []struct { + Protocol string `xml:"protocol"` + Number string `xml:"number"` + Name string `xml:"name"` + } `xml:"record"` + } + xml.Unmarshal(body, ®istry) + var tcpPorts bytes.Buffer + var udpPorts bytes.Buffer + var sctpPorts bytes.Buffer + done := map[string]map[int]bool{ + "tcp": map[int]bool{}, + "udp": map[int]bool{}, + "sctp": map[int]bool{}, + } + for _, r := range registry.Records { + port, err := strconv.Atoi(r.Number) + if err != nil { + continue + } + if r.Name == "" { + continue + } + var b *bytes.Buffer + switch r.Protocol { + case "tcp": + b = &tcpPorts + case "udp": + b = &udpPorts + case "sctp": + b = &sctpPorts + default: + continue + } + if done[r.Protocol][port] { + continue + } + done[r.Protocol][port] = true + fmt.Fprintf(b, "\t%d: %q,\n", port, r.Name) + } + fmt.Fprintln(os.Stderr, "Writing results to stdout") + fmt.Printf(fmtString, time.Now(), *url, tcpPorts.String(), udpPorts.String(), sctpPorts.String()) +} diff --git a/vendor/github.com/google/gopacket/layers/gen_linted.sh b/vendor/github.com/google/gopacket/layers/gen_linted.sh new file mode 100644 index 00000000..75c701f4 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/gen_linted.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +for i in *.go; do golint $i | grep -q . || echo $i; done > .linted diff --git a/vendor/github.com/google/gopacket/layers/geneve.go b/vendor/github.com/google/gopacket/layers/geneve.go new file mode 100644 index 00000000..6dc05cf3 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/geneve.go @@ -0,0 +1,98 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + + "github.com/google/gopacket" +) + +// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03 +// Geneve Header: +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Ver| Opt Len |O|C| Rsvd. | Protocol Type | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Virtual Network Identifier (VNI) | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Variable Length Options | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type Geneve struct { + BaseLayer + Version uint8 // 2 bits + OptionsLength uint8 // 6 bits + OAMPacket bool // 1 bits + CriticalOption bool // 1 bits + Protocol EthernetType // 16 bits + VNI uint32 // 24bits + Options []*GeneveOption +} + +// Geneve Tunnel Options +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Option Class | Type |R|R|R| Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Variable Option Data | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type GeneveOption struct { + Class uint16 // 16 bits + Type uint8 // 8 bits + Flags uint8 // 3 bits + Length uint8 // 5 bits + Data []byte +} + +// LayerType returns LayerTypeGeneve +func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve } + +func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) { + opt := &GeneveOption{} + + opt.Class = binary.BigEndian.Uint16(data[0:1]) + opt.Type = data[2] + opt.Flags = data[3] >> 4 + opt.Length = data[3] & 0xf + + copy(opt.Data, data[4:opt.Length]) + + return opt, 4 + opt.Length +} + +func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + gn.Version = data[0] >> 7 + gn.OptionsLength = data[0] & 0x3f + + gn.OAMPacket = data[1]&0x80 > 0 + gn.CriticalOption = data[1]&0x40 > 0 + gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4])) + + var buf [4]byte + copy(buf[1:], data[4:7]) + gn.VNI = binary.BigEndian.Uint32(buf[:]) + + offset, length := uint8(8), gn.OptionsLength + for length > 0 { + opt, len := decodeGeneveOption(data[offset:], gn) + gn.Options = append(gn.Options, opt) + + length -= len + offset += len + } + + gn.BaseLayer = BaseLayer{data[:offset], data[offset:]} + + return nil +} + +func (gn *Geneve) NextLayerType() gopacket.LayerType { + return gn.Protocol.LayerType() +} + +func decodeGeneve(data []byte, p gopacket.PacketBuilder) error { + gn := &Geneve{} + return decodingLayerDecoder(gn, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/geneve_test.go b/vendor/github.com/google/gopacket/layers/geneve_test.go new file mode 100644 index 00000000..ee34d524 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/geneve_test.go @@ -0,0 +1,108 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "reflect" + "testing" + + "github.com/google/gopacket" +) + +var testPacketGeneve1 = []byte{ + 0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0xfa, 0x16, 0x3e, 0x23, 0xd3, 0x42, + 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x86, 0x87, 0x39, 0x40, 0x00, + 0x40, 0x11, 0x31, 0x35, 0xc0, 0xa8, 0x00, 0x53, 0xc0, 0xa8, 0x00, 0x55, + 0x31, 0x57, 0x17, 0xc1, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x65, 0x58, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x09, 0x60, 0x5f, 0xa0, 0x91, 0xa2, 0xfe, + 0x54, 0x48, 0x88, 0x51, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0x01, 0xf6, + 0x40, 0x00, 0x40, 0x01, 0xb7, 0x5f, 0xc0, 0xa8, 0x00, 0x01, 0xc0, 0xa8, + 0x00, 0x02, 0x08, 0x00, 0x79, 0xdf, 0x0c, 0xfa, 0x63, 0xc4, 0x03, 0x0b, + 0x50, 0x58, 0x00, 0x00, 0x00, 0x00, 0xee, 0x2b, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +} + +var testPacketGeneve2 = []byte{ + 0x12, 0xbe, 0x4e, 0xb6, 0xa7, 0xc7, 0x02, 0x88, 0x0a, 0x81, 0xbd, 0x6d, + 0x08, 0x00, 0x45, 0x00, 0x00, 0x86, 0x20, 0xf2, 0x00, 0x00, 0x40, 0x11, + 0x01, 0x52, 0xac, 0x10, 0x00, 0x01, 0xac, 0x10, 0x00, 0x02, 0x40, 0xa6, + 0x17, 0xc1, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x65, 0x58, 0x00, 0x00, + 0x0a, 0x00, 0xd2, 0x8c, 0xdb, 0x12, 0x53, 0xd5, 0x8e, 0xab, 0xa2, 0xa5, + 0x02, 0xf7, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0x38, 0x1a, 0x40, 0x00, + 0x40, 0x01, 0x81, 0x3b, 0xc0, 0xa8, 0x00, 0x01, 0xc0, 0xa8, 0x00, 0x02, + 0x08, 0x00, 0xdd, 0x9d, 0x7e, 0xde, 0x02, 0xc3, 0xcb, 0x07, 0x51, 0x58, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x8d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, +} + +func TestDecodeGeneve1(t *testing.T) { + p := gopacket.NewPacket(testPacketGeneve1, LinkTypeLinuxSLL, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{ + LayerTypeLinuxSLL, LayerTypeIPv4, LayerTypeUDP, LayerTypeGeneve, + LayerTypeEthernet, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload, + }, t) + if got, ok := p.Layer(LayerTypeGeneve).(*Geneve); ok { + want := &Geneve{ + BaseLayer: BaseLayer{ + Contents: testPacketGeneve1[44:52], + Payload: testPacketGeneve1[52:150], + }, + Version: 0x0, + OptionsLength: 0x0, + OAMPacket: false, + CriticalOption: false, + Protocol: EthernetTypeTransparentEthernetBridging, + VNI: 0x0, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("Geneve layer mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } +} + +func TestDecodeGeneve2(t *testing.T) { + p := gopacket.NewPacket(testPacketGeneve2, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{ + LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeGeneve, + LayerTypeEthernet, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload, + }, t) + if got, ok := p.Layer(LayerTypeGeneve).(*Geneve); ok { + want := &Geneve{ + BaseLayer: BaseLayer{ + Contents: testPacketGeneve2[42:50], + Payload: testPacketGeneve2[50:148], + }, + Version: 0x0, + OptionsLength: 0x0, + OAMPacket: false, + CriticalOption: false, + Protocol: EthernetTypeTransparentEthernetBridging, + VNI: 0xa, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("Geneve layer mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } +} + +func BenchmarkDecodeGeneve1(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketGeneve1, LinkTypeEthernet, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/gre.go b/vendor/github.com/google/gopacket/layers/gre.go new file mode 100644 index 00000000..15d5290b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/gre.go @@ -0,0 +1,185 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + + "github.com/google/gopacket" +) + +// GRE is a Generic Routing Encapsulation header. +type GRE struct { + BaseLayer + ChecksumPresent, RoutingPresent, KeyPresent, SeqPresent, StrictSourceRoute bool + RecursionControl, Flags, Version uint8 + Protocol EthernetType + Checksum, Offset uint16 + Key, Seq uint32 + *GRERouting +} + +// GRERouting is GRE routing information, present if the RoutingPresent flag is +// set. +type GRERouting struct { + AddressFamily uint16 + SREOffset, SRELength uint8 + RoutingInformation []byte + Next *GRERouting +} + +// LayerType returns gopacket.LayerTypeGRE. +func (g *GRE) LayerType() gopacket.LayerType { return LayerTypeGRE } + +// DecodeFromBytes decodes the given bytes into this layer. +func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + g.ChecksumPresent = data[0]&0x80 != 0 + g.RoutingPresent = data[0]&0x40 != 0 + g.KeyPresent = data[0]&0x20 != 0 + g.SeqPresent = data[0]&0x10 != 0 + g.StrictSourceRoute = data[0]&0x08 != 0 + g.RecursionControl = data[0] & 0x7 + g.Flags = data[1] >> 3 + g.Version = data[1] & 0x7 + g.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4])) + offset := 4 + if g.ChecksumPresent || g.RoutingPresent { + g.Checksum = binary.BigEndian.Uint16(data[offset : offset+2]) + g.Offset = binary.BigEndian.Uint16(data[offset+2 : offset+4]) + offset += 4 + } + if g.KeyPresent { + g.Key = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } + if g.SeqPresent { + g.Seq = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } + if g.RoutingPresent { + tail := &g.GRERouting + for { + sre := &GRERouting{ + AddressFamily: binary.BigEndian.Uint16(data[offset : offset+2]), + SREOffset: data[offset+2], + SRELength: data[offset+3], + } + sre.RoutingInformation = data[offset+4 : offset+4+int(sre.SRELength)] + offset += 4 + int(sre.SRELength) + if sre.AddressFamily == 0 && sre.SRELength == 0 { + break + } + (*tail) = sre + tail = &sre.Next + } + } + g.BaseLayer = BaseLayer{data[:offset], data[offset:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the SerializationBuffer, +// implementing gopacket.SerializableLayer. See the docs for gopacket.SerializableLayer for more info. +func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + size := 4 + if g.ChecksumPresent || g.RoutingPresent { + size += 4 + } + if g.KeyPresent { + size += 4 + } + if g.SeqPresent { + size += 4 + } + if g.RoutingPresent { + r := g.GRERouting + for r != nil { + size += 4 + int(r.SRELength) + r = r.Next + } + size += 4 + } + buf, err := b.PrependBytes(size) + if err != nil { + return err + } + // Reset any potentially dirty memory in the first 2 bytes, as these use OR to set flags. + buf[0] = 0 + buf[1] = 0 + if g.ChecksumPresent { + buf[0] |= 0x80 + } + if g.RoutingPresent { + buf[0] |= 0x40 + } + if g.KeyPresent { + buf[0] |= 0x20 + } + if g.SeqPresent { + buf[0] |= 0x10 + } + if g.StrictSourceRoute { + buf[0] |= 0x08 + } + buf[0] |= g.RecursionControl + buf[1] |= g.Flags << 3 + buf[1] |= g.Version + binary.BigEndian.PutUint16(buf[2:4], uint16(g.Protocol)) + offset := 4 + if g.ChecksumPresent || g.RoutingPresent { + // Don't write the checksum value yet, as we may need to compute it, + // which requires the entire header be complete. + // Instead we zeroize the memory in case it is dirty. + buf[offset] = 0 + buf[offset+1] = 0 + binary.BigEndian.PutUint16(buf[offset+2:offset+4], g.Offset) + offset += 4 + } + if g.KeyPresent { + binary.BigEndian.PutUint32(buf[offset:offset+4], g.Key) + offset += 4 + } + if g.SeqPresent { + binary.BigEndian.PutUint32(buf[offset:offset+4], g.Seq) + offset += 4 + } + if g.RoutingPresent { + sre := g.GRERouting + for sre != nil { + binary.BigEndian.PutUint16(buf[offset:offset+2], sre.AddressFamily) + buf[offset+2] = sre.SREOffset + buf[offset+3] = sre.SRELength + copy(buf[offset+4:offset+4+int(sre.SRELength)], sre.RoutingInformation) + offset += 4 + int(sre.SRELength) + sre = sre.Next + } + // Terminate routing field with a "NULL" SRE. + binary.BigEndian.PutUint32(buf[offset:offset+4], 0) + } + if g.ChecksumPresent { + if opts.ComputeChecksums { + g.Checksum = tcpipChecksum(b.Bytes(), 0) + } + + binary.BigEndian.PutUint16(buf[4:6], g.Checksum) + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (g *GRE) CanDecode() gopacket.LayerClass { + return LayerTypeGRE +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (g *GRE) NextLayerType() gopacket.LayerType { + return g.Protocol.LayerType() +} + +func decodeGRE(data []byte, p gopacket.PacketBuilder) error { + g := &GRE{} + return decodingLayerDecoder(g, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/gre_test.go b/vendor/github.com/google/gopacket/layers/gre_test.go new file mode 100644 index 00000000..af2e38e3 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/gre_test.go @@ -0,0 +1,389 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +package layers + +import ( + "fmt" + "net" + "reflect" + "testing" + + "github.com/google/gopacket" +) + +// testPacketGRE is the packet: +// 15:08:08.003196 IP 192.168.1.1 > 192.168.1.2: GREv0, length 88: IP 172.16.1.1 > 172.16.2.1: ICMP echo request, id 4724, seq 1, length 64 +// 0x0000: 3a56 6b69 595e 8e7a 12c3 a971 0800 4500 :VkiY^.z...q..E. +// 0x0010: 006c 843c 4000 402f 32d3 c0a8 0101 c0a8 .l.<@.@/2....... +// 0x0020: 0102 0000 0800 4500 0054 0488 4000 4001 ......E..T..@.@. +// 0x0030: dafe ac10 0101 ac10 0201 0800 82c4 1274 ...............t +// 0x0040: 0001 c892 a354 0000 0000 380c 0000 0000 .....T....8..... +// 0x0050: 0000 1011 1213 1415 1617 1819 1a1b 1c1d ................ +// 0x0060: 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d ...!"#$%&'()*+,- +// 0x0070: 2e2f 3031 3233 3435 3637 ./01234567 +var testPacketGRE = []byte{ + 0x3a, 0x56, 0x6b, 0x69, 0x59, 0x5e, 0x8e, 0x7a, 0x12, 0xc3, 0xa9, 0x71, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x6c, 0x84, 0x3c, 0x40, 0x00, 0x40, 0x2f, 0x32, 0xd3, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, + 0x01, 0x02, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0x04, 0x88, 0x40, 0x00, 0x40, 0x01, + 0xda, 0xfe, 0xac, 0x10, 0x01, 0x01, 0xac, 0x10, 0x02, 0x01, 0x08, 0x00, 0x82, 0xc4, 0x12, 0x74, + 0x00, 0x01, 0xc8, 0x92, 0xa3, 0x54, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +} + +func TestPacketGRE(t *testing.T) { + p := gopacket.NewPacket(testPacketGRE, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeGRE, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + if got, ok := p.Layer(LayerTypeGRE).(*GRE); ok { + want := &GRE{ + BaseLayer: BaseLayer{testPacketGRE[34:38], testPacketGRE[38:]}, + Protocol: EthernetTypeIPv4, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("GRE layer mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } +} + +func BenchmarkDecodePacketGRE(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketGRE, LinkTypeEthernet, gopacket.NoCopy) + } +} + +var testIPv4OverGRE = []gopacket.SerializableLayer{ + &Ethernet{ + SrcMAC: net.HardwareAddr{142, 122, 18, 195, 169, 113}, + DstMAC: net.HardwareAddr{58, 86, 107, 105, 89, 94}, + EthernetType: EthernetTypeIPv4, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{192, 168, 1, 1}, + DstIP: net.IP{192, 168, 1, 2}, + Protocol: IPProtocolGRE, + Flags: IPv4DontFragment, + TTL: 64, + Id: 33852, + IHL: 5, + }, + &GRE{ + Protocol: EthernetTypeIPv4, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{172, 16, 1, 1}, + DstIP: net.IP{172, 16, 2, 1}, + Protocol: IPProtocolICMPv4, + Flags: IPv4DontFragment, + TTL: 64, + IHL: 5, + Id: 1160, + }, + &ICMPv4{ + TypeCode: CreateICMPv4TypeCode(ICMPv4TypeEchoRequest, 0), + Id: 4724, + Seq: 1, + }, + gopacket.Payload{ + 0xc8, 0x92, 0xa3, 0x54, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + }, +} + +func TestIPv4OverGREEncode(t *testing.T) { + b := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + if err := gopacket.SerializeLayers(b, opts, testIPv4OverGRE...); err != nil { + t.Errorf("Unable to serialize: %v", err) + } + p := gopacket.NewPacket(b.Bytes(), LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeGRE, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + if got, want := b.Bytes(), testPacketGRE; !reflect.DeepEqual(want, got) { + t.Errorf("Encoding mismatch, \nwant: %v\ngot %v\n", want, got) + } +} + +func BenchmarkEncodePacketGRE(b *testing.B) { + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + for i := 0; i < b.N; i++ { + gopacket.SerializeLayers(buf, opts, testIPv4OverGRE...) + buf.Clear() + } +} + +// testPacketEthernetOverGRE is the packet: +// 11:01:38.124768 IP 192.168.1.1 > 192.168.1.2: GREv0, length 102: IP 172.16.1.1 > 172.16.1.2: ICMP echo request, id 3842, seq 1, length 64 +// 0x0000: ea6b 4cd3 5513 d6b9 d880 56ef 0800 4500 .kL.U.....V...E. +// 0x0010: 007a 0acd 4000 402f ac34 c0a8 0101 c0a8 .z..@.@/.4...... +// 0x0020: 0102 0000 6558 aa6a 36e6 c630 6e32 3ec7 ....eX.j6..0n2>. +// 0x0030: 9def 0800 4500 0054 d970 4000 4001 0715 ....E..T.p@.@... +// 0x0040: ac10 0101 ac10 0102 0800 3f15 0f02 0001 ..........?..... +// 0x0050: 82d9 b154 0000 0000 b5e6 0100 0000 0000 ...T............ +// 0x0060: 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f ................ +// 0x0070: 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f .!"#$%&'()*+,-./ +// 0x0080: 3031 3233 3435 3637 01234567 +var testPacketEthernetOverGRE = []byte{ + 0xea, 0x6b, 0x4c, 0xd3, 0x55, 0x13, 0xd6, 0xb9, 0xd8, 0x80, 0x56, 0xef, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x7a, 0x0a, 0xcd, 0x40, 0x00, 0x40, 0x2f, 0xac, 0x34, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, + 0x01, 0x02, 0x00, 0x00, 0x65, 0x58, 0xaa, 0x6a, 0x36, 0xe6, 0xc6, 0x30, 0x6e, 0x32, 0x3e, 0xc7, + 0x9d, 0xef, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0xd9, 0x70, 0x40, 0x00, 0x40, 0x01, 0x07, 0x15, + 0xac, 0x10, 0x01, 0x01, 0xac, 0x10, 0x01, 0x02, 0x08, 0x00, 0x3f, 0x15, 0x0f, 0x02, 0x00, 0x01, + 0x82, 0xd9, 0xb1, 0x54, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xe6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +} + +func TestPacketEthernetOverGRE(t *testing.T) { + p := gopacket.NewPacket(testPacketEthernetOverGRE, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeGRE, LayerTypeEthernet, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + if got, ok := p.Layer(LayerTypeGRE).(*GRE); ok { + want := &GRE{ + BaseLayer: BaseLayer{testPacketEthernetOverGRE[34:38], testPacketEthernetOverGRE[38:]}, + Protocol: EthernetTypeTransparentEthernetBridging, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("GRE layer mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } +} + +func BenchmarkDecodePacketEthernetOverGRE(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketEthernetOverGRE, LinkTypeEthernet, gopacket.NoCopy) + } +} + +var testEthernetOverGRE = []gopacket.SerializableLayer{ + &Ethernet{ + SrcMAC: net.HardwareAddr{0xd6, 0xb9, 0xd8, 0x80, 0x56, 0xef}, + DstMAC: net.HardwareAddr{0xea, 0x6b, 0x4c, 0xd3, 0x55, 0x13}, + EthernetType: EthernetTypeIPv4, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{192, 168, 1, 1}, + DstIP: net.IP{192, 168, 1, 2}, + Protocol: IPProtocolGRE, + Flags: IPv4DontFragment, + TTL: 64, + Id: 2765, + IHL: 5, + }, + &GRE{ + Protocol: EthernetTypeTransparentEthernetBridging, + }, + &Ethernet{ + SrcMAC: net.HardwareAddr{0x6e, 0x32, 0x3e, 0xc7, 0x9d, 0xef}, + DstMAC: net.HardwareAddr{0xaa, 0x6a, 0x36, 0xe6, 0xc6, 0x30}, + EthernetType: EthernetTypeIPv4, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{172, 16, 1, 1}, + DstIP: net.IP{172, 16, 1, 2}, + Protocol: IPProtocolICMPv4, + Flags: IPv4DontFragment, + TTL: 64, + IHL: 5, + Id: 55664, + }, + &ICMPv4{ + TypeCode: CreateICMPv4TypeCode(ICMPv4TypeEchoRequest, 0), + Id: 3842, + Seq: 1, + }, + gopacket.Payload{ + 0x82, 0xd9, 0xb1, 0x54, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xe6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + }, +} + +func TestEthernetOverGREEncode(t *testing.T) { + b := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + if err := gopacket.SerializeLayers(b, opts, testEthernetOverGRE...); err != nil { + t.Errorf("Unable to serialize: %v", err) + } + p := gopacket.NewPacket(b.Bytes(), LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeGRE, LayerTypeEthernet, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + if got, want := b.Bytes(), testPacketEthernetOverGRE; !reflect.DeepEqual(want, got) { + t.Errorf("Encoding mismatch, \nwant: %v\ngot %v\n", want, got) + } +} + +func BenchmarkEncodePacketEthernetOverGRE(b *testing.B) { + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + for i := 0; i < b.N; i++ { + gopacket.SerializeLayers(buf, opts, testEthernetOverGRE...) + buf.Clear() + } +} + +var testGREChecksum = map[uint16][]gopacket.SerializableLayer{ + 0x77ff: { + &Ethernet{ + SrcMAC: net.HardwareAddr{0xc2, 0x00, 0x57, 0x75, 0x00, 0x00}, + DstMAC: net.HardwareAddr{0xc2, 0x01, 0x57, 0x75, 0x00, 0x00}, + EthernetType: EthernetTypeIPv4, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{10, 0, 0, 1}, + DstIP: net.IP{10, 0, 0, 2}, + Protocol: IPProtocolGRE, + TTL: 255, + Id: 10, + IHL: 5, + }, + &GRE{ + Protocol: EthernetTypeIPv4, + ChecksumPresent: true, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{1, 1, 1, 1}, + DstIP: net.IP{2, 2, 2, 2}, + Protocol: IPProtocolICMPv4, + TTL: 255, + IHL: 5, + Id: 10, + }, + &ICMPv4{ + TypeCode: CreateICMPv4TypeCode(ICMPv4TypeEchoRequest, 0), + Id: 2, + Seq: 0, + }, + gopacket.Payload{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xbe, 0x70, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + }, + }, + 0x8475: { + &Ethernet{ + SrcMAC: net.HardwareAddr{0xc2, 0x00, 0x57, 0x75, 0x00, 0x00}, + DstMAC: net.HardwareAddr{0xc2, 0x01, 0x57, 0x75, 0x00, 0x00}, + EthernetType: EthernetTypeIPv4, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{10, 0, 0, 1}, + DstIP: net.IP{10, 0, 0, 2}, + Protocol: IPProtocolGRE, + TTL: 255, + Id: 10, + IHL: 5, + }, + &GRE{ + Protocol: EthernetTypeIPv4, + ChecksumPresent: true, + }, + &IPv4{ + Version: 4, + SrcIP: net.IP{2, 3, 4, 5}, + DstIP: net.IP{2, 3, 4, 50}, + Protocol: IPProtocolUDP, + TTL: 1, + IHL: 5, + Flags: IPv4DontFragment, + Id: 964, + }, + &UDP{ + SrcPort: 41781, + DstPort: 33434, + }, + gopacket.Payload{ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + }, + }, +} + +func TestGREChecksum(t *testing.T) { + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + for cksum, packet := range testGREChecksum { + buf.Clear() + if err := setNetworkLayer(packet); err != nil { + t.Errorf("Failed to set network layer: %v", err) + continue + } + if err := gopacket.SerializeLayers(buf, opts, packet...); err != nil { + t.Errorf("Failed to serialize packet: %v", err) + continue + } + p := gopacket.NewPacket(buf.Bytes(), LinkTypeEthernet, gopacket.Default) + t.Log(p) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + continue + } + if got, ok := p.Layer(LayerTypeGRE).(*GRE); ok { + if got.Checksum != cksum { + t.Errorf("Incorrect checksum calculated for GRE packet: want %v, got %v", cksum, got.Checksum) + } + } + } +} + +func setNetworkLayer(layers []gopacket.SerializableLayer) error { + type setNetworkLayerForChecksum interface { + SetNetworkLayerForChecksum(gopacket.NetworkLayer) error + } + var l gopacket.NetworkLayer + for _, layer := range layers { + if n, ok := layer.(gopacket.NetworkLayer); ok { + l = n + } + if s, ok := layer.(setNetworkLayerForChecksum); ok { + if l == nil { + return fmt.Errorf("no enclosing network layer found before: %v", s) + } + if err := s.SetNetworkLayerForChecksum(l); err != nil { + return fmt.Errorf("failed to set network layer(%v) on layer(%v): %v", l, s, err) + } + } + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/iana_ports.go b/vendor/github.com/google/gopacket/layers/iana_ports.go new file mode 100644 index 00000000..54406359 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/iana_ports.go @@ -0,0 +1,11314 @@ +// Copyright 2012 Google, Inc. All rights reserved. + +package layers + +// Created by gen.go, don't edit manually +// Generated at 2017-01-04 15:05:26.649794815 -0700 MST +// Fetched from "http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml" + +// TCPPortNames contains the port names for all TCP ports. +var TCPPortNames = tcpPortNames + +// UDPPortNames contains the port names for all UDP ports. +var UDPPortNames = udpPortNames + +// SCTPPortNames contains the port names for all SCTP ports. +var SCTPPortNames = sctpPortNames + +var tcpPortNames = map[TCPPort]string{ + 1: "tcpmux", + 2: "compressnet", + 3: "compressnet", + 5: "rje", + 7: "echo", + 9: "discard", + 11: "systat", + 13: "daytime", + 17: "qotd", + 18: "msp", + 19: "chargen", + 20: "ftp-data", + 21: "ftp", + 22: "ssh", + 23: "telnet", + 25: "smtp", + 27: "nsw-fe", + 29: "msg-icp", + 31: "msg-auth", + 33: "dsp", + 37: "time", + 38: "rap", + 39: "rlp", + 41: "graphics", + 42: "name", + 43: "nicname", + 44: "mpm-flags", + 45: "mpm", + 46: "mpm-snd", + 47: "ni-ftp", + 48: "auditd", + 49: "tacacs", + 50: "re-mail-ck", + 52: "xns-time", + 53: "domain", + 54: "xns-ch", + 55: "isi-gl", + 56: "xns-auth", + 58: "xns-mail", + 61: "ni-mail", + 62: "acas", + 63: "whoispp", + 64: "covia", + 65: "tacacs-ds", + 66: "sql-net", + 67: "bootps", + 68: "bootpc", + 69: "tftp", + 70: "gopher", + 71: "netrjs-1", + 72: "netrjs-2", + 73: "netrjs-3", + 74: "netrjs-4", + 76: "deos", + 78: "vettcp", + 79: "finger", + 80: "http", + 82: "xfer", + 83: "mit-ml-dev", + 84: "ctf", + 85: "mit-ml-dev", + 86: "mfcobol", + 88: "kerberos", + 89: "su-mit-tg", + 90: "dnsix", + 91: "mit-dov", + 92: "npp", + 93: "dcp", + 94: "objcall", + 95: "supdup", + 96: "dixie", + 97: "swift-rvf", + 98: "tacnews", + 99: "metagram", + 101: "hostname", + 102: "iso-tsap", + 103: "gppitnp", + 104: "acr-nema", + 105: "cso", + 106: "3com-tsmux", + 107: "rtelnet", + 108: "snagas", + 109: "pop2", + 110: "pop3", + 111: "sunrpc", + 112: "mcidas", + 113: "ident", + 115: "sftp", + 116: "ansanotify", + 117: "uucp-path", + 118: "sqlserv", + 119: "nntp", + 120: "cfdptkt", + 121: "erpc", + 122: "smakynet", + 123: "ntp", + 124: "ansatrader", + 125: "locus-map", + 126: "nxedit", + 127: "locus-con", + 128: "gss-xlicen", + 129: "pwdgen", + 130: "cisco-fna", + 131: "cisco-tna", + 132: "cisco-sys", + 133: "statsrv", + 134: "ingres-net", + 135: "epmap", + 136: "profile", + 137: "netbios-ns", + 138: "netbios-dgm", + 139: "netbios-ssn", + 140: "emfis-data", + 141: "emfis-cntl", + 142: "bl-idm", + 143: "imap", + 144: "uma", + 145: "uaac", + 146: "iso-tp0", + 147: "iso-ip", + 148: "jargon", + 149: "aed-512", + 150: "sql-net", + 151: "hems", + 152: "bftp", + 153: "sgmp", + 154: "netsc-prod", + 155: "netsc-dev", + 156: "sqlsrv", + 157: "knet-cmp", + 158: "pcmail-srv", + 159: "nss-routing", + 160: "sgmp-traps", + 161: "snmp", + 162: "snmptrap", + 163: "cmip-man", + 164: "cmip-agent", + 165: "xns-courier", + 166: "s-net", + 167: "namp", + 168: "rsvd", + 169: "send", + 170: "print-srv", + 171: "multiplex", + 172: "cl-1", + 173: "xyplex-mux", + 174: "mailq", + 175: "vmnet", + 176: "genrad-mux", + 177: "xdmcp", + 178: "nextstep", + 179: "bgp", + 180: "ris", + 181: "unify", + 182: "audit", + 183: "ocbinder", + 184: "ocserver", + 185: "remote-kis", + 186: "kis", + 187: "aci", + 188: "mumps", + 189: "qft", + 190: "gacp", + 191: "prospero", + 192: "osu-nms", + 193: "srmp", + 194: "irc", + 195: "dn6-nlm-aud", + 196: "dn6-smm-red", + 197: "dls", + 198: "dls-mon", + 199: "smux", + 200: "src", + 201: "at-rtmp", + 202: "at-nbp", + 203: "at-3", + 204: "at-echo", + 205: "at-5", + 206: "at-zis", + 207: "at-7", + 208: "at-8", + 209: "qmtp", + 210: "z39-50", + 211: "914c-g", + 212: "anet", + 213: "ipx", + 214: "vmpwscs", + 215: "softpc", + 216: "CAIlic", + 217: "dbase", + 218: "mpp", + 219: "uarps", + 220: "imap3", + 221: "fln-spx", + 222: "rsh-spx", + 223: "cdc", + 224: "masqdialer", + 242: "direct", + 243: "sur-meas", + 244: "inbusiness", + 245: "link", + 246: "dsp3270", + 247: "subntbcst-tftp", + 248: "bhfhs", + 256: "rap", + 257: "set", + 259: "esro-gen", + 260: "openport", + 261: "nsiiops", + 262: "arcisdms", + 263: "hdap", + 264: "bgmp", + 265: "x-bone-ctl", + 266: "sst", + 267: "td-service", + 268: "td-replica", + 269: "manet", + 271: "pt-tls", + 280: "http-mgmt", + 281: "personal-link", + 282: "cableport-ax", + 283: "rescap", + 284: "corerjd", + 286: "fxp", + 287: "k-block", + 308: "novastorbakcup", + 309: "entrusttime", + 310: "bhmds", + 311: "asip-webadmin", + 312: "vslmp", + 313: "magenta-logic", + 314: "opalis-robot", + 315: "dpsi", + 316: "decauth", + 317: "zannet", + 318: "pkix-timestamp", + 319: "ptp-event", + 320: "ptp-general", + 321: "pip", + 322: "rtsps", + 323: "rpki-rtr", + 324: "rpki-rtr-tls", + 333: "texar", + 344: "pdap", + 345: "pawserv", + 346: "zserv", + 347: "fatserv", + 348: "csi-sgwp", + 349: "mftp", + 350: "matip-type-a", + 351: "matip-type-b", + 352: "dtag-ste-sb", + 353: "ndsauth", + 354: "bh611", + 355: "datex-asn", + 356: "cloanto-net-1", + 357: "bhevent", + 358: "shrinkwrap", + 359: "nsrmp", + 360: "scoi2odialog", + 361: "semantix", + 362: "srssend", + 363: "rsvp-tunnel", + 364: "aurora-cmgr", + 365: "dtk", + 366: "odmr", + 367: "mortgageware", + 368: "qbikgdp", + 369: "rpc2portmap", + 370: "codaauth2", + 371: "clearcase", + 372: "ulistproc", + 373: "legent-1", + 374: "legent-2", + 375: "hassle", + 376: "nip", + 377: "tnETOS", + 378: "dsETOS", + 379: "is99c", + 380: "is99s", + 381: "hp-collector", + 382: "hp-managed-node", + 383: "hp-alarm-mgr", + 384: "arns", + 385: "ibm-app", + 386: "asa", + 387: "aurp", + 388: "unidata-ldm", + 389: "ldap", + 390: "uis", + 391: "synotics-relay", + 392: "synotics-broker", + 393: "meta5", + 394: "embl-ndt", + 395: "netcp", + 396: "netware-ip", + 397: "mptn", + 398: "kryptolan", + 399: "iso-tsap-c2", + 400: "osb-sd", + 401: "ups", + 402: "genie", + 403: "decap", + 404: "nced", + 405: "ncld", + 406: "imsp", + 407: "timbuktu", + 408: "prm-sm", + 409: "prm-nm", + 410: "decladebug", + 411: "rmt", + 412: "synoptics-trap", + 413: "smsp", + 414: "infoseek", + 415: "bnet", + 416: "silverplatter", + 417: "onmux", + 418: "hyper-g", + 419: "ariel1", + 420: "smpte", + 421: "ariel2", + 422: "ariel3", + 423: "opc-job-start", + 424: "opc-job-track", + 425: "icad-el", + 426: "smartsdp", + 427: "svrloc", + 428: "ocs-cmu", + 429: "ocs-amu", + 430: "utmpsd", + 431: "utmpcd", + 432: "iasd", + 433: "nnsp", + 434: "mobileip-agent", + 435: "mobilip-mn", + 436: "dna-cml", + 437: "comscm", + 438: "dsfgw", + 439: "dasp", + 440: "sgcp", + 441: "decvms-sysmgt", + 442: "cvc-hostd", + 443: "https", + 444: "snpp", + 445: "microsoft-ds", + 446: "ddm-rdb", + 447: "ddm-dfm", + 448: "ddm-ssl", + 449: "as-servermap", + 450: "tserver", + 451: "sfs-smp-net", + 452: "sfs-config", + 453: "creativeserver", + 454: "contentserver", + 455: "creativepartnr", + 456: "macon-tcp", + 457: "scohelp", + 458: "appleqtc", + 459: "ampr-rcmd", + 460: "skronk", + 461: "datasurfsrv", + 462: "datasurfsrvsec", + 463: "alpes", + 464: "kpasswd", + 465: "urd", + 466: "digital-vrc", + 467: "mylex-mapd", + 468: "photuris", + 469: "rcp", + 470: "scx-proxy", + 471: "mondex", + 472: "ljk-login", + 473: "hybrid-pop", + 474: "tn-tl-w1", + 475: "tcpnethaspsrv", + 476: "tn-tl-fd1", + 477: "ss7ns", + 478: "spsc", + 479: "iafserver", + 480: "iafdbase", + 481: "ph", + 482: "bgs-nsi", + 483: "ulpnet", + 484: "integra-sme", + 485: "powerburst", + 486: "avian", + 487: "saft", + 488: "gss-http", + 489: "nest-protocol", + 490: "micom-pfs", + 491: "go-login", + 492: "ticf-1", + 493: "ticf-2", + 494: "pov-ray", + 495: "intecourier", + 496: "pim-rp-disc", + 497: "retrospect", + 498: "siam", + 499: "iso-ill", + 500: "isakmp", + 501: "stmf", + 502: "mbap", + 503: "intrinsa", + 504: "citadel", + 505: "mailbox-lm", + 506: "ohimsrv", + 507: "crs", + 508: "xvttp", + 509: "snare", + 510: "fcp", + 511: "passgo", + 512: "exec", + 513: "login", + 514: "shell", + 515: "printer", + 516: "videotex", + 517: "talk", + 518: "ntalk", + 519: "utime", + 520: "efs", + 521: "ripng", + 522: "ulp", + 523: "ibm-db2", + 524: "ncp", + 525: "timed", + 526: "tempo", + 527: "stx", + 528: "custix", + 529: "irc-serv", + 530: "courier", + 531: "conference", + 532: "netnews", + 533: "netwall", + 534: "windream", + 535: "iiop", + 536: "opalis-rdv", + 537: "nmsp", + 538: "gdomap", + 539: "apertus-ldp", + 540: "uucp", + 541: "uucp-rlogin", + 542: "commerce", + 543: "klogin", + 544: "kshell", + 545: "appleqtcsrvr", + 546: "dhcpv6-client", + 547: "dhcpv6-server", + 548: "afpovertcp", + 549: "idfp", + 550: "new-rwho", + 551: "cybercash", + 552: "devshr-nts", + 553: "pirp", + 554: "rtsp", + 555: "dsf", + 556: "remotefs", + 557: "openvms-sysipc", + 558: "sdnskmp", + 559: "teedtap", + 560: "rmonitor", + 561: "monitor", + 562: "chshell", + 563: "nntps", + 564: "9pfs", + 565: "whoami", + 566: "streettalk", + 567: "banyan-rpc", + 568: "ms-shuttle", + 569: "ms-rome", + 570: "meter", + 571: "meter", + 572: "sonar", + 573: "banyan-vip", + 574: "ftp-agent", + 575: "vemmi", + 576: "ipcd", + 577: "vnas", + 578: "ipdd", + 579: "decbsrv", + 580: "sntp-heartbeat", + 581: "bdp", + 582: "scc-security", + 583: "philips-vc", + 584: "keyserver", + 586: "password-chg", + 587: "submission", + 588: "cal", + 589: "eyelink", + 590: "tns-cml", + 591: "http-alt", + 592: "eudora-set", + 593: "http-rpc-epmap", + 594: "tpip", + 595: "cab-protocol", + 596: "smsd", + 597: "ptcnameservice", + 598: "sco-websrvrmg3", + 599: "acp", + 600: "ipcserver", + 601: "syslog-conn", + 602: "xmlrpc-beep", + 603: "idxp", + 604: "tunnel", + 605: "soap-beep", + 606: "urm", + 607: "nqs", + 608: "sift-uft", + 609: "npmp-trap", + 610: "npmp-local", + 611: "npmp-gui", + 612: "hmmp-ind", + 613: "hmmp-op", + 614: "sshell", + 615: "sco-inetmgr", + 616: "sco-sysmgr", + 617: "sco-dtmgr", + 618: "dei-icda", + 619: "compaq-evm", + 620: "sco-websrvrmgr", + 621: "escp-ip", + 622: "collaborator", + 623: "oob-ws-http", + 624: "cryptoadmin", + 625: "dec-dlm", + 626: "asia", + 627: "passgo-tivoli", + 628: "qmqp", + 629: "3com-amp3", + 630: "rda", + 631: "ipp", + 632: "bmpp", + 633: "servstat", + 634: "ginad", + 635: "rlzdbase", + 636: "ldaps", + 637: "lanserver", + 638: "mcns-sec", + 639: "msdp", + 640: "entrust-sps", + 641: "repcmd", + 642: "esro-emsdp", + 643: "sanity", + 644: "dwr", + 645: "pssc", + 646: "ldp", + 647: "dhcp-failover", + 648: "rrp", + 649: "cadview-3d", + 650: "obex", + 651: "ieee-mms", + 652: "hello-port", + 653: "repscmd", + 654: "aodv", + 655: "tinc", + 656: "spmp", + 657: "rmc", + 658: "tenfold", + 660: "mac-srvr-admin", + 661: "hap", + 662: "pftp", + 663: "purenoise", + 664: "oob-ws-https", + 665: "sun-dr", + 666: "mdqs", + 667: "disclose", + 668: "mecomm", + 669: "meregister", + 670: "vacdsm-sws", + 671: "vacdsm-app", + 672: "vpps-qua", + 673: "cimplex", + 674: "acap", + 675: "dctp", + 676: "vpps-via", + 677: "vpp", + 678: "ggf-ncp", + 679: "mrm", + 680: "entrust-aaas", + 681: "entrust-aams", + 682: "xfr", + 683: "corba-iiop", + 684: "corba-iiop-ssl", + 685: "mdc-portmapper", + 686: "hcp-wismar", + 687: "asipregistry", + 688: "realm-rusd", + 689: "nmap", + 690: "vatp", + 691: "msexch-routing", + 692: "hyperwave-isp", + 693: "connendp", + 694: "ha-cluster", + 695: "ieee-mms-ssl", + 696: "rushd", + 697: "uuidgen", + 698: "olsr", + 699: "accessnetwork", + 700: "epp", + 701: "lmp", + 702: "iris-beep", + 704: "elcsd", + 705: "agentx", + 706: "silc", + 707: "borland-dsj", + 709: "entrust-kmsh", + 710: "entrust-ash", + 711: "cisco-tdp", + 712: "tbrpf", + 713: "iris-xpc", + 714: "iris-xpcs", + 715: "iris-lwz", + 729: "netviewdm1", + 730: "netviewdm2", + 731: "netviewdm3", + 741: "netgw", + 742: "netrcs", + 744: "flexlm", + 747: "fujitsu-dev", + 748: "ris-cm", + 749: "kerberos-adm", + 750: "rfile", + 751: "pump", + 752: "qrh", + 753: "rrh", + 754: "tell", + 758: "nlogin", + 759: "con", + 760: "ns", + 761: "rxe", + 762: "quotad", + 763: "cycleserv", + 764: "omserv", + 765: "webster", + 767: "phonebook", + 769: "vid", + 770: "cadlock", + 771: "rtip", + 772: "cycleserv2", + 773: "submit", + 774: "rpasswd", + 775: "entomb", + 776: "wpages", + 777: "multiling-http", + 780: "wpgs", + 800: "mdbs-daemon", + 801: "device", + 802: "mbap-s", + 810: "fcp-udp", + 828: "itm-mcell-s", + 829: "pkix-3-ca-ra", + 830: "netconf-ssh", + 831: "netconf-beep", + 832: "netconfsoaphttp", + 833: "netconfsoapbeep", + 847: "dhcp-failover2", + 848: "gdoi", + 853: "domain-s", + 860: "iscsi", + 861: "owamp-control", + 862: "twamp-control", + 873: "rsync", + 886: "iclcnet-locate", + 887: "iclcnet-svinfo", + 888: "accessbuilder", + 900: "omginitialrefs", + 901: "smpnameres", + 902: "ideafarm-door", + 903: "ideafarm-panic", + 910: "kink", + 911: "xact-backup", + 912: "apex-mesh", + 913: "apex-edge", + 989: "ftps-data", + 990: "ftps", + 991: "nas", + 992: "telnets", + 993: "imaps", + 995: "pop3s", + 996: "vsinet", + 997: "maitrd", + 998: "busboy", + 999: "garcon", + 1000: "cadlock2", + 1001: "webpush", + 1010: "surf", + 1021: "exp1", + 1022: "exp2", + 1025: "blackjack", + 1026: "cap", + 1029: "solid-mux", + 1033: "netinfo-local", + 1034: "activesync", + 1035: "mxxrlogin", + 1036: "nsstp", + 1037: "ams", + 1038: "mtqp", + 1039: "sbl", + 1040: "netarx", + 1041: "danf-ak2", + 1042: "afrog", + 1043: "boinc-client", + 1044: "dcutility", + 1045: "fpitp", + 1046: "wfremotertm", + 1047: "neod1", + 1048: "neod2", + 1049: "td-postman", + 1050: "cma", + 1051: "optima-vnet", + 1052: "ddt", + 1053: "remote-as", + 1054: "brvread", + 1055: "ansyslmd", + 1056: "vfo", + 1057: "startron", + 1058: "nim", + 1059: "nimreg", + 1060: "polestar", + 1061: "kiosk", + 1062: "veracity", + 1063: "kyoceranetdev", + 1064: "jstel", + 1065: "syscomlan", + 1066: "fpo-fns", + 1067: "instl-boots", + 1068: "instl-bootc", + 1069: "cognex-insight", + 1070: "gmrupdateserv", + 1071: "bsquare-voip", + 1072: "cardax", + 1073: "bridgecontrol", + 1074: "warmspotMgmt", + 1075: "rdrmshc", + 1076: "dab-sti-c", + 1077: "imgames", + 1078: "avocent-proxy", + 1079: "asprovatalk", + 1080: "socks", + 1081: "pvuniwien", + 1082: "amt-esd-prot", + 1083: "ansoft-lm-1", + 1084: "ansoft-lm-2", + 1085: "webobjects", + 1086: "cplscrambler-lg", + 1087: "cplscrambler-in", + 1088: "cplscrambler-al", + 1089: "ff-annunc", + 1090: "ff-fms", + 1091: "ff-sm", + 1092: "obrpd", + 1093: "proofd", + 1094: "rootd", + 1095: "nicelink", + 1096: "cnrprotocol", + 1097: "sunclustermgr", + 1098: "rmiactivation", + 1099: "rmiregistry", + 1100: "mctp", + 1101: "pt2-discover", + 1102: "adobeserver-1", + 1103: "adobeserver-2", + 1104: "xrl", + 1105: "ftranhc", + 1106: "isoipsigport-1", + 1107: "isoipsigport-2", + 1108: "ratio-adp", + 1110: "webadmstart", + 1111: "lmsocialserver", + 1112: "icp", + 1113: "ltp-deepspace", + 1114: "mini-sql", + 1115: "ardus-trns", + 1116: "ardus-cntl", + 1117: "ardus-mtrns", + 1118: "sacred", + 1119: "bnetgame", + 1120: "bnetfile", + 1121: "rmpp", + 1122: "availant-mgr", + 1123: "murray", + 1124: "hpvmmcontrol", + 1125: "hpvmmagent", + 1126: "hpvmmdata", + 1127: "kwdb-commn", + 1128: "saphostctrl", + 1129: "saphostctrls", + 1130: "casp", + 1131: "caspssl", + 1132: "kvm-via-ip", + 1133: "dfn", + 1134: "aplx", + 1135: "omnivision", + 1136: "hhb-gateway", + 1137: "trim", + 1138: "encrypted-admin", + 1139: "evm", + 1140: "autonoc", + 1141: "mxomss", + 1142: "edtools", + 1143: "imyx", + 1144: "fuscript", + 1145: "x9-icue", + 1146: "audit-transfer", + 1147: "capioverlan", + 1148: "elfiq-repl", + 1149: "bvtsonar", + 1150: "blaze", + 1151: "unizensus", + 1152: "winpoplanmess", + 1153: "c1222-acse", + 1154: "resacommunity", + 1155: "nfa", + 1156: "iascontrol-oms", + 1157: "iascontrol", + 1158: "dbcontrol-oms", + 1159: "oracle-oms", + 1160: "olsv", + 1161: "health-polling", + 1162: "health-trap", + 1163: "sddp", + 1164: "qsm-proxy", + 1165: "qsm-gui", + 1166: "qsm-remote", + 1167: "cisco-ipsla", + 1168: "vchat", + 1169: "tripwire", + 1170: "atc-lm", + 1171: "atc-appserver", + 1172: "dnap", + 1173: "d-cinema-rrp", + 1174: "fnet-remote-ui", + 1175: "dossier", + 1176: "indigo-server", + 1177: "dkmessenger", + 1178: "sgi-storman", + 1179: "b2n", + 1180: "mc-client", + 1181: "3comnetman", + 1182: "accelenet", + 1183: "llsurfup-http", + 1184: "llsurfup-https", + 1185: "catchpole", + 1186: "mysql-cluster", + 1187: "alias", + 1188: "hp-webadmin", + 1189: "unet", + 1190: "commlinx-avl", + 1191: "gpfs", + 1192: "caids-sensor", + 1193: "fiveacross", + 1194: "openvpn", + 1195: "rsf-1", + 1196: "netmagic", + 1197: "carrius-rshell", + 1198: "cajo-discovery", + 1199: "dmidi", + 1200: "scol", + 1201: "nucleus-sand", + 1202: "caiccipc", + 1203: "ssslic-mgr", + 1204: "ssslog-mgr", + 1205: "accord-mgc", + 1206: "anthony-data", + 1207: "metasage", + 1208: "seagull-ais", + 1209: "ipcd3", + 1210: "eoss", + 1211: "groove-dpp", + 1212: "lupa", + 1213: "mpc-lifenet", + 1214: "kazaa", + 1215: "scanstat-1", + 1216: "etebac5", + 1217: "hpss-ndapi", + 1218: "aeroflight-ads", + 1219: "aeroflight-ret", + 1220: "qt-serveradmin", + 1221: "sweetware-apps", + 1222: "nerv", + 1223: "tgp", + 1224: "vpnz", + 1225: "slinkysearch", + 1226: "stgxfws", + 1227: "dns2go", + 1228: "florence", + 1229: "zented", + 1230: "periscope", + 1231: "menandmice-lpm", + 1232: "first-defense", + 1233: "univ-appserver", + 1234: "search-agent", + 1235: "mosaicsyssvc1", + 1236: "bvcontrol", + 1237: "tsdos390", + 1238: "hacl-qs", + 1239: "nmsd", + 1240: "instantia", + 1241: "nessus", + 1242: "nmasoverip", + 1243: "serialgateway", + 1244: "isbconference1", + 1245: "isbconference2", + 1246: "payrouter", + 1247: "visionpyramid", + 1248: "hermes", + 1249: "mesavistaco", + 1250: "swldy-sias", + 1251: "servergraph", + 1252: "bspne-pcc", + 1253: "q55-pcc", + 1254: "de-noc", + 1255: "de-cache-query", + 1256: "de-server", + 1257: "shockwave2", + 1258: "opennl", + 1259: "opennl-voice", + 1260: "ibm-ssd", + 1261: "mpshrsv", + 1262: "qnts-orb", + 1263: "dka", + 1264: "prat", + 1265: "dssiapi", + 1266: "dellpwrappks", + 1267: "epc", + 1268: "propel-msgsys", + 1269: "watilapp", + 1270: "opsmgr", + 1271: "excw", + 1272: "cspmlockmgr", + 1273: "emc-gateway", + 1274: "t1distproc", + 1275: "ivcollector", + 1277: "miva-mqs", + 1278: "dellwebadmin-1", + 1279: "dellwebadmin-2", + 1280: "pictrography", + 1281: "healthd", + 1282: "emperion", + 1283: "productinfo", + 1284: "iee-qfx", + 1285: "neoiface", + 1286: "netuitive", + 1287: "routematch", + 1288: "navbuddy", + 1289: "jwalkserver", + 1290: "winjaserver", + 1291: "seagulllms", + 1292: "dsdn", + 1293: "pkt-krb-ipsec", + 1294: "cmmdriver", + 1295: "ehtp", + 1296: "dproxy", + 1297: "sdproxy", + 1298: "lpcp", + 1299: "hp-sci", + 1300: "h323hostcallsc", + 1301: "ci3-software-1", + 1302: "ci3-software-2", + 1303: "sftsrv", + 1304: "boomerang", + 1305: "pe-mike", + 1306: "re-conn-proto", + 1307: "pacmand", + 1308: "odsi", + 1309: "jtag-server", + 1310: "husky", + 1311: "rxmon", + 1312: "sti-envision", + 1313: "bmc-patroldb", + 1314: "pdps", + 1315: "els", + 1316: "exbit-escp", + 1317: "vrts-ipcserver", + 1318: "krb5gatekeeper", + 1319: "amx-icsp", + 1320: "amx-axbnet", + 1321: "pip", + 1322: "novation", + 1323: "brcd", + 1324: "delta-mcp", + 1325: "dx-instrument", + 1326: "wimsic", + 1327: "ultrex", + 1328: "ewall", + 1329: "netdb-export", + 1330: "streetperfect", + 1331: "intersan", + 1332: "pcia-rxp-b", + 1333: "passwrd-policy", + 1334: "writesrv", + 1335: "digital-notary", + 1336: "ischat", + 1337: "menandmice-dns", + 1338: "wmc-log-svc", + 1339: "kjtsiteserver", + 1340: "naap", + 1341: "qubes", + 1342: "esbroker", + 1343: "re101", + 1344: "icap", + 1345: "vpjp", + 1346: "alta-ana-lm", + 1347: "bbn-mmc", + 1348: "bbn-mmx", + 1349: "sbook", + 1350: "editbench", + 1351: "equationbuilder", + 1352: "lotusnote", + 1353: "relief", + 1354: "XSIP-network", + 1355: "intuitive-edge", + 1356: "cuillamartin", + 1357: "pegboard", + 1358: "connlcli", + 1359: "ftsrv", + 1360: "mimer", + 1361: "linx", + 1362: "timeflies", + 1363: "ndm-requester", + 1364: "ndm-server", + 1365: "adapt-sna", + 1366: "netware-csp", + 1367: "dcs", + 1368: "screencast", + 1369: "gv-us", + 1370: "us-gv", + 1371: "fc-cli", + 1372: "fc-ser", + 1373: "chromagrafx", + 1374: "molly", + 1375: "bytex", + 1376: "ibm-pps", + 1377: "cichlid", + 1378: "elan", + 1379: "dbreporter", + 1380: "telesis-licman", + 1381: "apple-licman", + 1382: "udt-os", + 1383: "gwha", + 1384: "os-licman", + 1385: "atex-elmd", + 1386: "checksum", + 1387: "cadsi-lm", + 1388: "objective-dbc", + 1389: "iclpv-dm", + 1390: "iclpv-sc", + 1391: "iclpv-sas", + 1392: "iclpv-pm", + 1393: "iclpv-nls", + 1394: "iclpv-nlc", + 1395: "iclpv-wsm", + 1396: "dvl-activemail", + 1397: "audio-activmail", + 1398: "video-activmail", + 1399: "cadkey-licman", + 1400: "cadkey-tablet", + 1401: "goldleaf-licman", + 1402: "prm-sm-np", + 1403: "prm-nm-np", + 1404: "igi-lm", + 1405: "ibm-res", + 1406: "netlabs-lm", + 1407: "tibet-server", + 1408: "sophia-lm", + 1409: "here-lm", + 1410: "hiq", + 1411: "af", + 1412: "innosys", + 1413: "innosys-acl", + 1414: "ibm-mqseries", + 1415: "dbstar", + 1416: "novell-lu6-2", + 1417: "timbuktu-srv1", + 1418: "timbuktu-srv2", + 1419: "timbuktu-srv3", + 1420: "timbuktu-srv4", + 1421: "gandalf-lm", + 1422: "autodesk-lm", + 1423: "essbase", + 1424: "hybrid", + 1425: "zion-lm", + 1426: "sais", + 1427: "mloadd", + 1428: "informatik-lm", + 1429: "nms", + 1430: "tpdu", + 1431: "rgtp", + 1432: "blueberry-lm", + 1433: "ms-sql-s", + 1434: "ms-sql-m", + 1435: "ibm-cics", + 1436: "saism", + 1437: "tabula", + 1438: "eicon-server", + 1439: "eicon-x25", + 1440: "eicon-slp", + 1441: "cadis-1", + 1442: "cadis-2", + 1443: "ies-lm", + 1444: "marcam-lm", + 1445: "proxima-lm", + 1446: "ora-lm", + 1447: "apri-lm", + 1448: "oc-lm", + 1449: "peport", + 1450: "dwf", + 1451: "infoman", + 1452: "gtegsc-lm", + 1453: "genie-lm", + 1454: "interhdl-elmd", + 1455: "esl-lm", + 1456: "dca", + 1457: "valisys-lm", + 1458: "nrcabq-lm", + 1459: "proshare1", + 1460: "proshare2", + 1461: "ibm-wrless-lan", + 1462: "world-lm", + 1463: "nucleus", + 1464: "msl-lmd", + 1465: "pipes", + 1466: "oceansoft-lm", + 1467: "csdmbase", + 1468: "csdm", + 1469: "aal-lm", + 1470: "uaiact", + 1471: "csdmbase", + 1472: "csdm", + 1473: "openmath", + 1474: "telefinder", + 1475: "taligent-lm", + 1476: "clvm-cfg", + 1477: "ms-sna-server", + 1478: "ms-sna-base", + 1479: "dberegister", + 1480: "pacerforum", + 1481: "airs", + 1482: "miteksys-lm", + 1483: "afs", + 1484: "confluent", + 1485: "lansource", + 1486: "nms-topo-serv", + 1487: "localinfosrvr", + 1488: "docstor", + 1489: "dmdocbroker", + 1490: "insitu-conf", + 1492: "stone-design-1", + 1493: "netmap-lm", + 1494: "ica", + 1495: "cvc", + 1496: "liberty-lm", + 1497: "rfx-lm", + 1498: "sybase-sqlany", + 1499: "fhc", + 1500: "vlsi-lm", + 1501: "saiscm", + 1502: "shivadiscovery", + 1503: "imtc-mcs", + 1504: "evb-elm", + 1505: "funkproxy", + 1506: "utcd", + 1507: "symplex", + 1508: "diagmond", + 1509: "robcad-lm", + 1510: "mvx-lm", + 1511: "3l-l1", + 1512: "wins", + 1513: "fujitsu-dtc", + 1514: "fujitsu-dtcns", + 1515: "ifor-protocol", + 1516: "vpad", + 1517: "vpac", + 1518: "vpvd", + 1519: "vpvc", + 1520: "atm-zip-office", + 1521: "ncube-lm", + 1522: "ricardo-lm", + 1523: "cichild-lm", + 1524: "ingreslock", + 1525: "orasrv", + 1526: "pdap-np", + 1527: "tlisrv", + 1529: "coauthor", + 1530: "rap-service", + 1531: "rap-listen", + 1532: "miroconnect", + 1533: "virtual-places", + 1534: "micromuse-lm", + 1535: "ampr-info", + 1536: "ampr-inter", + 1537: "sdsc-lm", + 1538: "3ds-lm", + 1539: "intellistor-lm", + 1540: "rds", + 1541: "rds2", + 1542: "gridgen-elmd", + 1543: "simba-cs", + 1544: "aspeclmd", + 1545: "vistium-share", + 1546: "abbaccuray", + 1547: "laplink", + 1548: "axon-lm", + 1549: "shivahose", + 1550: "3m-image-lm", + 1551: "hecmtl-db", + 1552: "pciarray", + 1553: "sna-cs", + 1554: "caci-lm", + 1555: "livelan", + 1556: "veritas-pbx", + 1557: "arbortext-lm", + 1558: "xingmpeg", + 1559: "web2host", + 1560: "asci-val", + 1561: "facilityview", + 1562: "pconnectmgr", + 1563: "cadabra-lm", + 1564: "pay-per-view", + 1565: "winddlb", + 1566: "corelvideo", + 1567: "jlicelmd", + 1568: "tsspmap", + 1569: "ets", + 1570: "orbixd", + 1571: "rdb-dbs-disp", + 1572: "chip-lm", + 1573: "itscomm-ns", + 1574: "mvel-lm", + 1575: "oraclenames", + 1576: "moldflow-lm", + 1577: "hypercube-lm", + 1578: "jacobus-lm", + 1579: "ioc-sea-lm", + 1580: "tn-tl-r1", + 1581: "mil-2045-47001", + 1582: "msims", + 1583: "simbaexpress", + 1584: "tn-tl-fd2", + 1585: "intv", + 1586: "ibm-abtact", + 1587: "pra-elmd", + 1588: "triquest-lm", + 1589: "vqp", + 1590: "gemini-lm", + 1591: "ncpm-pm", + 1592: "commonspace", + 1593: "mainsoft-lm", + 1594: "sixtrak", + 1595: "radio", + 1596: "radio-sm", + 1597: "orbplus-iiop", + 1598: "picknfs", + 1599: "simbaservices", + 1600: "issd", + 1601: "aas", + 1602: "inspect", + 1603: "picodbc", + 1604: "icabrowser", + 1605: "slp", + 1606: "slm-api", + 1607: "stt", + 1608: "smart-lm", + 1609: "isysg-lm", + 1610: "taurus-wh", + 1611: "ill", + 1612: "netbill-trans", + 1613: "netbill-keyrep", + 1614: "netbill-cred", + 1615: "netbill-auth", + 1616: "netbill-prod", + 1617: "nimrod-agent", + 1618: "skytelnet", + 1619: "xs-openstorage", + 1620: "faxportwinport", + 1621: "softdataphone", + 1622: "ontime", + 1623: "jaleosnd", + 1624: "udp-sr-port", + 1625: "svs-omagent", + 1626: "shockwave", + 1627: "t128-gateway", + 1628: "lontalk-norm", + 1629: "lontalk-urgnt", + 1630: "oraclenet8cman", + 1631: "visitview", + 1632: "pammratc", + 1633: "pammrpc", + 1634: "loaprobe", + 1635: "edb-server1", + 1636: "isdc", + 1637: "islc", + 1638: "ismc", + 1639: "cert-initiator", + 1640: "cert-responder", + 1641: "invision", + 1642: "isis-am", + 1643: "isis-ambc", + 1644: "saiseh", + 1645: "sightline", + 1646: "sa-msg-port", + 1647: "rsap", + 1648: "concurrent-lm", + 1649: "kermit", + 1650: "nkd", + 1651: "shiva-confsrvr", + 1652: "xnmp", + 1653: "alphatech-lm", + 1654: "stargatealerts", + 1655: "dec-mbadmin", + 1656: "dec-mbadmin-h", + 1657: "fujitsu-mmpdc", + 1658: "sixnetudr", + 1659: "sg-lm", + 1660: "skip-mc-gikreq", + 1661: "netview-aix-1", + 1662: "netview-aix-2", + 1663: "netview-aix-3", + 1664: "netview-aix-4", + 1665: "netview-aix-5", + 1666: "netview-aix-6", + 1667: "netview-aix-7", + 1668: "netview-aix-8", + 1669: "netview-aix-9", + 1670: "netview-aix-10", + 1671: "netview-aix-11", + 1672: "netview-aix-12", + 1673: "proshare-mc-1", + 1674: "proshare-mc-2", + 1675: "pdp", + 1676: "netcomm1", + 1677: "groupwise", + 1678: "prolink", + 1679: "darcorp-lm", + 1680: "microcom-sbp", + 1681: "sd-elmd", + 1682: "lanyon-lantern", + 1683: "ncpm-hip", + 1684: "snaresecure", + 1685: "n2nremote", + 1686: "cvmon", + 1687: "nsjtp-ctrl", + 1688: "nsjtp-data", + 1689: "firefox", + 1690: "ng-umds", + 1691: "empire-empuma", + 1692: "sstsys-lm", + 1693: "rrirtr", + 1694: "rrimwm", + 1695: "rrilwm", + 1696: "rrifmm", + 1697: "rrisat", + 1698: "rsvp-encap-1", + 1699: "rsvp-encap-2", + 1700: "mps-raft", + 1701: "l2f", + 1702: "deskshare", + 1703: "hb-engine", + 1704: "bcs-broker", + 1705: "slingshot", + 1706: "jetform", + 1707: "vdmplay", + 1708: "gat-lmd", + 1709: "centra", + 1710: "impera", + 1711: "pptconference", + 1712: "registrar", + 1713: "conferencetalk", + 1714: "sesi-lm", + 1715: "houdini-lm", + 1716: "xmsg", + 1717: "fj-hdnet", + 1718: "h323gatedisc", + 1719: "h323gatestat", + 1720: "h323hostcall", + 1721: "caicci", + 1722: "hks-lm", + 1723: "pptp", + 1724: "csbphonemaster", + 1725: "iden-ralp", + 1726: "iberiagames", + 1727: "winddx", + 1728: "telindus", + 1729: "citynl", + 1730: "roketz", + 1731: "msiccp", + 1732: "proxim", + 1733: "siipat", + 1734: "cambertx-lm", + 1735: "privatechat", + 1736: "street-stream", + 1737: "ultimad", + 1738: "gamegen1", + 1739: "webaccess", + 1740: "encore", + 1741: "cisco-net-mgmt", + 1742: "3Com-nsd", + 1743: "cinegrfx-lm", + 1744: "ncpm-ft", + 1745: "remote-winsock", + 1746: "ftrapid-1", + 1747: "ftrapid-2", + 1748: "oracle-em1", + 1749: "aspen-services", + 1750: "sslp", + 1751: "swiftnet", + 1752: "lofr-lm", + 1753: "predatar-comms", + 1754: "oracle-em2", + 1755: "ms-streaming", + 1756: "capfast-lmd", + 1757: "cnhrp", + 1758: "tftp-mcast", + 1759: "spss-lm", + 1760: "www-ldap-gw", + 1761: "cft-0", + 1762: "cft-1", + 1763: "cft-2", + 1764: "cft-3", + 1765: "cft-4", + 1766: "cft-5", + 1767: "cft-6", + 1768: "cft-7", + 1769: "bmc-net-adm", + 1770: "bmc-net-svc", + 1771: "vaultbase", + 1772: "essweb-gw", + 1773: "kmscontrol", + 1774: "global-dtserv", + 1775: "vdab", + 1776: "femis", + 1777: "powerguardian", + 1778: "prodigy-intrnet", + 1779: "pharmasoft", + 1780: "dpkeyserv", + 1781: "answersoft-lm", + 1782: "hp-hcip", + 1784: "finle-lm", + 1785: "windlm", + 1786: "funk-logger", + 1787: "funk-license", + 1788: "psmond", + 1789: "hello", + 1790: "nmsp", + 1791: "ea1", + 1792: "ibm-dt-2", + 1793: "rsc-robot", + 1794: "cera-bcm", + 1795: "dpi-proxy", + 1796: "vocaltec-admin", + 1797: "uma", + 1798: "etp", + 1799: "netrisk", + 1800: "ansys-lm", + 1801: "msmq", + 1802: "concomp1", + 1803: "hp-hcip-gwy", + 1804: "enl", + 1805: "enl-name", + 1806: "musiconline", + 1807: "fhsp", + 1808: "oracle-vp2", + 1809: "oracle-vp1", + 1810: "jerand-lm", + 1811: "scientia-sdb", + 1812: "radius", + 1813: "radius-acct", + 1814: "tdp-suite", + 1815: "mmpft", + 1816: "harp", + 1817: "rkb-oscs", + 1818: "etftp", + 1819: "plato-lm", + 1820: "mcagent", + 1821: "donnyworld", + 1822: "es-elmd", + 1823: "unisys-lm", + 1824: "metrics-pas", + 1825: "direcpc-video", + 1826: "ardt", + 1827: "asi", + 1828: "itm-mcell-u", + 1829: "optika-emedia", + 1830: "net8-cman", + 1831: "myrtle", + 1832: "tht-treasure", + 1833: "udpradio", + 1834: "ardusuni", + 1835: "ardusmul", + 1836: "ste-smsc", + 1837: "csoft1", + 1838: "talnet", + 1839: "netopia-vo1", + 1840: "netopia-vo2", + 1841: "netopia-vo3", + 1842: "netopia-vo4", + 1843: "netopia-vo5", + 1844: "direcpc-dll", + 1845: "altalink", + 1846: "tunstall-pnc", + 1847: "slp-notify", + 1848: "fjdocdist", + 1849: "alpha-sms", + 1850: "gsi", + 1851: "ctcd", + 1852: "virtual-time", + 1853: "vids-avtp", + 1854: "buddy-draw", + 1855: "fiorano-rtrsvc", + 1856: "fiorano-msgsvc", + 1857: "datacaptor", + 1858: "privateark", + 1859: "gammafetchsvr", + 1860: "sunscalar-svc", + 1861: "lecroy-vicp", + 1862: "mysql-cm-agent", + 1863: "msnp", + 1864: "paradym-31port", + 1865: "entp", + 1866: "swrmi", + 1867: "udrive", + 1868: "viziblebrowser", + 1869: "transact", + 1870: "sunscalar-dns", + 1871: "canocentral0", + 1872: "canocentral1", + 1873: "fjmpjps", + 1874: "fjswapsnp", + 1875: "westell-stats", + 1876: "ewcappsrv", + 1877: "hp-webqosdb", + 1878: "drmsmc", + 1879: "nettgain-nms", + 1880: "vsat-control", + 1881: "ibm-mqseries2", + 1882: "ecsqdmn", + 1883: "mqtt", + 1884: "idmaps", + 1885: "vrtstrapserver", + 1886: "leoip", + 1887: "filex-lport", + 1888: "ncconfig", + 1889: "unify-adapter", + 1890: "wilkenlistener", + 1891: "childkey-notif", + 1892: "childkey-ctrl", + 1893: "elad", + 1894: "o2server-port", + 1896: "b-novative-ls", + 1897: "metaagent", + 1898: "cymtec-port", + 1899: "mc2studios", + 1900: "ssdp", + 1901: "fjicl-tep-a", + 1902: "fjicl-tep-b", + 1903: "linkname", + 1904: "fjicl-tep-c", + 1905: "sugp", + 1906: "tpmd", + 1907: "intrastar", + 1908: "dawn", + 1909: "global-wlink", + 1910: "ultrabac", + 1911: "mtp", + 1912: "rhp-iibp", + 1913: "armadp", + 1914: "elm-momentum", + 1915: "facelink", + 1916: "persona", + 1917: "noagent", + 1918: "can-nds", + 1919: "can-dch", + 1920: "can-ferret", + 1921: "noadmin", + 1922: "tapestry", + 1923: "spice", + 1924: "xiip", + 1925: "discovery-port", + 1926: "egs", + 1927: "videte-cipc", + 1928: "emsd-port", + 1929: "bandwiz-system", + 1930: "driveappserver", + 1931: "amdsched", + 1932: "ctt-broker", + 1933: "xmapi", + 1934: "xaapi", + 1935: "macromedia-fcs", + 1936: "jetcmeserver", + 1937: "jwserver", + 1938: "jwclient", + 1939: "jvserver", + 1940: "jvclient", + 1941: "dic-aida", + 1942: "res", + 1943: "beeyond-media", + 1944: "close-combat", + 1945: "dialogic-elmd", + 1946: "tekpls", + 1947: "sentinelsrm", + 1948: "eye2eye", + 1949: "ismaeasdaqlive", + 1950: "ismaeasdaqtest", + 1951: "bcs-lmserver", + 1952: "mpnjsc", + 1953: "rapidbase", + 1954: "abr-api", + 1955: "abr-secure", + 1956: "vrtl-vmf-ds", + 1957: "unix-status", + 1958: "dxadmind", + 1959: "simp-all", + 1960: "nasmanager", + 1961: "bts-appserver", + 1962: "biap-mp", + 1963: "webmachine", + 1964: "solid-e-engine", + 1965: "tivoli-npm", + 1966: "slush", + 1967: "sns-quote", + 1968: "lipsinc", + 1969: "lipsinc1", + 1970: "netop-rc", + 1971: "netop-school", + 1972: "intersys-cache", + 1973: "dlsrap", + 1974: "drp", + 1975: "tcoflashagent", + 1976: "tcoregagent", + 1977: "tcoaddressbook", + 1978: "unisql", + 1979: "unisql-java", + 1980: "pearldoc-xact", + 1981: "p2pq", + 1982: "estamp", + 1983: "lhtp", + 1984: "bb", + 1985: "hsrp", + 1986: "licensedaemon", + 1987: "tr-rsrb-p1", + 1988: "tr-rsrb-p2", + 1989: "tr-rsrb-p3", + 1990: "stun-p1", + 1991: "stun-p2", + 1992: "stun-p3", + 1993: "snmp-tcp-port", + 1994: "stun-port", + 1995: "perf-port", + 1996: "tr-rsrb-port", + 1997: "gdp-port", + 1998: "x25-svc-port", + 1999: "tcp-id-port", + 2000: "cisco-sccp", + 2001: "dc", + 2002: "globe", + 2003: "brutus", + 2004: "mailbox", + 2005: "berknet", + 2006: "invokator", + 2007: "dectalk", + 2008: "conf", + 2009: "news", + 2010: "search", + 2011: "raid-cc", + 2012: "ttyinfo", + 2013: "raid-am", + 2014: "troff", + 2015: "cypress", + 2016: "bootserver", + 2017: "cypress-stat", + 2018: "terminaldb", + 2019: "whosockami", + 2020: "xinupageserver", + 2021: "servexec", + 2022: "down", + 2023: "xinuexpansion3", + 2024: "xinuexpansion4", + 2025: "ellpack", + 2026: "scrabble", + 2027: "shadowserver", + 2028: "submitserver", + 2029: "hsrpv6", + 2030: "device2", + 2031: "mobrien-chat", + 2032: "blackboard", + 2033: "glogger", + 2034: "scoremgr", + 2035: "imsldoc", + 2036: "e-dpnet", + 2037: "applus", + 2038: "objectmanager", + 2039: "prizma", + 2040: "lam", + 2041: "interbase", + 2042: "isis", + 2043: "isis-bcast", + 2044: "rimsl", + 2045: "cdfunc", + 2046: "sdfunc", + 2047: "dls", + 2048: "dls-monitor", + 2049: "shilp", + 2050: "av-emb-config", + 2051: "epnsdp", + 2052: "clearvisn", + 2053: "lot105-ds-upd", + 2054: "weblogin", + 2055: "iop", + 2056: "omnisky", + 2057: "rich-cp", + 2058: "newwavesearch", + 2059: "bmc-messaging", + 2060: "teleniumdaemon", + 2061: "netmount", + 2062: "icg-swp", + 2063: "icg-bridge", + 2064: "icg-iprelay", + 2065: "dlsrpn", + 2066: "aura", + 2067: "dlswpn", + 2068: "avauthsrvprtcl", + 2069: "event-port", + 2070: "ah-esp-encap", + 2071: "acp-port", + 2072: "msync", + 2073: "gxs-data-port", + 2074: "vrtl-vmf-sa", + 2075: "newlixengine", + 2076: "newlixconfig", + 2077: "tsrmagt", + 2078: "tpcsrvr", + 2079: "idware-router", + 2080: "autodesk-nlm", + 2081: "kme-trap-port", + 2082: "infowave", + 2083: "radsec", + 2084: "sunclustergeo", + 2085: "ada-cip", + 2086: "gnunet", + 2087: "eli", + 2088: "ip-blf", + 2089: "sep", + 2090: "lrp", + 2091: "prp", + 2092: "descent3", + 2093: "nbx-cc", + 2094: "nbx-au", + 2095: "nbx-ser", + 2096: "nbx-dir", + 2097: "jetformpreview", + 2098: "dialog-port", + 2099: "h2250-annex-g", + 2100: "amiganetfs", + 2101: "rtcm-sc104", + 2102: "zephyr-srv", + 2103: "zephyr-clt", + 2104: "zephyr-hm", + 2105: "minipay", + 2106: "mzap", + 2107: "bintec-admin", + 2108: "comcam", + 2109: "ergolight", + 2110: "umsp", + 2111: "dsatp", + 2112: "idonix-metanet", + 2113: "hsl-storm", + 2114: "newheights", + 2115: "kdm", + 2116: "ccowcmr", + 2117: "mentaclient", + 2118: "mentaserver", + 2119: "gsigatekeeper", + 2120: "qencp", + 2121: "scientia-ssdb", + 2122: "caupc-remote", + 2123: "gtp-control", + 2124: "elatelink", + 2125: "lockstep", + 2126: "pktcable-cops", + 2127: "index-pc-wb", + 2128: "net-steward", + 2129: "cs-live", + 2130: "xds", + 2131: "avantageb2b", + 2132: "solera-epmap", + 2133: "zymed-zpp", + 2134: "avenue", + 2135: "gris", + 2136: "appworxsrv", + 2137: "connect", + 2138: "unbind-cluster", + 2139: "ias-auth", + 2140: "ias-reg", + 2141: "ias-admind", + 2142: "tdmoip", + 2143: "lv-jc", + 2144: "lv-ffx", + 2145: "lv-pici", + 2146: "lv-not", + 2147: "lv-auth", + 2148: "veritas-ucl", + 2149: "acptsys", + 2150: "dynamic3d", + 2151: "docent", + 2152: "gtp-user", + 2153: "ctlptc", + 2154: "stdptc", + 2155: "brdptc", + 2156: "trp", + 2157: "xnds", + 2158: "touchnetplus", + 2159: "gdbremote", + 2160: "apc-2160", + 2161: "apc-2161", + 2162: "navisphere", + 2163: "navisphere-sec", + 2164: "ddns-v3", + 2165: "x-bone-api", + 2166: "iwserver", + 2167: "raw-serial", + 2168: "easy-soft-mux", + 2169: "brain", + 2170: "eyetv", + 2171: "msfw-storage", + 2172: "msfw-s-storage", + 2173: "msfw-replica", + 2174: "msfw-array", + 2175: "airsync", + 2176: "rapi", + 2177: "qwave", + 2178: "bitspeer", + 2179: "vmrdp", + 2180: "mc-gt-srv", + 2181: "eforward", + 2182: "cgn-stat", + 2183: "cgn-config", + 2184: "nvd", + 2185: "onbase-dds", + 2186: "gtaua", + 2187: "ssmc", + 2188: "radware-rpm", + 2189: "radware-rpm-s", + 2190: "tivoconnect", + 2191: "tvbus", + 2192: "asdis", + 2193: "drwcs", + 2197: "mnp-exchange", + 2198: "onehome-remote", + 2199: "onehome-help", + 2200: "ici", + 2201: "ats", + 2202: "imtc-map", + 2203: "b2-runtime", + 2204: "b2-license", + 2205: "jps", + 2206: "hpocbus", + 2207: "hpssd", + 2208: "hpiod", + 2209: "rimf-ps", + 2210: "noaaport", + 2211: "emwin", + 2212: "leecoposserver", + 2213: "kali", + 2214: "rpi", + 2215: "ipcore", + 2216: "vtu-comms", + 2217: "gotodevice", + 2218: "bounzza", + 2219: "netiq-ncap", + 2220: "netiq", + 2221: "ethernet-ip-s", + 2222: "EtherNet-IP-1", + 2223: "rockwell-csp2", + 2224: "efi-mg", + 2225: "rcip-itu", + 2226: "di-drm", + 2227: "di-msg", + 2228: "ehome-ms", + 2229: "datalens", + 2230: "queueadm", + 2231: "wimaxasncp", + 2232: "ivs-video", + 2233: "infocrypt", + 2234: "directplay", + 2235: "sercomm-wlink", + 2236: "nani", + 2237: "optech-port1-lm", + 2238: "aviva-sna", + 2239: "imagequery", + 2240: "recipe", + 2241: "ivsd", + 2242: "foliocorp", + 2243: "magicom", + 2244: "nmsserver", + 2245: "hao", + 2246: "pc-mta-addrmap", + 2247: "antidotemgrsvr", + 2248: "ums", + 2249: "rfmp", + 2250: "remote-collab", + 2251: "dif-port", + 2252: "njenet-ssl", + 2253: "dtv-chan-req", + 2254: "seispoc", + 2255: "vrtp", + 2256: "pcc-mfp", + 2257: "simple-tx-rx", + 2258: "rcts", + 2260: "apc-2260", + 2261: "comotionmaster", + 2262: "comotionback", + 2263: "ecwcfg", + 2264: "apx500api-1", + 2265: "apx500api-2", + 2266: "mfserver", + 2267: "ontobroker", + 2268: "amt", + 2269: "mikey", + 2270: "starschool", + 2271: "mmcals", + 2272: "mmcal", + 2273: "mysql-im", + 2274: "pcttunnell", + 2275: "ibridge-data", + 2276: "ibridge-mgmt", + 2277: "bluectrlproxy", + 2278: "s3db", + 2279: "xmquery", + 2280: "lnvpoller", + 2281: "lnvconsole", + 2282: "lnvalarm", + 2283: "lnvstatus", + 2284: "lnvmaps", + 2285: "lnvmailmon", + 2286: "nas-metering", + 2287: "dna", + 2288: "netml", + 2289: "dict-lookup", + 2290: "sonus-logging", + 2291: "eapsp", + 2292: "mib-streaming", + 2293: "npdbgmngr", + 2294: "konshus-lm", + 2295: "advant-lm", + 2296: "theta-lm", + 2297: "d2k-datamover1", + 2298: "d2k-datamover2", + 2299: "pc-telecommute", + 2300: "cvmmon", + 2301: "cpq-wbem", + 2302: "binderysupport", + 2303: "proxy-gateway", + 2304: "attachmate-uts", + 2305: "mt-scaleserver", + 2306: "tappi-boxnet", + 2307: "pehelp", + 2308: "sdhelp", + 2309: "sdserver", + 2310: "sdclient", + 2311: "messageservice", + 2312: "wanscaler", + 2313: "iapp", + 2314: "cr-websystems", + 2315: "precise-sft", + 2316: "sent-lm", + 2317: "attachmate-g32", + 2318: "cadencecontrol", + 2319: "infolibria", + 2320: "siebel-ns", + 2321: "rdlap", + 2322: "ofsd", + 2323: "3d-nfsd", + 2324: "cosmocall", + 2325: "ansysli", + 2326: "idcp", + 2327: "xingcsm", + 2328: "netrix-sftm", + 2329: "nvd", + 2330: "tscchat", + 2331: "agentview", + 2332: "rcc-host", + 2333: "snapp", + 2334: "ace-client", + 2335: "ace-proxy", + 2336: "appleugcontrol", + 2337: "ideesrv", + 2338: "norton-lambert", + 2339: "3com-webview", + 2340: "wrs-registry", + 2341: "xiostatus", + 2342: "manage-exec", + 2343: "nati-logos", + 2344: "fcmsys", + 2345: "dbm", + 2346: "redstorm-join", + 2347: "redstorm-find", + 2348: "redstorm-info", + 2349: "redstorm-diag", + 2350: "psbserver", + 2351: "psrserver", + 2352: "pslserver", + 2353: "pspserver", + 2354: "psprserver", + 2355: "psdbserver", + 2356: "gxtelmd", + 2357: "unihub-server", + 2358: "futrix", + 2359: "flukeserver", + 2360: "nexstorindltd", + 2361: "tl1", + 2362: "digiman", + 2363: "mediacntrlnfsd", + 2364: "oi-2000", + 2365: "dbref", + 2366: "qip-login", + 2367: "service-ctrl", + 2368: "opentable", + 2370: "l3-hbmon", + 2371: "hp-rda", + 2372: "lanmessenger", + 2373: "remographlm", + 2374: "hydra", + 2375: "docker", + 2376: "docker-s", + 2377: "swarm", + 2379: "etcd-client", + 2380: "etcd-server", + 2381: "compaq-https", + 2382: "ms-olap3", + 2383: "ms-olap4", + 2384: "sd-request", + 2385: "sd-data", + 2386: "virtualtape", + 2387: "vsamredirector", + 2388: "mynahautostart", + 2389: "ovsessionmgr", + 2390: "rsmtp", + 2391: "3com-net-mgmt", + 2392: "tacticalauth", + 2393: "ms-olap1", + 2394: "ms-olap2", + 2395: "lan900-remote", + 2396: "wusage", + 2397: "ncl", + 2398: "orbiter", + 2399: "fmpro-fdal", + 2400: "opequus-server", + 2401: "cvspserver", + 2402: "taskmaster2000", + 2403: "taskmaster2000", + 2404: "iec-104", + 2405: "trc-netpoll", + 2406: "jediserver", + 2407: "orion", + 2408: "railgun-webaccl", + 2409: "sns-protocol", + 2410: "vrts-registry", + 2411: "netwave-ap-mgmt", + 2412: "cdn", + 2413: "orion-rmi-reg", + 2414: "beeyond", + 2415: "codima-rtp", + 2416: "rmtserver", + 2417: "composit-server", + 2418: "cas", + 2419: "attachmate-s2s", + 2420: "dslremote-mgmt", + 2421: "g-talk", + 2422: "crmsbits", + 2423: "rnrp", + 2424: "kofax-svr", + 2425: "fjitsuappmgr", + 2426: "vcmp", + 2427: "mgcp-gateway", + 2428: "ott", + 2429: "ft-role", + 2430: "venus", + 2431: "venus-se", + 2432: "codasrv", + 2433: "codasrv-se", + 2434: "pxc-epmap", + 2435: "optilogic", + 2436: "topx", + 2437: "unicontrol", + 2438: "msp", + 2439: "sybasedbsynch", + 2440: "spearway", + 2441: "pvsw-inet", + 2442: "netangel", + 2443: "powerclientcsf", + 2444: "btpp2sectrans", + 2445: "dtn1", + 2446: "bues-service", + 2447: "ovwdb", + 2448: "hpppssvr", + 2449: "ratl", + 2450: "netadmin", + 2451: "netchat", + 2452: "snifferclient", + 2453: "madge-ltd", + 2454: "indx-dds", + 2455: "wago-io-system", + 2456: "altav-remmgt", + 2457: "rapido-ip", + 2458: "griffin", + 2459: "community", + 2460: "ms-theater", + 2461: "qadmifoper", + 2462: "qadmifevent", + 2463: "lsi-raid-mgmt", + 2464: "direcpc-si", + 2465: "lbm", + 2466: "lbf", + 2467: "high-criteria", + 2468: "qip-msgd", + 2469: "mti-tcs-comm", + 2470: "taskman-port", + 2471: "seaodbc", + 2472: "c3", + 2473: "aker-cdp", + 2474: "vitalanalysis", + 2475: "ace-server", + 2476: "ace-svr-prop", + 2477: "ssm-cvs", + 2478: "ssm-cssps", + 2479: "ssm-els", + 2480: "powerexchange", + 2481: "giop", + 2482: "giop-ssl", + 2483: "ttc", + 2484: "ttc-ssl", + 2485: "netobjects1", + 2486: "netobjects2", + 2487: "pns", + 2488: "moy-corp", + 2489: "tsilb", + 2490: "qip-qdhcp", + 2491: "conclave-cpp", + 2492: "groove", + 2493: "talarian-mqs", + 2494: "bmc-ar", + 2495: "fast-rem-serv", + 2496: "dirgis", + 2497: "quaddb", + 2498: "odn-castraq", + 2499: "unicontrol", + 2500: "rtsserv", + 2501: "rtsclient", + 2502: "kentrox-prot", + 2503: "nms-dpnss", + 2504: "wlbs", + 2505: "ppcontrol", + 2506: "jbroker", + 2507: "spock", + 2508: "jdatastore", + 2509: "fjmpss", + 2510: "fjappmgrbulk", + 2511: "metastorm", + 2512: "citrixima", + 2513: "citrixadmin", + 2514: "facsys-ntp", + 2515: "facsys-router", + 2516: "maincontrol", + 2517: "call-sig-trans", + 2518: "willy", + 2519: "globmsgsvc", + 2520: "pvsw", + 2521: "adaptecmgr", + 2522: "windb", + 2523: "qke-llc-v3", + 2524: "optiwave-lm", + 2525: "ms-v-worlds", + 2526: "ema-sent-lm", + 2527: "iqserver", + 2528: "ncr-ccl", + 2529: "utsftp", + 2530: "vrcommerce", + 2531: "ito-e-gui", + 2532: "ovtopmd", + 2533: "snifferserver", + 2534: "combox-web-acc", + 2535: "madcap", + 2536: "btpp2audctr1", + 2537: "upgrade", + 2538: "vnwk-prapi", + 2539: "vsiadmin", + 2540: "lonworks", + 2541: "lonworks2", + 2542: "udrawgraph", + 2543: "reftek", + 2544: "novell-zen", + 2545: "sis-emt", + 2546: "vytalvaultbrtp", + 2547: "vytalvaultvsmp", + 2548: "vytalvaultpipe", + 2549: "ipass", + 2550: "ads", + 2551: "isg-uda-server", + 2552: "call-logging", + 2553: "efidiningport", + 2554: "vcnet-link-v10", + 2555: "compaq-wcp", + 2556: "nicetec-nmsvc", + 2557: "nicetec-mgmt", + 2558: "pclemultimedia", + 2559: "lstp", + 2560: "labrat", + 2561: "mosaixcc", + 2562: "delibo", + 2563: "cti-redwood", + 2564: "hp-3000-telnet", + 2565: "coord-svr", + 2566: "pcs-pcw", + 2567: "clp", + 2568: "spamtrap", + 2569: "sonuscallsig", + 2570: "hs-port", + 2571: "cecsvc", + 2572: "ibp", + 2573: "trustestablish", + 2574: "blockade-bpsp", + 2575: "hl7", + 2576: "tclprodebugger", + 2577: "scipticslsrvr", + 2578: "rvs-isdn-dcp", + 2579: "mpfoncl", + 2580: "tributary", + 2581: "argis-te", + 2582: "argis-ds", + 2583: "mon", + 2584: "cyaserv", + 2585: "netx-server", + 2586: "netx-agent", + 2587: "masc", + 2588: "privilege", + 2589: "quartus-tcl", + 2590: "idotdist", + 2591: "maytagshuffle", + 2592: "netrek", + 2593: "mns-mail", + 2594: "dts", + 2595: "worldfusion1", + 2596: "worldfusion2", + 2597: "homesteadglory", + 2598: "citriximaclient", + 2599: "snapd", + 2600: "hpstgmgr", + 2601: "discp-client", + 2602: "discp-server", + 2603: "servicemeter", + 2604: "nsc-ccs", + 2605: "nsc-posa", + 2606: "netmon", + 2607: "connection", + 2608: "wag-service", + 2609: "system-monitor", + 2610: "versa-tek", + 2611: "lionhead", + 2612: "qpasa-agent", + 2613: "smntubootstrap", + 2614: "neveroffline", + 2615: "firepower", + 2616: "appswitch-emp", + 2617: "cmadmin", + 2618: "priority-e-com", + 2619: "bruce", + 2620: "lpsrecommender", + 2621: "miles-apart", + 2622: "metricadbc", + 2623: "lmdp", + 2624: "aria", + 2625: "blwnkl-port", + 2626: "gbjd816", + 2627: "moshebeeri", + 2628: "dict", + 2629: "sitaraserver", + 2630: "sitaramgmt", + 2631: "sitaradir", + 2632: "irdg-post", + 2633: "interintelli", + 2634: "pk-electronics", + 2635: "backburner", + 2636: "solve", + 2637: "imdocsvc", + 2638: "sybaseanywhere", + 2639: "aminet", + 2640: "ami-control", + 2641: "hdl-srv", + 2642: "tragic", + 2643: "gte-samp", + 2644: "travsoft-ipx-t", + 2645: "novell-ipx-cmd", + 2646: "and-lm", + 2647: "syncserver", + 2648: "upsnotifyprot", + 2649: "vpsipport", + 2650: "eristwoguns", + 2651: "ebinsite", + 2652: "interpathpanel", + 2653: "sonus", + 2654: "corel-vncadmin", + 2655: "unglue", + 2656: "kana", + 2657: "sns-dispatcher", + 2658: "sns-admin", + 2659: "sns-query", + 2660: "gcmonitor", + 2661: "olhost", + 2662: "bintec-capi", + 2663: "bintec-tapi", + 2664: "patrol-mq-gm", + 2665: "patrol-mq-nm", + 2666: "extensis", + 2667: "alarm-clock-s", + 2668: "alarm-clock-c", + 2669: "toad", + 2670: "tve-announce", + 2671: "newlixreg", + 2672: "nhserver", + 2673: "firstcall42", + 2674: "ewnn", + 2675: "ttc-etap", + 2676: "simslink", + 2677: "gadgetgate1way", + 2678: "gadgetgate2way", + 2679: "syncserverssl", + 2680: "pxc-sapxom", + 2681: "mpnjsomb", + 2683: "ncdloadbalance", + 2684: "mpnjsosv", + 2685: "mpnjsocl", + 2686: "mpnjsomg", + 2687: "pq-lic-mgmt", + 2688: "md-cg-http", + 2689: "fastlynx", + 2690: "hp-nnm-data", + 2691: "itinternet", + 2692: "admins-lms", + 2694: "pwrsevent", + 2695: "vspread", + 2696: "unifyadmin", + 2697: "oce-snmp-trap", + 2698: "mck-ivpip", + 2699: "csoft-plusclnt", + 2700: "tqdata", + 2701: "sms-rcinfo", + 2702: "sms-xfer", + 2703: "sms-chat", + 2704: "sms-remctrl", + 2705: "sds-admin", + 2706: "ncdmirroring", + 2707: "emcsymapiport", + 2708: "banyan-net", + 2709: "supermon", + 2710: "sso-service", + 2711: "sso-control", + 2712: "aocp", + 2713: "raventbs", + 2714: "raventdm", + 2715: "hpstgmgr2", + 2716: "inova-ip-disco", + 2717: "pn-requester", + 2718: "pn-requester2", + 2719: "scan-change", + 2720: "wkars", + 2721: "smart-diagnose", + 2722: "proactivesrvr", + 2723: "watchdog-nt", + 2724: "qotps", + 2725: "msolap-ptp2", + 2726: "tams", + 2727: "mgcp-callagent", + 2728: "sqdr", + 2729: "tcim-control", + 2730: "nec-raidplus", + 2731: "fyre-messanger", + 2732: "g5m", + 2733: "signet-ctf", + 2734: "ccs-software", + 2735: "netiq-mc", + 2736: "radwiz-nms-srv", + 2737: "srp-feedback", + 2738: "ndl-tcp-ois-gw", + 2739: "tn-timing", + 2740: "alarm", + 2741: "tsb", + 2742: "tsb2", + 2743: "murx", + 2744: "honyaku", + 2745: "urbisnet", + 2746: "cpudpencap", + 2747: "fjippol-swrly", + 2748: "fjippol-polsvr", + 2749: "fjippol-cnsl", + 2750: "fjippol-port1", + 2751: "fjippol-port2", + 2752: "rsisysaccess", + 2753: "de-spot", + 2754: "apollo-cc", + 2755: "expresspay", + 2756: "simplement-tie", + 2757: "cnrp", + 2758: "apollo-status", + 2759: "apollo-gms", + 2760: "sabams", + 2761: "dicom-iscl", + 2762: "dicom-tls", + 2763: "desktop-dna", + 2764: "data-insurance", + 2765: "qip-audup", + 2766: "compaq-scp", + 2767: "uadtc", + 2768: "uacs", + 2769: "exce", + 2770: "veronica", + 2771: "vergencecm", + 2772: "auris", + 2773: "rbakcup1", + 2774: "rbakcup2", + 2775: "smpp", + 2776: "ridgeway1", + 2777: "ridgeway2", + 2778: "gwen-sonya", + 2779: "lbc-sync", + 2780: "lbc-control", + 2781: "whosells", + 2782: "everydayrc", + 2783: "aises", + 2784: "www-dev", + 2785: "aic-np", + 2786: "aic-oncrpc", + 2787: "piccolo", + 2788: "fryeserv", + 2789: "media-agent", + 2790: "plgproxy", + 2791: "mtport-regist", + 2792: "f5-globalsite", + 2793: "initlsmsad", + 2795: "livestats", + 2796: "ac-tech", + 2797: "esp-encap", + 2798: "tmesis-upshot", + 2799: "icon-discover", + 2800: "acc-raid", + 2801: "igcp", + 2802: "veritas-tcp1", + 2803: "btprjctrl", + 2804: "dvr-esm", + 2805: "wta-wsp-s", + 2806: "cspuni", + 2807: "cspmulti", + 2808: "j-lan-p", + 2809: "corbaloc", + 2810: "netsteward", + 2811: "gsiftp", + 2812: "atmtcp", + 2813: "llm-pass", + 2814: "llm-csv", + 2815: "lbc-measure", + 2816: "lbc-watchdog", + 2817: "nmsigport", + 2818: "rmlnk", + 2819: "fc-faultnotify", + 2820: "univision", + 2821: "vrts-at-port", + 2822: "ka0wuc", + 2823: "cqg-netlan", + 2824: "cqg-netlan-1", + 2826: "slc-systemlog", + 2827: "slc-ctrlrloops", + 2828: "itm-lm", + 2829: "silkp1", + 2830: "silkp2", + 2831: "silkp3", + 2832: "silkp4", + 2833: "glishd", + 2834: "evtp", + 2835: "evtp-data", + 2836: "catalyst", + 2837: "repliweb", + 2838: "starbot", + 2839: "nmsigport", + 2840: "l3-exprt", + 2841: "l3-ranger", + 2842: "l3-hawk", + 2843: "pdnet", + 2844: "bpcp-poll", + 2845: "bpcp-trap", + 2846: "aimpp-hello", + 2847: "aimpp-port-req", + 2848: "amt-blc-port", + 2849: "fxp", + 2850: "metaconsole", + 2851: "webemshttp", + 2852: "bears-01", + 2853: "ispipes", + 2854: "infomover", + 2855: "msrp", + 2856: "cesdinv", + 2857: "simctlp", + 2858: "ecnp", + 2859: "activememory", + 2860: "dialpad-voice1", + 2861: "dialpad-voice2", + 2862: "ttg-protocol", + 2863: "sonardata", + 2864: "astromed-main", + 2865: "pit-vpn", + 2866: "iwlistener", + 2867: "esps-portal", + 2868: "npep-messaging", + 2869: "icslap", + 2870: "daishi", + 2871: "msi-selectplay", + 2872: "radix", + 2874: "dxmessagebase1", + 2875: "dxmessagebase2", + 2876: "sps-tunnel", + 2877: "bluelance", + 2878: "aap", + 2879: "ucentric-ds", + 2880: "synapse", + 2881: "ndsp", + 2882: "ndtp", + 2883: "ndnp", + 2884: "flashmsg", + 2885: "topflow", + 2886: "responselogic", + 2887: "aironetddp", + 2888: "spcsdlobby", + 2889: "rsom", + 2890: "cspclmulti", + 2891: "cinegrfx-elmd", + 2892: "snifferdata", + 2893: "vseconnector", + 2894: "abacus-remote", + 2895: "natuslink", + 2896: "ecovisiong6-1", + 2897: "citrix-rtmp", + 2898: "appliance-cfg", + 2899: "powergemplus", + 2900: "quicksuite", + 2901: "allstorcns", + 2902: "netaspi", + 2903: "suitcase", + 2904: "m2ua", + 2905: "m3ua", + 2906: "caller9", + 2907: "webmethods-b2b", + 2908: "mao", + 2909: "funk-dialout", + 2910: "tdaccess", + 2911: "blockade", + 2912: "epicon", + 2913: "boosterware", + 2914: "gamelobby", + 2915: "tksocket", + 2916: "elvin-server", + 2917: "elvin-client", + 2918: "kastenchasepad", + 2919: "roboer", + 2920: "roboeda", + 2921: "cesdcdman", + 2922: "cesdcdtrn", + 2923: "wta-wsp-wtp-s", + 2924: "precise-vip", + 2926: "mobile-file-dl", + 2927: "unimobilectrl", + 2928: "redstone-cpss", + 2929: "amx-webadmin", + 2930: "amx-weblinx", + 2931: "circle-x", + 2932: "incp", + 2933: "4-tieropmgw", + 2934: "4-tieropmcli", + 2935: "qtp", + 2936: "otpatch", + 2937: "pnaconsult-lm", + 2938: "sm-pas-1", + 2939: "sm-pas-2", + 2940: "sm-pas-3", + 2941: "sm-pas-4", + 2942: "sm-pas-5", + 2943: "ttnrepository", + 2944: "megaco-h248", + 2945: "h248-binary", + 2946: "fjsvmpor", + 2947: "gpsd", + 2948: "wap-push", + 2949: "wap-pushsecure", + 2950: "esip", + 2951: "ottp", + 2952: "mpfwsas", + 2953: "ovalarmsrv", + 2954: "ovalarmsrv-cmd", + 2955: "csnotify", + 2956: "ovrimosdbman", + 2957: "jmact5", + 2958: "jmact6", + 2959: "rmopagt", + 2960: "dfoxserver", + 2961: "boldsoft-lm", + 2962: "iph-policy-cli", + 2963: "iph-policy-adm", + 2964: "bullant-srap", + 2965: "bullant-rap", + 2966: "idp-infotrieve", + 2967: "ssc-agent", + 2968: "enpp", + 2969: "essp", + 2970: "index-net", + 2971: "netclip", + 2972: "pmsm-webrctl", + 2973: "svnetworks", + 2974: "signal", + 2975: "fjmpcm", + 2976: "cns-srv-port", + 2977: "ttc-etap-ns", + 2978: "ttc-etap-ds", + 2979: "h263-video", + 2980: "wimd", + 2981: "mylxamport", + 2982: "iwb-whiteboard", + 2983: "netplan", + 2984: "hpidsadmin", + 2985: "hpidsagent", + 2986: "stonefalls", + 2987: "identify", + 2988: "hippad", + 2989: "zarkov", + 2990: "boscap", + 2991: "wkstn-mon", + 2992: "avenyo", + 2993: "veritas-vis1", + 2994: "veritas-vis2", + 2995: "idrs", + 2996: "vsixml", + 2997: "rebol", + 2998: "realsecure", + 2999: "remoteware-un", + 3000: "hbci", + 3001: "origo-native", + 3002: "exlm-agent", + 3003: "cgms", + 3004: "csoftragent", + 3005: "geniuslm", + 3006: "ii-admin", + 3007: "lotusmtap", + 3008: "midnight-tech", + 3009: "pxc-ntfy", + 3010: "gw", + 3011: "trusted-web", + 3012: "twsdss", + 3013: "gilatskysurfer", + 3014: "broker-service", + 3015: "nati-dstp", + 3016: "notify-srvr", + 3017: "event-listener", + 3018: "srvc-registry", + 3019: "resource-mgr", + 3020: "cifs", + 3021: "agriserver", + 3022: "csregagent", + 3023: "magicnotes", + 3024: "nds-sso", + 3025: "arepa-raft", + 3026: "agri-gateway", + 3027: "LiebDevMgmt-C", + 3028: "LiebDevMgmt-DM", + 3029: "LiebDevMgmt-A", + 3030: "arepa-cas", + 3031: "eppc", + 3032: "redwood-chat", + 3033: "pdb", + 3034: "osmosis-aeea", + 3035: "fjsv-gssagt", + 3036: "hagel-dump", + 3037: "hp-san-mgmt", + 3038: "santak-ups", + 3039: "cogitate", + 3040: "tomato-springs", + 3041: "di-traceware", + 3042: "journee", + 3043: "brp", + 3044: "epp", + 3045: "responsenet", + 3046: "di-ase", + 3047: "hlserver", + 3048: "pctrader", + 3049: "nsws", + 3050: "gds-db", + 3051: "galaxy-server", + 3052: "apc-3052", + 3053: "dsom-server", + 3054: "amt-cnf-prot", + 3055: "policyserver", + 3056: "cdl-server", + 3057: "goahead-fldup", + 3058: "videobeans", + 3059: "qsoft", + 3060: "interserver", + 3061: "cautcpd", + 3062: "ncacn-ip-tcp", + 3063: "ncadg-ip-udp", + 3064: "rprt", + 3065: "slinterbase", + 3066: "netattachsdmp", + 3067: "fjhpjp", + 3068: "ls3bcast", + 3069: "ls3", + 3070: "mgxswitch", + 3071: "csd-mgmt-port", + 3072: "csd-monitor", + 3073: "vcrp", + 3074: "xbox", + 3075: "orbix-locator", + 3076: "orbix-config", + 3077: "orbix-loc-ssl", + 3078: "orbix-cfg-ssl", + 3079: "lv-frontpanel", + 3080: "stm-pproc", + 3081: "tl1-lv", + 3082: "tl1-raw", + 3083: "tl1-telnet", + 3084: "itm-mccs", + 3085: "pcihreq", + 3086: "jdl-dbkitchen", + 3087: "asoki-sma", + 3088: "xdtp", + 3089: "ptk-alink", + 3090: "stss", + 3091: "1ci-smcs", + 3093: "rapidmq-center", + 3094: "rapidmq-reg", + 3095: "panasas", + 3096: "ndl-aps", + 3098: "umm-port", + 3099: "chmd", + 3100: "opcon-xps", + 3101: "hp-pxpib", + 3102: "slslavemon", + 3103: "autocuesmi", + 3104: "autocuelog", + 3105: "cardbox", + 3106: "cardbox-http", + 3107: "business", + 3108: "geolocate", + 3109: "personnel", + 3110: "sim-control", + 3111: "wsynch", + 3112: "ksysguard", + 3113: "cs-auth-svr", + 3114: "ccmad", + 3115: "mctet-master", + 3116: "mctet-gateway", + 3117: "mctet-jserv", + 3118: "pkagent", + 3119: "d2000kernel", + 3120: "d2000webserver", + 3121: "pcmk-remote", + 3122: "vtr-emulator", + 3123: "edix", + 3124: "beacon-port", + 3125: "a13-an", + 3127: "ctx-bridge", + 3128: "ndl-aas", + 3129: "netport-id", + 3130: "icpv2", + 3131: "netbookmark", + 3132: "ms-rule-engine", + 3133: "prism-deploy", + 3134: "ecp", + 3135: "peerbook-port", + 3136: "grubd", + 3137: "rtnt-1", + 3138: "rtnt-2", + 3139: "incognitorv", + 3140: "ariliamulti", + 3141: "vmodem", + 3142: "rdc-wh-eos", + 3143: "seaview", + 3144: "tarantella", + 3145: "csi-lfap", + 3146: "bears-02", + 3147: "rfio", + 3148: "nm-game-admin", + 3149: "nm-game-server", + 3150: "nm-asses-admin", + 3151: "nm-assessor", + 3152: "feitianrockey", + 3153: "s8-client-port", + 3154: "ccmrmi", + 3155: "jpegmpeg", + 3156: "indura", + 3157: "e3consultants", + 3158: "stvp", + 3159: "navegaweb-port", + 3160: "tip-app-server", + 3161: "doc1lm", + 3162: "sflm", + 3163: "res-sap", + 3164: "imprs", + 3165: "newgenpay", + 3166: "sossecollector", + 3167: "nowcontact", + 3168: "poweronnud", + 3169: "serverview-as", + 3170: "serverview-asn", + 3171: "serverview-gf", + 3172: "serverview-rm", + 3173: "serverview-icc", + 3174: "armi-server", + 3175: "t1-e1-over-ip", + 3176: "ars-master", + 3177: "phonex-port", + 3178: "radclientport", + 3179: "h2gf-w-2m", + 3180: "mc-brk-srv", + 3181: "bmcpatrolagent", + 3182: "bmcpatrolrnvu", + 3183: "cops-tls", + 3184: "apogeex-port", + 3185: "smpppd", + 3186: "iiw-port", + 3187: "odi-port", + 3188: "brcm-comm-port", + 3189: "pcle-infex", + 3190: "csvr-proxy", + 3191: "csvr-sslproxy", + 3192: "firemonrcc", + 3193: "spandataport", + 3194: "magbind", + 3195: "ncu-1", + 3196: "ncu-2", + 3197: "embrace-dp-s", + 3198: "embrace-dp-c", + 3199: "dmod-workspace", + 3200: "tick-port", + 3201: "cpq-tasksmart", + 3202: "intraintra", + 3203: "netwatcher-mon", + 3204: "netwatcher-db", + 3205: "isns", + 3206: "ironmail", + 3207: "vx-auth-port", + 3208: "pfu-prcallback", + 3209: "netwkpathengine", + 3210: "flamenco-proxy", + 3211: "avsecuremgmt", + 3212: "surveyinst", + 3213: "neon24x7", + 3214: "jmq-daemon-1", + 3215: "jmq-daemon-2", + 3216: "ferrari-foam", + 3217: "unite", + 3218: "smartpackets", + 3219: "wms-messenger", + 3220: "xnm-ssl", + 3221: "xnm-clear-text", + 3222: "glbp", + 3223: "digivote", + 3224: "aes-discovery", + 3225: "fcip-port", + 3226: "isi-irp", + 3227: "dwnmshttp", + 3228: "dwmsgserver", + 3229: "global-cd-port", + 3230: "sftdst-port", + 3231: "vidigo", + 3232: "mdtp", + 3233: "whisker", + 3234: "alchemy", + 3235: "mdap-port", + 3236: "apparenet-ts", + 3237: "apparenet-tps", + 3238: "apparenet-as", + 3239: "apparenet-ui", + 3240: "triomotion", + 3241: "sysorb", + 3242: "sdp-id-port", + 3243: "timelot", + 3244: "onesaf", + 3245: "vieo-fe", + 3246: "dvt-system", + 3247: "dvt-data", + 3248: "procos-lm", + 3249: "ssp", + 3250: "hicp", + 3251: "sysscanner", + 3252: "dhe", + 3253: "pda-data", + 3254: "pda-sys", + 3255: "semaphore", + 3256: "cpqrpm-agent", + 3257: "cpqrpm-server", + 3258: "ivecon-port", + 3259: "epncdp2", + 3260: "iscsi-target", + 3261: "winshadow", + 3262: "necp", + 3263: "ecolor-imager", + 3264: "ccmail", + 3265: "altav-tunnel", + 3266: "ns-cfg-server", + 3267: "ibm-dial-out", + 3268: "msft-gc", + 3269: "msft-gc-ssl", + 3270: "verismart", + 3271: "csoft-prev", + 3272: "user-manager", + 3273: "sxmp", + 3274: "ordinox-server", + 3275: "samd", + 3276: "maxim-asics", + 3277: "awg-proxy", + 3278: "lkcmserver", + 3279: "admind", + 3280: "vs-server", + 3281: "sysopt", + 3282: "datusorb", + 3283: "Apple Remote Desktop (Net Assistant)", + 3284: "4talk", + 3285: "plato", + 3286: "e-net", + 3287: "directvdata", + 3288: "cops", + 3289: "enpc", + 3290: "caps-lm", + 3291: "sah-lm", + 3292: "cart-o-rama", + 3293: "fg-fps", + 3294: "fg-gip", + 3295: "dyniplookup", + 3296: "rib-slm", + 3297: "cytel-lm", + 3298: "deskview", + 3299: "pdrncs", + 3300: "ceph", + 3302: "mcs-fastmail", + 3303: "opsession-clnt", + 3304: "opsession-srvr", + 3305: "odette-ftp", + 3306: "mysql", + 3307: "opsession-prxy", + 3308: "tns-server", + 3309: "tns-adv", + 3310: "dyna-access", + 3311: "mcns-tel-ret", + 3312: "appman-server", + 3313: "uorb", + 3314: "uohost", + 3315: "cdid", + 3316: "aicc-cmi", + 3317: "vsaiport", + 3318: "ssrip", + 3319: "sdt-lmd", + 3320: "officelink2000", + 3321: "vnsstr", + 3326: "sftu", + 3327: "bbars", + 3328: "egptlm", + 3329: "hp-device-disc", + 3330: "mcs-calypsoicf", + 3331: "mcs-messaging", + 3332: "mcs-mailsvr", + 3333: "dec-notes", + 3334: "directv-web", + 3335: "directv-soft", + 3336: "directv-tick", + 3337: "directv-catlg", + 3338: "anet-b", + 3339: "anet-l", + 3340: "anet-m", + 3341: "anet-h", + 3342: "webtie", + 3343: "ms-cluster-net", + 3344: "bnt-manager", + 3345: "influence", + 3346: "trnsprntproxy", + 3347: "phoenix-rpc", + 3348: "pangolin-laser", + 3349: "chevinservices", + 3350: "findviatv", + 3351: "btrieve", + 3352: "ssql", + 3353: "fatpipe", + 3354: "suitjd", + 3355: "ordinox-dbase", + 3356: "upnotifyps", + 3357: "adtech-test", + 3358: "mpsysrmsvr", + 3359: "wg-netforce", + 3360: "kv-server", + 3361: "kv-agent", + 3362: "dj-ilm", + 3363: "nati-vi-server", + 3364: "creativeserver", + 3365: "contentserver", + 3366: "creativepartnr", + 3372: "tip2", + 3373: "lavenir-lm", + 3374: "cluster-disc", + 3375: "vsnm-agent", + 3376: "cdbroker", + 3377: "cogsys-lm", + 3378: "wsicopy", + 3379: "socorfs", + 3380: "sns-channels", + 3381: "geneous", + 3382: "fujitsu-neat", + 3383: "esp-lm", + 3384: "hp-clic", + 3385: "qnxnetman", + 3386: "gprs-data", + 3387: "backroomnet", + 3388: "cbserver", + 3389: "ms-wbt-server", + 3390: "dsc", + 3391: "savant", + 3392: "efi-lm", + 3393: "d2k-tapestry1", + 3394: "d2k-tapestry2", + 3395: "dyna-lm", + 3396: "printer-agent", + 3397: "cloanto-lm", + 3398: "mercantile", + 3399: "csms", + 3400: "csms2", + 3401: "filecast", + 3402: "fxaengine-net", + 3405: "nokia-ann-ch1", + 3406: "nokia-ann-ch2", + 3407: "ldap-admin", + 3408: "BESApi", + 3409: "networklens", + 3410: "networklenss", + 3411: "biolink-auth", + 3412: "xmlblaster", + 3413: "svnet", + 3414: "wip-port", + 3415: "bcinameservice", + 3416: "commandport", + 3417: "csvr", + 3418: "rnmap", + 3419: "softaudit", + 3420: "ifcp-port", + 3421: "bmap", + 3422: "rusb-sys-port", + 3423: "xtrm", + 3424: "xtrms", + 3425: "agps-port", + 3426: "arkivio", + 3427: "websphere-snmp", + 3428: "twcss", + 3429: "gcsp", + 3430: "ssdispatch", + 3431: "ndl-als", + 3432: "osdcp", + 3433: "opnet-smp", + 3434: "opencm", + 3435: "pacom", + 3436: "gc-config", + 3437: "autocueds", + 3438: "spiral-admin", + 3439: "hri-port", + 3440: "ans-console", + 3441: "connect-client", + 3442: "connect-server", + 3443: "ov-nnm-websrv", + 3444: "denali-server", + 3445: "monp", + 3446: "3comfaxrpc", + 3447: "directnet", + 3448: "dnc-port", + 3449: "hotu-chat", + 3450: "castorproxy", + 3451: "asam", + 3452: "sabp-signal", + 3453: "pscupd", + 3454: "mira", + 3455: "prsvp", + 3456: "vat", + 3457: "vat-control", + 3458: "d3winosfi", + 3459: "integral", + 3460: "edm-manager", + 3461: "edm-stager", + 3462: "edm-std-notify", + 3463: "edm-adm-notify", + 3464: "edm-mgr-sync", + 3465: "edm-mgr-cntrl", + 3466: "workflow", + 3467: "rcst", + 3468: "ttcmremotectrl", + 3469: "pluribus", + 3470: "jt400", + 3471: "jt400-ssl", + 3472: "jaugsremotec-1", + 3473: "jaugsremotec-2", + 3474: "ttntspauto", + 3475: "genisar-port", + 3476: "nppmp", + 3477: "ecomm", + 3478: "stun", + 3479: "twrpc", + 3480: "plethora", + 3481: "cleanerliverc", + 3482: "vulture", + 3483: "slim-devices", + 3484: "gbs-stp", + 3485: "celatalk", + 3486: "ifsf-hb-port", + 3487: "ltctcp", + 3488: "fs-rh-srv", + 3489: "dtp-dia", + 3490: "colubris", + 3491: "swr-port", + 3492: "tvdumtray-port", + 3493: "nut", + 3494: "ibm3494", + 3495: "seclayer-tcp", + 3496: "seclayer-tls", + 3497: "ipether232port", + 3498: "dashpas-port", + 3499: "sccip-media", + 3500: "rtmp-port", + 3501: "isoft-p2p", + 3502: "avinstalldisc", + 3503: "lsp-ping", + 3504: "ironstorm", + 3505: "ccmcomm", + 3506: "apc-3506", + 3507: "nesh-broker", + 3508: "interactionweb", + 3509: "vt-ssl", + 3510: "xss-port", + 3511: "webmail-2", + 3512: "aztec", + 3513: "arcpd", + 3514: "must-p2p", + 3515: "must-backplane", + 3516: "smartcard-port", + 3517: "802-11-iapp", + 3518: "artifact-msg", + 3519: "nvmsgd", + 3520: "galileolog", + 3521: "mc3ss", + 3522: "nssocketport", + 3523: "odeumservlink", + 3524: "ecmport", + 3525: "eisport", + 3526: "starquiz-port", + 3527: "beserver-msg-q", + 3528: "jboss-iiop", + 3529: "jboss-iiop-ssl", + 3530: "gf", + 3531: "joltid", + 3532: "raven-rmp", + 3533: "raven-rdp", + 3534: "urld-port", + 3535: "ms-la", + 3536: "snac", + 3537: "ni-visa-remote", + 3538: "ibm-diradm", + 3539: "ibm-diradm-ssl", + 3540: "pnrp-port", + 3541: "voispeed-port", + 3542: "hacl-monitor", + 3543: "qftest-lookup", + 3544: "teredo", + 3545: "camac", + 3547: "symantec-sim", + 3548: "interworld", + 3549: "tellumat-nms", + 3550: "ssmpp", + 3551: "apcupsd", + 3552: "taserver", + 3553: "rbr-discovery", + 3554: "questnotify", + 3555: "razor", + 3556: "sky-transport", + 3557: "personalos-001", + 3558: "mcp-port", + 3559: "cctv-port", + 3560: "iniserve-port", + 3561: "bmc-onekey", + 3562: "sdbproxy", + 3563: "watcomdebug", + 3564: "esimport", + 3565: "m2pa", + 3566: "quest-data-hub", + 3567: "dof-eps", + 3568: "dof-tunnel-sec", + 3569: "mbg-ctrl", + 3570: "mccwebsvr-port", + 3571: "megardsvr-port", + 3572: "megaregsvrport", + 3573: "tag-ups-1", + 3574: "dmaf-server", + 3575: "ccm-port", + 3576: "cmc-port", + 3577: "config-port", + 3578: "data-port", + 3579: "ttat3lb", + 3580: "nati-svrloc", + 3581: "kfxaclicensing", + 3582: "press", + 3583: "canex-watch", + 3584: "u-dbap", + 3585: "emprise-lls", + 3586: "emprise-lsc", + 3587: "p2pgroup", + 3588: "sentinel", + 3589: "isomair", + 3590: "wv-csp-sms", + 3591: "gtrack-server", + 3592: "gtrack-ne", + 3593: "bpmd", + 3594: "mediaspace", + 3595: "shareapp", + 3596: "iw-mmogame", + 3597: "a14", + 3598: "a15", + 3599: "quasar-server", + 3600: "trap-daemon", + 3601: "visinet-gui", + 3602: "infiniswitchcl", + 3603: "int-rcv-cntrl", + 3604: "bmc-jmx-port", + 3605: "comcam-io", + 3606: "splitlock", + 3607: "precise-i3", + 3608: "trendchip-dcp", + 3609: "cpdi-pidas-cm", + 3610: "echonet", + 3611: "six-degrees", + 3612: "hp-dataprotect", + 3613: "alaris-disc", + 3614: "sigma-port", + 3615: "start-network", + 3616: "cd3o-protocol", + 3617: "sharp-server", + 3618: "aairnet-1", + 3619: "aairnet-2", + 3620: "ep-pcp", + 3621: "ep-nsp", + 3622: "ff-lr-port", + 3623: "haipe-discover", + 3624: "dist-upgrade", + 3625: "volley", + 3626: "bvcdaemon-port", + 3627: "jamserverport", + 3628: "ept-machine", + 3629: "escvpnet", + 3630: "cs-remote-db", + 3631: "cs-services", + 3632: "distcc", + 3633: "wacp", + 3634: "hlibmgr", + 3635: "sdo", + 3636: "servistaitsm", + 3637: "scservp", + 3638: "ehp-backup", + 3639: "xap-ha", + 3640: "netplay-port1", + 3641: "netplay-port2", + 3642: "juxml-port", + 3643: "audiojuggler", + 3644: "ssowatch", + 3645: "cyc", + 3646: "xss-srv-port", + 3647: "splitlock-gw", + 3648: "fjcp", + 3649: "nmmp", + 3650: "prismiq-plugin", + 3651: "xrpc-registry", + 3652: "vxcrnbuport", + 3653: "tsp", + 3654: "vaprtm", + 3655: "abatemgr", + 3656: "abatjss", + 3657: "immedianet-bcn", + 3658: "ps-ams", + 3659: "apple-sasl", + 3660: "can-nds-ssl", + 3661: "can-ferret-ssl", + 3662: "pserver", + 3663: "dtp", + 3664: "ups-engine", + 3665: "ent-engine", + 3666: "eserver-pap", + 3667: "infoexch", + 3668: "dell-rm-port", + 3669: "casanswmgmt", + 3670: "smile", + 3671: "efcp", + 3672: "lispworks-orb", + 3673: "mediavault-gui", + 3674: "wininstall-ipc", + 3675: "calltrax", + 3676: "va-pacbase", + 3677: "roverlog", + 3678: "ipr-dglt", + 3679: "Escale (Newton Dock)", + 3680: "npds-tracker", + 3681: "bts-x73", + 3682: "cas-mapi", + 3683: "bmc-ea", + 3684: "faxstfx-port", + 3685: "dsx-agent", + 3686: "tnmpv2", + 3687: "simple-push", + 3688: "simple-push-s", + 3689: "daap", + 3690: "svn", + 3691: "magaya-network", + 3692: "intelsync", + 3693: "easl", + 3695: "bmc-data-coll", + 3696: "telnetcpcd", + 3697: "nw-license", + 3698: "sagectlpanel", + 3699: "kpn-icw", + 3700: "lrs-paging", + 3701: "netcelera", + 3702: "ws-discovery", + 3703: "adobeserver-3", + 3704: "adobeserver-4", + 3705: "adobeserver-5", + 3706: "rt-event", + 3707: "rt-event-s", + 3708: "sun-as-iiops", + 3709: "ca-idms", + 3710: "portgate-auth", + 3711: "edb-server2", + 3712: "sentinel-ent", + 3713: "tftps", + 3714: "delos-dms", + 3715: "anoto-rendezv", + 3716: "wv-csp-sms-cir", + 3717: "wv-csp-udp-cir", + 3718: "opus-services", + 3719: "itelserverport", + 3720: "ufastro-instr", + 3721: "xsync", + 3722: "xserveraid", + 3723: "sychrond", + 3724: "blizwow", + 3725: "na-er-tip", + 3726: "array-manager", + 3727: "e-mdu", + 3728: "e-woa", + 3729: "fksp-audit", + 3730: "client-ctrl", + 3731: "smap", + 3732: "m-wnn", + 3733: "multip-msg", + 3734: "synel-data", + 3735: "pwdis", + 3736: "rs-rmi", + 3737: "xpanel", + 3738: "versatalk", + 3739: "launchbird-lm", + 3740: "heartbeat", + 3741: "wysdma", + 3742: "cst-port", + 3743: "ipcs-command", + 3744: "sasg", + 3745: "gw-call-port", + 3746: "linktest", + 3747: "linktest-s", + 3748: "webdata", + 3749: "cimtrak", + 3750: "cbos-ip-port", + 3751: "gprs-cube", + 3752: "vipremoteagent", + 3753: "nattyserver", + 3754: "timestenbroker", + 3755: "sas-remote-hlp", + 3756: "canon-capt", + 3757: "grf-port", + 3758: "apw-registry", + 3759: "exapt-lmgr", + 3760: "adtempusclient", + 3761: "gsakmp", + 3762: "gbs-smp", + 3763: "xo-wave", + 3764: "mni-prot-rout", + 3765: "rtraceroute", + 3766: "sitewatch-s", + 3767: "listmgr-port", + 3768: "rblcheckd", + 3769: "haipe-otnk", + 3770: "cindycollab", + 3771: "paging-port", + 3772: "ctp", + 3773: "ctdhercules", + 3774: "zicom", + 3775: "ispmmgr", + 3776: "dvcprov-port", + 3777: "jibe-eb", + 3778: "c-h-it-port", + 3779: "cognima", + 3780: "nnp", + 3781: "abcvoice-port", + 3782: "iso-tp0s", + 3783: "bim-pem", + 3784: "bfd-control", + 3785: "bfd-echo", + 3786: "upstriggervsw", + 3787: "fintrx", + 3788: "isrp-port", + 3789: "remotedeploy", + 3790: "quickbooksrds", + 3791: "tvnetworkvideo", + 3792: "sitewatch", + 3793: "dcsoftware", + 3794: "jaus", + 3795: "myblast", + 3796: "spw-dialer", + 3797: "idps", + 3798: "minilock", + 3799: "radius-dynauth", + 3800: "pwgpsi", + 3801: "ibm-mgr", + 3802: "vhd", + 3803: "soniqsync", + 3804: "iqnet-port", + 3805: "tcpdataserver", + 3806: "wsmlb", + 3807: "spugna", + 3808: "sun-as-iiops-ca", + 3809: "apocd", + 3810: "wlanauth", + 3811: "amp", + 3812: "neto-wol-server", + 3813: "rap-ip", + 3814: "neto-dcs", + 3815: "lansurveyorxml", + 3816: "sunlps-http", + 3817: "tapeware", + 3818: "crinis-hb", + 3819: "epl-slp", + 3820: "scp", + 3821: "pmcp", + 3822: "acp-discovery", + 3823: "acp-conduit", + 3824: "acp-policy", + 3825: "ffserver", + 3826: "warmux", + 3827: "netmpi", + 3828: "neteh", + 3829: "neteh-ext", + 3830: "cernsysmgmtagt", + 3831: "dvapps", + 3832: "xxnetserver", + 3833: "aipn-auth", + 3834: "spectardata", + 3835: "spectardb", + 3836: "markem-dcp", + 3837: "mkm-discovery", + 3838: "sos", + 3839: "amx-rms", + 3840: "flirtmitmir", + 3841: "shiprush-db-svr", + 3842: "nhci", + 3843: "quest-agent", + 3844: "rnm", + 3845: "v-one-spp", + 3846: "an-pcp", + 3847: "msfw-control", + 3848: "item", + 3849: "spw-dnspreload", + 3850: "qtms-bootstrap", + 3851: "spectraport", + 3852: "sse-app-config", + 3853: "sscan", + 3854: "stryker-com", + 3855: "opentrac", + 3856: "informer", + 3857: "trap-port", + 3858: "trap-port-mom", + 3859: "nav-port", + 3860: "sasp", + 3861: "winshadow-hd", + 3862: "giga-pocket", + 3863: "asap-tcp", + 3864: "asap-tcp-tls", + 3865: "xpl", + 3866: "dzdaemon", + 3867: "dzoglserver", + 3868: "diameter", + 3869: "ovsam-mgmt", + 3870: "ovsam-d-agent", + 3871: "avocent-adsap", + 3872: "oem-agent", + 3873: "fagordnc", + 3874: "sixxsconfig", + 3875: "pnbscada", + 3876: "dl-agent", + 3877: "xmpcr-interface", + 3878: "fotogcad", + 3879: "appss-lm", + 3880: "igrs", + 3881: "idac", + 3882: "msdts1", + 3883: "vrpn", + 3884: "softrack-meter", + 3885: "topflow-ssl", + 3886: "nei-management", + 3887: "ciphire-data", + 3888: "ciphire-serv", + 3889: "dandv-tester", + 3890: "ndsconnect", + 3891: "rtc-pm-port", + 3892: "pcc-image-port", + 3893: "cgi-starapi", + 3894: "syam-agent", + 3895: "syam-smc", + 3896: "sdo-tls", + 3897: "sdo-ssh", + 3898: "senip", + 3899: "itv-control", + 3900: "udt-os", + 3901: "nimsh", + 3902: "nimaux", + 3903: "charsetmgr", + 3904: "omnilink-port", + 3905: "mupdate", + 3906: "topovista-data", + 3907: "imoguia-port", + 3908: "hppronetman", + 3909: "surfcontrolcpa", + 3910: "prnrequest", + 3911: "prnstatus", + 3912: "gbmt-stars", + 3913: "listcrt-port", + 3914: "listcrt-port-2", + 3915: "agcat", + 3916: "wysdmc", + 3917: "aftmux", + 3918: "pktcablemmcops", + 3919: "hyperip", + 3920: "exasoftport1", + 3921: "herodotus-net", + 3922: "sor-update", + 3923: "symb-sb-port", + 3924: "mpl-gprs-port", + 3925: "zmp", + 3926: "winport", + 3927: "natdataservice", + 3928: "netboot-pxe", + 3929: "smauth-port", + 3930: "syam-webserver", + 3931: "msr-plugin-port", + 3932: "dyn-site", + 3933: "plbserve-port", + 3934: "sunfm-port", + 3935: "sdp-portmapper", + 3936: "mailprox", + 3937: "dvbservdsc", + 3938: "dbcontrol-agent", + 3939: "aamp", + 3940: "xecp-node", + 3941: "homeportal-web", + 3942: "srdp", + 3943: "tig", + 3944: "sops", + 3945: "emcads", + 3946: "backupedge", + 3947: "ccp", + 3948: "apdap", + 3949: "drip", + 3950: "namemunge", + 3951: "pwgippfax", + 3952: "i3-sessionmgr", + 3953: "xmlink-connect", + 3954: "adrep", + 3955: "p2pcommunity", + 3956: "gvcp", + 3957: "mqe-broker", + 3958: "mqe-agent", + 3959: "treehopper", + 3960: "bess", + 3961: "proaxess", + 3962: "sbi-agent", + 3963: "thrp", + 3964: "sasggprs", + 3965: "ati-ip-to-ncpe", + 3966: "bflckmgr", + 3967: "ppsms", + 3968: "ianywhere-dbns", + 3969: "landmarks", + 3970: "lanrevagent", + 3971: "lanrevserver", + 3972: "iconp", + 3973: "progistics", + 3974: "citysearch", + 3975: "airshot", + 3976: "opswagent", + 3977: "opswmanager", + 3978: "secure-cfg-svr", + 3979: "smwan", + 3980: "acms", + 3981: "starfish", + 3982: "eis", + 3983: "eisp", + 3984: "mapper-nodemgr", + 3985: "mapper-mapethd", + 3986: "mapper-ws-ethd", + 3987: "centerline", + 3988: "dcs-config", + 3989: "bv-queryengine", + 3990: "bv-is", + 3991: "bv-smcsrv", + 3992: "bv-ds", + 3993: "bv-agent", + 3995: "iss-mgmt-ssl", + 3996: "abcsoftware", + 3997: "agentsease-db", + 3998: "dnx", + 3999: "nvcnet", + 4000: "terabase", + 4001: "newoak", + 4002: "pxc-spvr-ft", + 4003: "pxc-splr-ft", + 4004: "pxc-roid", + 4005: "pxc-pin", + 4006: "pxc-spvr", + 4007: "pxc-splr", + 4008: "netcheque", + 4009: "chimera-hwm", + 4010: "samsung-unidex", + 4011: "altserviceboot", + 4012: "pda-gate", + 4013: "acl-manager", + 4014: "taiclock", + 4015: "talarian-mcast1", + 4016: "talarian-mcast2", + 4017: "talarian-mcast3", + 4018: "talarian-mcast4", + 4019: "talarian-mcast5", + 4020: "trap", + 4021: "nexus-portal", + 4022: "dnox", + 4023: "esnm-zoning", + 4024: "tnp1-port", + 4025: "partimage", + 4026: "as-debug", + 4027: "bxp", + 4028: "dtserver-port", + 4029: "ip-qsig", + 4030: "jdmn-port", + 4031: "suucp", + 4032: "vrts-auth-port", + 4033: "sanavigator", + 4034: "ubxd", + 4035: "wap-push-http", + 4036: "wap-push-https", + 4037: "ravehd", + 4038: "fazzt-ptp", + 4039: "fazzt-admin", + 4040: "yo-main", + 4041: "houston", + 4042: "ldxp", + 4043: "nirp", + 4044: "ltp", + 4045: "npp", + 4046: "acp-proto", + 4047: "ctp-state", + 4049: "wafs", + 4050: "cisco-wafs", + 4051: "cppdp", + 4052: "interact", + 4053: "ccu-comm-1", + 4054: "ccu-comm-2", + 4055: "ccu-comm-3", + 4056: "lms", + 4057: "wfm", + 4058: "kingfisher", + 4059: "dlms-cosem", + 4060: "dsmeter-iatc", + 4061: "ice-location", + 4062: "ice-slocation", + 4063: "ice-router", + 4064: "ice-srouter", + 4065: "avanti-cdp", + 4066: "pmas", + 4067: "idp", + 4068: "ipfltbcst", + 4069: "minger", + 4070: "tripe", + 4071: "aibkup", + 4072: "zieto-sock", + 4073: "iRAPP", + 4074: "cequint-cityid", + 4075: "perimlan", + 4076: "seraph", + 4078: "cssp", + 4079: "santools", + 4080: "lorica-in", + 4081: "lorica-in-sec", + 4082: "lorica-out", + 4083: "lorica-out-sec", + 4085: "ezmessagesrv", + 4087: "applusservice", + 4088: "npsp", + 4089: "opencore", + 4090: "omasgport", + 4091: "ewinstaller", + 4092: "ewdgs", + 4093: "pvxpluscs", + 4094: "sysrqd", + 4095: "xtgui", + 4096: "bre", + 4097: "patrolview", + 4098: "drmsfsd", + 4099: "dpcp", + 4100: "igo-incognito", + 4101: "brlp-0", + 4102: "brlp-1", + 4103: "brlp-2", + 4104: "brlp-3", + 4105: "shofar", + 4106: "synchronite", + 4107: "j-ac", + 4108: "accel", + 4109: "izm", + 4110: "g2tag", + 4111: "xgrid", + 4112: "apple-vpns-rp", + 4113: "aipn-reg", + 4114: "jomamqmonitor", + 4115: "cds", + 4116: "smartcard-tls", + 4117: "hillrserv", + 4118: "netscript", + 4119: "assuria-slm", + 4121: "e-builder", + 4122: "fprams", + 4123: "z-wave", + 4124: "tigv2", + 4125: "opsview-envoy", + 4126: "ddrepl", + 4127: "unikeypro", + 4128: "nufw", + 4129: "nuauth", + 4130: "fronet", + 4131: "stars", + 4132: "nuts-dem", + 4133: "nuts-bootp", + 4134: "nifty-hmi", + 4135: "cl-db-attach", + 4136: "cl-db-request", + 4137: "cl-db-remote", + 4138: "nettest", + 4139: "thrtx", + 4140: "cedros-fds", + 4141: "oirtgsvc", + 4142: "oidocsvc", + 4143: "oidsr", + 4145: "vvr-control", + 4146: "tgcconnect", + 4147: "vrxpservman", + 4148: "hhb-handheld", + 4149: "agslb", + 4150: "PowerAlert-nsa", + 4151: "menandmice-noh", + 4152: "idig-mux", + 4153: "mbl-battd", + 4154: "atlinks", + 4155: "bzr", + 4156: "stat-results", + 4157: "stat-scanner", + 4158: "stat-cc", + 4159: "nss", + 4160: "jini-discovery", + 4161: "omscontact", + 4162: "omstopology", + 4163: "silverpeakpeer", + 4164: "silverpeakcomm", + 4165: "altcp", + 4166: "joost", + 4167: "ddgn", + 4168: "pslicser", + 4169: "iadt", + 4170: "d-cinema-csp", + 4171: "ml-svnet", + 4172: "pcoip", + 4174: "smcluster", + 4175: "bccp", + 4176: "tl-ipcproxy", + 4177: "wello", + 4178: "storman", + 4179: "MaxumSP", + 4180: "httpx", + 4181: "macbak", + 4182: "pcptcpservice", + 4183: "cyborgnet", + 4184: "universe-suite", + 4185: "wcpp", + 4186: "boxbackupstore", + 4187: "csc-proxy", + 4188: "vatata", + 4189: "pcep", + 4190: "sieve", + 4192: "azeti", + 4193: "pvxplusio", + 4199: "eims-admin", + 4300: "corelccam", + 4301: "d-data", + 4302: "d-data-control", + 4303: "srcp", + 4304: "owserver", + 4305: "batman", + 4306: "pinghgl", + 4307: "visicron-vs", + 4308: "compx-lockview", + 4309: "dserver", + 4310: "mirrtex", + 4311: "p6ssmc", + 4312: "pscl-mgt", + 4313: "perrla", + 4314: "choiceview-agt", + 4316: "choiceview-clt", + 4320: "fdt-rcatp", + 4321: "rwhois", + 4322: "trim-event", + 4323: "trim-ice", + 4325: "geognosisman", + 4326: "geognosis", + 4327: "jaxer-web", + 4328: "jaxer-manager", + 4329: "publiqare-sync", + 4330: "dey-sapi", + 4331: "ktickets-rest", + 4333: "ahsp", + 4334: "netconf-ch-ssh", + 4335: "netconf-ch-tls", + 4336: "restconf-ch-tls", + 4340: "gaia", + 4341: "lisp-data", + 4342: "lisp-cons", + 4343: "unicall", + 4344: "vinainstall", + 4345: "m4-network-as", + 4346: "elanlm", + 4347: "lansurveyor", + 4348: "itose", + 4349: "fsportmap", + 4350: "net-device", + 4351: "plcy-net-svcs", + 4352: "pjlink", + 4353: "f5-iquery", + 4354: "qsnet-trans", + 4355: "qsnet-workst", + 4356: "qsnet-assist", + 4357: "qsnet-cond", + 4358: "qsnet-nucl", + 4359: "omabcastltkm", + 4360: "matrix-vnet", + 4368: "wxbrief", + 4369: "epmd", + 4370: "elpro-tunnel", + 4371: "l2c-control", + 4372: "l2c-data", + 4373: "remctl", + 4374: "psi-ptt", + 4375: "tolteces", + 4376: "bip", + 4377: "cp-spxsvr", + 4378: "cp-spxdpy", + 4379: "ctdb", + 4389: "xandros-cms", + 4390: "wiegand", + 4391: "apwi-imserver", + 4392: "apwi-rxserver", + 4393: "apwi-rxspooler", + 4395: "omnivisionesx", + 4396: "fly", + 4400: "ds-srv", + 4401: "ds-srvr", + 4402: "ds-clnt", + 4403: "ds-user", + 4404: "ds-admin", + 4405: "ds-mail", + 4406: "ds-slp", + 4407: "nacagent", + 4408: "slscc", + 4409: "netcabinet-com", + 4410: "itwo-server", + 4411: "found", + 4413: "avi-nms", + 4414: "updog", + 4415: "brcd-vr-req", + 4416: "pjj-player", + 4417: "workflowdir", + 4419: "cbp", + 4420: "nvm-express", + 4421: "scaleft", + 4422: "tsepisp", + 4423: "thingkit", + 4425: "netrockey6", + 4426: "beacon-port-2", + 4427: "drizzle", + 4428: "omviserver", + 4429: "omviagent", + 4430: "rsqlserver", + 4431: "wspipe", + 4432: "l-acoustics", + 4433: "vop", + 4442: "saris", + 4443: "pharos", + 4444: "krb524", + 4445: "upnotifyp", + 4446: "n1-fwp", + 4447: "n1-rmgmt", + 4448: "asc-slmd", + 4449: "privatewire", + 4450: "camp", + 4451: "ctisystemmsg", + 4452: "ctiprogramload", + 4453: "nssalertmgr", + 4454: "nssagentmgr", + 4455: "prchat-user", + 4456: "prchat-server", + 4457: "prRegister", + 4458: "mcp", + 4484: "hpssmgmt", + 4485: "assyst-dr", + 4486: "icms", + 4487: "prex-tcp", + 4488: "awacs-ice", + 4500: "ipsec-nat-t", + 4535: "ehs", + 4536: "ehs-ssl", + 4537: "wssauthsvc", + 4538: "swx-gate", + 4545: "worldscores", + 4546: "sf-lm", + 4547: "lanner-lm", + 4548: "synchromesh", + 4549: "aegate", + 4550: "gds-adppiw-db", + 4551: "ieee-mih", + 4552: "menandmice-mon", + 4553: "icshostsvc", + 4554: "msfrs", + 4555: "rsip", + 4556: "dtn-bundle", + 4559: "hylafax", + 4563: "amahi-anywhere", + 4566: "kwtc", + 4567: "tram", + 4568: "bmc-reporting", + 4569: "iax", + 4570: "deploymentmap", + 4573: "cardifftec-back", + 4590: "rid", + 4591: "l3t-at-an", + 4593: "ipt-anri-anri", + 4594: "ias-session", + 4595: "ias-paging", + 4596: "ias-neighbor", + 4597: "a21-an-1xbs", + 4598: "a16-an-an", + 4599: "a17-an-an", + 4600: "piranha1", + 4601: "piranha2", + 4602: "mtsserver", + 4603: "menandmice-upg", + 4604: "irp", + 4605: "sixchat", + 4658: "playsta2-app", + 4659: "playsta2-lob", + 4660: "smaclmgr", + 4661: "kar2ouche", + 4662: "oms", + 4663: "noteit", + 4664: "ems", + 4665: "contclientms", + 4666: "eportcomm", + 4667: "mmacomm", + 4668: "mmaeds", + 4669: "eportcommdata", + 4670: "light", + 4671: "acter", + 4672: "rfa", + 4673: "cxws", + 4674: "appiq-mgmt", + 4675: "dhct-status", + 4676: "dhct-alerts", + 4677: "bcs", + 4678: "traversal", + 4679: "mgesupervision", + 4680: "mgemanagement", + 4681: "parliant", + 4682: "finisar", + 4683: "spike", + 4684: "rfid-rp1", + 4685: "autopac", + 4686: "msp-os", + 4687: "nst", + 4688: "mobile-p2p", + 4689: "altovacentral", + 4690: "prelude", + 4691: "mtn", + 4692: "conspiracy", + 4700: "netxms-agent", + 4701: "netxms-mgmt", + 4702: "netxms-sync", + 4703: "npqes-test", + 4704: "assuria-ins", + 4725: "truckstar", + 4727: "fcis", + 4728: "capmux", + 4730: "gearman", + 4731: "remcap", + 4733: "resorcs", + 4737: "ipdr-sp", + 4738: "solera-lpn", + 4739: "ipfix", + 4740: "ipfixs", + 4741: "lumimgrd", + 4742: "sicct", + 4743: "openhpid", + 4744: "ifsp", + 4745: "fmp", + 4749: "profilemac", + 4750: "ssad", + 4751: "spocp", + 4752: "snap", + 4753: "simon", + 4756: "RDCenter", + 4774: "converge", + 4784: "bfd-multi-ctl", + 4786: "smart-install", + 4787: "sia-ctrl-plane", + 4788: "xmcp", + 4800: "iims", + 4801: "iwec", + 4802: "ilss", + 4803: "notateit", + 4827: "htcp", + 4837: "varadero-0", + 4838: "varadero-1", + 4839: "varadero-2", + 4840: "opcua-tcp", + 4841: "quosa", + 4842: "gw-asv", + 4843: "opcua-tls", + 4844: "gw-log", + 4845: "wcr-remlib", + 4846: "contamac-icm", + 4847: "wfc", + 4848: "appserv-http", + 4849: "appserv-https", + 4850: "sun-as-nodeagt", + 4851: "derby-repli", + 4867: "unify-debug", + 4868: "phrelay", + 4869: "phrelaydbg", + 4870: "cc-tracking", + 4871: "wired", + 4876: "tritium-can", + 4877: "lmcs", + 4879: "wsdl-event", + 4880: "hislip", + 4883: "wmlserver", + 4884: "hivestor", + 4885: "abbs", + 4894: "lyskom", + 4899: "radmin-port", + 4900: "hfcs", + 4901: "flr-agent", + 4902: "magiccontrol", + 4912: "lutap", + 4913: "lutcp", + 4914: "bones", + 4915: "frcs", + 4940: "eq-office-4940", + 4941: "eq-office-4941", + 4942: "eq-office-4942", + 4949: "munin", + 4950: "sybasesrvmon", + 4951: "pwgwims", + 4952: "sagxtsds", + 4953: "dbsyncarbiter", + 4969: "ccss-qmm", + 4970: "ccss-qsm", + 4984: "webyast", + 4985: "gerhcs", + 4986: "mrip", + 4987: "smar-se-port1", + 4988: "smar-se-port2", + 4989: "parallel", + 4990: "busycal", + 4991: "vrt", + 4999: "hfcs-manager", + 5000: "commplex-main", + 5001: "commplex-link", + 5002: "rfe", + 5003: "fmpro-internal", + 5004: "avt-profile-1", + 5005: "avt-profile-2", + 5006: "wsm-server", + 5007: "wsm-server-ssl", + 5008: "synapsis-edge", + 5009: "winfs", + 5010: "telelpathstart", + 5011: "telelpathattack", + 5012: "nsp", + 5013: "fmpro-v6", + 5015: "fmwp", + 5020: "zenginkyo-1", + 5021: "zenginkyo-2", + 5022: "mice", + 5023: "htuilsrv", + 5024: "scpi-telnet", + 5025: "scpi-raw", + 5026: "strexec-d", + 5027: "strexec-s", + 5028: "qvr", + 5029: "infobright", + 5030: "surfpass", + 5032: "signacert-agent", + 5033: "jtnetd-server", + 5034: "jtnetd-status", + 5042: "asnaacceler8db", + 5043: "swxadmin", + 5044: "lxi-evntsvc", + 5045: "osp", + 5048: "texai", + 5049: "ivocalize", + 5050: "mmcc", + 5051: "ita-agent", + 5052: "ita-manager", + 5053: "rlm", + 5054: "rlm-admin", + 5055: "unot", + 5056: "intecom-ps1", + 5057: "intecom-ps2", + 5059: "sds", + 5060: "sip", + 5061: "sips", + 5062: "na-localise", + 5063: "csrpc", + 5064: "ca-1", + 5065: "ca-2", + 5066: "stanag-5066", + 5067: "authentx", + 5068: "bitforestsrv", + 5069: "i-net-2000-npr", + 5070: "vtsas", + 5071: "powerschool", + 5072: "ayiya", + 5073: "tag-pm", + 5074: "alesquery", + 5075: "pvaccess", + 5080: "onscreen", + 5081: "sdl-ets", + 5082: "qcp", + 5083: "qfp", + 5084: "llrp", + 5085: "encrypted-llrp", + 5086: "aprigo-cs", + 5087: "biotic", + 5093: "sentinel-lm", + 5094: "hart-ip", + 5099: "sentlm-srv2srv", + 5100: "socalia", + 5101: "talarian-tcp", + 5102: "oms-nonsecure", + 5103: "actifio-c2c", + 5106: "actifioudsagent", + 5107: "actifioreplic", + 5111: "taep-as-svc", + 5112: "pm-cmdsvr", + 5114: "ev-services", + 5115: "autobuild", + 5117: "gradecam", + 5120: "barracuda-bbs", + 5133: "nbt-pc", + 5134: "ppactivation", + 5135: "erp-scale", + 5137: "ctsd", + 5145: "rmonitor-secure", + 5146: "social-alarm", + 5150: "atmp", + 5151: "esri-sde", + 5152: "sde-discovery", + 5153: "toruxserver", + 5154: "bzflag", + 5155: "asctrl-agent", + 5156: "rugameonline", + 5157: "mediat", + 5161: "snmpssh", + 5162: "snmpssh-trap", + 5163: "sbackup", + 5164: "vpa", + 5165: "ife-icorp", + 5166: "winpcs", + 5167: "scte104", + 5168: "scte30", + 5172: "pcoip-mgmt", + 5190: "aol", + 5191: "aol-1", + 5192: "aol-2", + 5193: "aol-3", + 5194: "cpscomm", + 5195: "ampl-lic", + 5196: "ampl-tableproxy", + 5197: "tunstall-lwp", + 5200: "targus-getdata", + 5201: "targus-getdata1", + 5202: "targus-getdata2", + 5203: "targus-getdata3", + 5209: "nomad", + 5215: "noteza", + 5221: "3exmp", + 5222: "xmpp-client", + 5223: "hpvirtgrp", + 5224: "hpvirtctrl", + 5225: "hp-server", + 5226: "hp-status", + 5227: "perfd", + 5228: "hpvroom", + 5229: "jaxflow", + 5230: "jaxflow-data", + 5231: "crusecontrol", + 5232: "csedaemon", + 5233: "enfs", + 5234: "eenet", + 5235: "galaxy-network", + 5236: "padl2sim", + 5237: "mnet-discovery", + 5245: "downtools", + 5248: "caacws", + 5249: "caaclang2", + 5250: "soagateway", + 5251: "caevms", + 5252: "movaz-ssc", + 5253: "kpdp", + 5254: "logcabin", + 5264: "3com-njack-1", + 5265: "3com-njack-2", + 5269: "xmpp-server", + 5270: "cartographerxmp", + 5271: "cuelink", + 5272: "pk", + 5280: "xmpp-bosh", + 5281: "undo-lm", + 5282: "transmit-port", + 5298: "presence", + 5299: "nlg-data", + 5300: "hacl-hb", + 5301: "hacl-gs", + 5302: "hacl-cfg", + 5303: "hacl-probe", + 5304: "hacl-local", + 5305: "hacl-test", + 5306: "sun-mc-grp", + 5307: "sco-aip", + 5308: "cfengine", + 5309: "jprinter", + 5310: "outlaws", + 5312: "permabit-cs", + 5313: "rrdp", + 5314: "opalis-rbt-ipc", + 5315: "hacl-poll", + 5316: "hpbladems", + 5317: "hpdevms", + 5318: "pkix-cmc", + 5320: "bsfserver-zn", + 5321: "bsfsvr-zn-ssl", + 5343: "kfserver", + 5344: "xkotodrcp", + 5349: "stuns", + 5352: "dns-llq", + 5353: "mdns", + 5354: "mdnsresponder", + 5355: "llmnr", + 5356: "ms-smlbiz", + 5357: "wsdapi", + 5358: "wsdapi-s", + 5359: "ms-alerter", + 5360: "ms-sideshow", + 5361: "ms-s-sideshow", + 5362: "serverwsd2", + 5363: "net-projection", + 5397: "stresstester", + 5398: "elektron-admin", + 5399: "securitychase", + 5400: "excerpt", + 5401: "excerpts", + 5402: "mftp", + 5403: "hpoms-ci-lstn", + 5404: "hpoms-dps-lstn", + 5405: "netsupport", + 5406: "systemics-sox", + 5407: "foresyte-clear", + 5408: "foresyte-sec", + 5409: "salient-dtasrv", + 5410: "salient-usrmgr", + 5411: "actnet", + 5412: "continuus", + 5413: "wwiotalk", + 5414: "statusd", + 5415: "ns-server", + 5416: "sns-gateway", + 5417: "sns-agent", + 5418: "mcntp", + 5419: "dj-ice", + 5420: "cylink-c", + 5421: "netsupport2", + 5422: "salient-mux", + 5423: "virtualuser", + 5424: "beyond-remote", + 5425: "br-channel", + 5426: "devbasic", + 5427: "sco-peer-tta", + 5428: "telaconsole", + 5429: "base", + 5430: "radec-corp", + 5431: "park-agent", + 5432: "postgresql", + 5433: "pyrrho", + 5434: "sgi-arrayd", + 5435: "sceanics", + 5443: "spss", + 5445: "smbdirect", + 5450: "tiepie", + 5453: "surebox", + 5454: "apc-5454", + 5455: "apc-5455", + 5456: "apc-5456", + 5461: "silkmeter", + 5462: "ttl-publisher", + 5463: "ttlpriceproxy", + 5464: "quailnet", + 5465: "netops-broker", + 5470: "apsolab-col", + 5471: "apsolab-cols", + 5472: "apsolab-tag", + 5473: "apsolab-tags", + 5475: "apsolab-data", + 5500: "fcp-addr-srvr1", + 5501: "fcp-addr-srvr2", + 5502: "fcp-srvr-inst1", + 5503: "fcp-srvr-inst2", + 5504: "fcp-cics-gw1", + 5505: "checkoutdb", + 5506: "amc", + 5507: "psl-management", + 5550: "cbus", + 5553: "sgi-eventmond", + 5554: "sgi-esphttp", + 5555: "personal-agent", + 5556: "freeciv", + 5557: "farenet", + 5565: "hpe-dp-bura", + 5566: "westec-connect", + 5567: "dof-dps-mc-sec", + 5568: "sdt", + 5569: "rdmnet-ctrl", + 5573: "sdmmp", + 5574: "lsi-bobcat", + 5575: "ora-oap", + 5579: "fdtracks", + 5580: "tmosms0", + 5581: "tmosms1", + 5582: "fac-restore", + 5583: "tmo-icon-sync", + 5584: "bis-web", + 5585: "bis-sync", + 5586: "att-mt-sms", + 5597: "ininmessaging", + 5598: "mctfeed", + 5599: "esinstall", + 5600: "esmmanager", + 5601: "esmagent", + 5602: "a1-msc", + 5603: "a1-bs", + 5604: "a3-sdunode", + 5605: "a4-sdunode", + 5618: "efr", + 5627: "ninaf", + 5628: "htrust", + 5629: "symantec-sfdb", + 5630: "precise-comm", + 5631: "pcanywheredata", + 5632: "pcanywherestat", + 5633: "beorl", + 5634: "xprtld", + 5635: "sfmsso", + 5636: "sfm-db-server", + 5637: "cssc", + 5638: "flcrs", + 5639: "ics", + 5646: "vfmobile", + 5670: "filemq", + 5671: "amqps", + 5672: "amqp", + 5673: "jms", + 5674: "hyperscsi-port", + 5675: "v5ua", + 5676: "raadmin", + 5677: "questdb2-lnchr", + 5678: "rrac", + 5679: "dccm", + 5680: "auriga-router", + 5681: "ncxcp", + 5688: "ggz", + 5689: "qmvideo", + 5693: "rbsystem", + 5696: "kmip", + 5700: "supportassist", + 5713: "proshareaudio", + 5714: "prosharevideo", + 5715: "prosharedata", + 5716: "prosharerequest", + 5717: "prosharenotify", + 5718: "dpm", + 5719: "dpm-agent", + 5720: "ms-licensing", + 5721: "dtpt", + 5722: "msdfsr", + 5723: "omhs", + 5724: "omsdk", + 5725: "ms-ilm", + 5726: "ms-ilm-sts", + 5727: "asgenf", + 5728: "io-dist-data", + 5729: "openmail", + 5730: "unieng", + 5741: "ida-discover1", + 5742: "ida-discover2", + 5743: "watchdoc-pod", + 5744: "watchdoc", + 5745: "fcopy-server", + 5746: "fcopys-server", + 5747: "tunatic", + 5748: "tunalyzer", + 5750: "rscd", + 5755: "openmailg", + 5757: "x500ms", + 5766: "openmailns", + 5767: "s-openmail", + 5768: "openmailpxy", + 5769: "spramsca", + 5770: "spramsd", + 5771: "netagent", + 5777: "dali-port", + 5780: "vts-rpc", + 5781: "3par-evts", + 5782: "3par-mgmt", + 5783: "3par-mgmt-ssl", + 5785: "3par-rcopy", + 5793: "xtreamx", + 5813: "icmpd", + 5814: "spt-automation", + 5841: "shiprush-d-ch", + 5842: "reversion", + 5859: "wherehoo", + 5863: "ppsuitemsg", + 5868: "diameters", + 5883: "jute", + 5900: "rfb", + 5910: "cm", + 5911: "cpdlc", + 5912: "fis", + 5913: "ads-c", + 5963: "indy", + 5968: "mppolicy-v5", + 5969: "mppolicy-mgr", + 5984: "couchdb", + 5985: "wsman", + 5986: "wsmans", + 5987: "wbem-rmi", + 5988: "wbem-http", + 5989: "wbem-https", + 5990: "wbem-exp-https", + 5991: "nuxsl", + 5992: "consul-insight", + 5993: "cim-rs", + 5999: "cvsup", + 6064: "ndl-ahp-svc", + 6065: "winpharaoh", + 6066: "ewctsp", + 6068: "gsmp-ancp", + 6069: "trip", + 6070: "messageasap", + 6071: "ssdtp", + 6072: "diagnose-proc", + 6073: "directplay8", + 6074: "max", + 6075: "dpm-acm", + 6076: "msft-dpm-cert", + 6077: "iconstructsrv", + 6084: "reload-config", + 6085: "konspire2b", + 6086: "pdtp", + 6087: "ldss", + 6088: "doglms", + 6099: "raxa-mgmt", + 6100: "synchronet-db", + 6101: "synchronet-rtc", + 6102: "synchronet-upd", + 6103: "rets", + 6104: "dbdb", + 6105: "primaserver", + 6106: "mpsserver", + 6107: "etc-control", + 6108: "sercomm-scadmin", + 6109: "globecast-id", + 6110: "softcm", + 6111: "spc", + 6112: "dtspcd", + 6113: "dayliteserver", + 6114: "wrspice", + 6115: "xic", + 6116: "xtlserv", + 6117: "daylitetouch", + 6121: "spdy", + 6122: "bex-webadmin", + 6123: "backup-express", + 6124: "pnbs", + 6130: "damewaremobgtwy", + 6133: "nbt-wol", + 6140: "pulsonixnls", + 6141: "meta-corp", + 6142: "aspentec-lm", + 6143: "watershed-lm", + 6144: "statsci1-lm", + 6145: "statsci2-lm", + 6146: "lonewolf-lm", + 6147: "montage-lm", + 6148: "ricardo-lm", + 6149: "tal-pod", + 6159: "efb-aci", + 6160: "ecmp", + 6161: "patrol-ism", + 6162: "patrol-coll", + 6163: "pscribe", + 6200: "lm-x", + 6209: "qmtps", + 6222: "radmind", + 6241: "jeol-nsdtp-1", + 6242: "jeol-nsdtp-2", + 6243: "jeol-nsdtp-3", + 6244: "jeol-nsdtp-4", + 6251: "tl1-raw-ssl", + 6252: "tl1-ssh", + 6253: "crip", + 6267: "gld", + 6268: "grid", + 6269: "grid-alt", + 6300: "bmc-grx", + 6301: "bmc-ctd-ldap", + 6306: "ufmp", + 6315: "scup", + 6316: "abb-escp", + 6317: "nav-data-cmd", + 6320: "repsvc", + 6321: "emp-server1", + 6322: "emp-server2", + 6324: "hrd-ncs", + 6325: "dt-mgmtsvc", + 6326: "dt-vra", + 6343: "sflow", + 6344: "streletz", + 6346: "gnutella-svc", + 6347: "gnutella-rtr", + 6350: "adap", + 6355: "pmcs", + 6360: "metaedit-mu", + 6370: "metaedit-se", + 6379: "redis", + 6382: "metatude-mds", + 6389: "clariion-evr01", + 6390: "metaedit-ws", + 6417: "faxcomservice", + 6418: "syserverremote", + 6419: "svdrp", + 6420: "nim-vdrshell", + 6421: "nim-wan", + 6432: "pgbouncer", + 6442: "tarp", + 6443: "sun-sr-https", + 6444: "sge-qmaster", + 6445: "sge-execd", + 6446: "mysql-proxy", + 6455: "skip-cert-recv", + 6456: "skip-cert-send", + 6471: "lvision-lm", + 6480: "sun-sr-http", + 6481: "servicetags", + 6482: "ldoms-mgmt", + 6483: "SunVTS-RMI", + 6484: "sun-sr-jms", + 6485: "sun-sr-iiop", + 6486: "sun-sr-iiops", + 6487: "sun-sr-iiop-aut", + 6488: "sun-sr-jmx", + 6489: "sun-sr-admin", + 6500: "boks", + 6501: "boks-servc", + 6502: "boks-servm", + 6503: "boks-clntd", + 6505: "badm-priv", + 6506: "badm-pub", + 6507: "bdir-priv", + 6508: "bdir-pub", + 6509: "mgcs-mfp-port", + 6510: "mcer-port", + 6513: "netconf-tls", + 6514: "syslog-tls", + 6515: "elipse-rec", + 6543: "lds-distrib", + 6544: "lds-dump", + 6547: "apc-6547", + 6548: "apc-6548", + 6549: "apc-6549", + 6550: "fg-sysupdate", + 6551: "sum", + 6558: "xdsxdm", + 6566: "sane-port", + 6568: "canit-store", + 6579: "affiliate", + 6580: "parsec-master", + 6581: "parsec-peer", + 6582: "parsec-game", + 6583: "joaJewelSuite", + 6600: "mshvlm", + 6601: "mstmg-sstp", + 6602: "wsscomfrmwk", + 6619: "odette-ftps", + 6620: "kftp-data", + 6621: "kftp", + 6622: "mcftp", + 6623: "ktelnet", + 6624: "datascaler-db", + 6625: "datascaler-ctl", + 6626: "wago-service", + 6627: "nexgen", + 6628: "afesc-mc", + 6629: "nexgen-aux", + 6632: "mxodbc-connect", + 6640: "ovsdb", + 6653: "openflow", + 6655: "pcs-sf-ui-man", + 6656: "emgmsg", + 6670: "vocaltec-gold", + 6671: "p4p-portal", + 6672: "vision-server", + 6673: "vision-elmd", + 6678: "vfbp", + 6679: "osaut", + 6687: "clever-ctrace", + 6688: "clever-tcpip", + 6689: "tsa", + 6690: "cleverdetect", + 6697: "ircs-u", + 6701: "kti-icad-srvr", + 6702: "e-design-net", + 6703: "e-design-web", + 6714: "ibprotocol", + 6715: "fibotrader-com", + 6716: "princity-agent", + 6767: "bmc-perf-agent", + 6768: "bmc-perf-mgrd", + 6769: "adi-gxp-srvprt", + 6770: "plysrv-http", + 6771: "plysrv-https", + 6777: "ntz-tracker", + 6778: "ntz-p2p-storage", + 6785: "dgpf-exchg", + 6786: "smc-jmx", + 6787: "smc-admin", + 6788: "smc-http", + 6789: "radg", + 6790: "hnmp", + 6791: "hnm", + 6801: "acnet", + 6817: "pentbox-sim", + 6831: "ambit-lm", + 6841: "netmo-default", + 6842: "netmo-http", + 6850: "iccrushmore", + 6868: "acctopus-cc", + 6888: "muse", + 6900: "rtimeviewer", + 6901: "jetstream", + 6935: "ethoscan", + 6936: "xsmsvc", + 6946: "bioserver", + 6951: "otlp", + 6961: "jmact3", + 6962: "jmevt2", + 6963: "swismgr1", + 6964: "swismgr2", + 6965: "swistrap", + 6966: "swispol", + 6969: "acmsoda", + 6970: "conductor", + 6997: "MobilitySrv", + 6998: "iatp-highpri", + 6999: "iatp-normalpri", + 7000: "afs3-fileserver", + 7001: "afs3-callback", + 7002: "afs3-prserver", + 7003: "afs3-vlserver", + 7004: "afs3-kaserver", + 7005: "afs3-volser", + 7006: "afs3-errors", + 7007: "afs3-bos", + 7008: "afs3-update", + 7009: "afs3-rmtsys", + 7010: "ups-onlinet", + 7011: "talon-disc", + 7012: "talon-engine", + 7013: "microtalon-dis", + 7014: "microtalon-com", + 7015: "talon-webserver", + 7018: "fisa-svc", + 7019: "doceri-ctl", + 7020: "dpserve", + 7021: "dpserveadmin", + 7022: "ctdp", + 7023: "ct2nmcs", + 7024: "vmsvc", + 7025: "vmsvc-2", + 7030: "op-probe", + 7031: "iposplanet", + 7070: "arcp", + 7071: "iwg1", + 7073: "martalk", + 7080: "empowerid", + 7099: "lazy-ptop", + 7100: "font-service", + 7101: "elcn", + 7117: "rothaga", + 7121: "virprot-lm", + 7128: "scenidm", + 7129: "scenccs", + 7161: "cabsm-comm", + 7162: "caistoragemgr", + 7163: "cacsambroker", + 7164: "fsr", + 7165: "doc-server", + 7166: "aruba-server", + 7167: "casrmagent", + 7168: "cnckadserver", + 7169: "ccag-pib", + 7170: "nsrp", + 7171: "drm-production", + 7172: "metalbend", + 7173: "zsecure", + 7174: "clutild", + 7200: "fodms", + 7201: "dlip", + 7215: "PS-Server", + 7216: "PS-Capture-Pro", + 7227: "ramp", + 7228: "citrixupp", + 7229: "citrixuppg", + 7236: "display", + 7237: "pads", + 7244: "frc-hicp", + 7262: "cnap", + 7272: "watchme-7272", + 7273: "oma-rlp", + 7274: "oma-rlp-s", + 7275: "oma-ulp", + 7276: "oma-ilp", + 7277: "oma-ilp-s", + 7278: "oma-dcdocbs", + 7279: "ctxlic", + 7280: "itactionserver1", + 7281: "itactionserver2", + 7282: "mzca-action", + 7283: "genstat", + 7365: "lcm-server", + 7391: "mindfilesys", + 7392: "mrssrendezvous", + 7393: "nfoldman", + 7394: "fse", + 7395: "winqedit", + 7397: "hexarc", + 7400: "rtps-discovery", + 7401: "rtps-dd-ut", + 7402: "rtps-dd-mt", + 7410: "ionixnetmon", + 7411: "daqstream", + 7421: "mtportmon", + 7426: "pmdmgr", + 7427: "oveadmgr", + 7428: "ovladmgr", + 7429: "opi-sock", + 7430: "xmpv7", + 7431: "pmd", + 7437: "faximum", + 7443: "oracleas-https", + 7471: "sttunnel", + 7473: "rise", + 7474: "neo4j", + 7478: "openit", + 7491: "telops-lmd", + 7500: "silhouette", + 7501: "ovbus", + 7508: "adcp", + 7509: "acplt", + 7510: "ovhpas", + 7511: "pafec-lm", + 7542: "saratoga", + 7543: "atul", + 7544: "nta-ds", + 7545: "nta-us", + 7546: "cfs", + 7547: "cwmp", + 7548: "tidp", + 7549: "nls-tl", + 7551: "controlone-con", + 7560: "sncp", + 7563: "cfw", + 7566: "vsi-omega", + 7569: "dell-eql-asm", + 7570: "aries-kfinder", + 7574: "coherence", + 7588: "sun-lm", + 7606: "mipi-debug", + 7624: "indi", + 7626: "simco", + 7627: "soap-http", + 7628: "zen-pawn", + 7629: "xdas", + 7630: "hawk", + 7631: "tesla-sys-msg", + 7633: "pmdfmgt", + 7648: "cuseeme", + 7672: "imqstomp", + 7673: "imqstomps", + 7674: "imqtunnels", + 7675: "imqtunnel", + 7676: "imqbrokerd", + 7677: "sun-user-https", + 7680: "pando-pub", + 7683: "dmt", + 7687: "bolt", + 7689: "collaber", + 7697: "klio", + 7700: "em7-secom", + 7707: "sync-em7", + 7708: "scinet", + 7720: "medimageportal", + 7724: "nsdeepfreezectl", + 7725: "nitrogen", + 7726: "freezexservice", + 7727: "trident-data", + 7728: "osvr", + 7734: "smip", + 7738: "aiagent", + 7741: "scriptview", + 7742: "msss", + 7743: "sstp-1", + 7744: "raqmon-pdu", + 7747: "prgp", + 7775: "inetfs", + 7777: "cbt", + 7778: "interwise", + 7779: "vstat", + 7781: "accu-lmgr", + 7786: "minivend", + 7787: "popup-reminders", + 7789: "office-tools", + 7794: "q3ade", + 7797: "pnet-conn", + 7798: "pnet-enc", + 7799: "altbsdp", + 7800: "asr", + 7801: "ssp-client", + 7810: "rbt-wanopt", + 7845: "apc-7845", + 7846: "apc-7846", + 7847: "csoauth", + 7869: "mobileanalyzer", + 7870: "rbt-smc", + 7871: "mdm", + 7878: "owms", + 7880: "pss", + 7887: "ubroker", + 7900: "mevent", + 7901: "tnos-sp", + 7902: "tnos-dp", + 7903: "tnos-dps", + 7913: "qo-secure", + 7932: "t2-drm", + 7933: "t2-brm", + 7962: "generalsync", + 7967: "supercell", + 7979: "micromuse-ncps", + 7980: "quest-vista", + 7981: "sossd-collect", + 7982: "sossd-agent", + 7997: "pushns", + 7999: "irdmi2", + 8000: "irdmi", + 8001: "vcom-tunnel", + 8002: "teradataordbms", + 8003: "mcreport", + 8005: "mxi", + 8008: "http-alt", + 8019: "qbdb", + 8020: "intu-ec-svcdisc", + 8021: "intu-ec-client", + 8022: "oa-system", + 8025: "ca-audit-da", + 8026: "ca-audit-ds", + 8032: "pro-ed", + 8033: "mindprint", + 8034: "vantronix-mgmt", + 8040: "ampify", + 8042: "fs-agent", + 8043: "fs-server", + 8044: "fs-mgmt", + 8051: "rocrail", + 8052: "senomix01", + 8053: "senomix02", + 8054: "senomix03", + 8055: "senomix04", + 8056: "senomix05", + 8057: "senomix06", + 8058: "senomix07", + 8059: "senomix08", + 8066: "toad-bi-appsrvr", + 8067: "infi-async", + 8074: "gadugadu", + 8080: "http-alt", + 8081: "sunproxyadmin", + 8082: "us-cli", + 8083: "us-srv", + 8086: "d-s-n", + 8087: "simplifymedia", + 8088: "radan-http", + 8091: "jamlink", + 8097: "sac", + 8100: "xprint-server", + 8101: "ldoms-migr", + 8102: "kz-migr", + 8115: "mtl8000-matrix", + 8116: "cp-cluster", + 8117: "purityrpc", + 8118: "privoxy", + 8121: "apollo-data", + 8122: "apollo-admin", + 8128: "paycash-online", + 8129: "paycash-wbp", + 8130: "indigo-vrmi", + 8131: "indigo-vbcp", + 8132: "dbabble", + 8140: "puppet", + 8148: "isdd", + 8153: "quantastor", + 8160: "patrol", + 8161: "patrol-snmp", + 8162: "lpar2rrd", + 8181: "intermapper", + 8182: "vmware-fdm", + 8183: "proremote", + 8184: "itach", + 8190: "gcp-rphy", + 8191: "limnerpressure", + 8192: "spytechphone", + 8194: "blp1", + 8195: "blp2", + 8199: "vvr-data", + 8200: "trivnet1", + 8201: "trivnet2", + 8204: "lm-perfworks", + 8205: "lm-instmgr", + 8206: "lm-dta", + 8207: "lm-sserver", + 8208: "lm-webwatcher", + 8230: "rexecj", + 8243: "synapse-nhttps", + 8276: "pando-sec", + 8280: "synapse-nhttp", + 8282: "libelle", + 8292: "blp3", + 8293: "hiperscan-id", + 8294: "blp4", + 8300: "tmi", + 8301: "amberon", + 8313: "hub-open-net", + 8320: "tnp-discover", + 8321: "tnp", + 8322: "garmin-marine", + 8351: "server-find", + 8376: "cruise-enum", + 8377: "cruise-swroute", + 8378: "cruise-config", + 8379: "cruise-diags", + 8380: "cruise-update", + 8383: "m2mservices", + 8400: "cvd", + 8401: "sabarsd", + 8402: "abarsd", + 8403: "admind", + 8404: "svcloud", + 8405: "svbackup", + 8415: "dlpx-sp", + 8416: "espeech", + 8417: "espeech-rtp", + 8442: "cybro-a-bus", + 8443: "pcsync-https", + 8444: "pcsync-http", + 8445: "copy", + 8450: "npmp", + 8457: "nexentamv", + 8470: "cisco-avp", + 8471: "pim-port", + 8472: "otv", + 8473: "vp2p", + 8474: "noteshare", + 8500: "fmtp", + 8501: "cmtp-mgt", + 8502: "ftnmtp", + 8554: "rtsp-alt", + 8555: "d-fence", + 8567: "dof-tunnel", + 8600: "asterix", + 8610: "canon-mfnp", + 8611: "canon-bjnp1", + 8612: "canon-bjnp2", + 8613: "canon-bjnp3", + 8614: "canon-bjnp4", + 8615: "imink", + 8665: "monetra", + 8666: "monetra-admin", + 8675: "msi-cps-rm", + 8686: "sun-as-jmxrmi", + 8688: "openremote-ctrl", + 8699: "vnyx", + 8711: "nvc", + 8733: "ibus", + 8750: "dey-keyneg", + 8763: "mc-appserver", + 8764: "openqueue", + 8765: "ultraseek-http", + 8766: "amcs", + 8770: "dpap", + 8778: "uec", + 8786: "msgclnt", + 8787: "msgsrvr", + 8793: "acd-pm", + 8800: "sunwebadmin", + 8804: "truecm", + 8873: "dxspider", + 8880: "cddbp-alt", + 8881: "galaxy4d", + 8883: "secure-mqtt", + 8888: "ddi-tcp-1", + 8889: "ddi-tcp-2", + 8890: "ddi-tcp-3", + 8891: "ddi-tcp-4", + 8892: "ddi-tcp-5", + 8893: "ddi-tcp-6", + 8894: "ddi-tcp-7", + 8899: "ospf-lite", + 8900: "jmb-cds1", + 8901: "jmb-cds2", + 8910: "manyone-http", + 8911: "manyone-xml", + 8912: "wcbackup", + 8913: "dragonfly", + 8937: "twds", + 8953: "ub-dns-control", + 8954: "cumulus-admin", + 8980: "nod-provider", + 8989: "sunwebadmins", + 8990: "http-wmap", + 8991: "https-wmap", + 8997: "oracle-ms-ens", + 8998: "canto-roboflow", + 8999: "bctp", + 9000: "cslistener", + 9001: "etlservicemgr", + 9002: "dynamid", + 9005: "golem", + 9008: "ogs-server", + 9009: "pichat", + 9010: "sdr", + 9020: "tambora", + 9021: "panagolin-ident", + 9022: "paragent", + 9023: "swa-1", + 9024: "swa-2", + 9025: "swa-3", + 9026: "swa-4", + 9050: "versiera", + 9051: "fio-cmgmt", + 9060: "CardWeb-IO", + 9080: "glrpc", + 9083: "emc-pp-mgmtsvc", + 9084: "aurora", + 9085: "ibm-rsyscon", + 9086: "net2display", + 9087: "classic", + 9088: "sqlexec", + 9089: "sqlexec-ssl", + 9090: "websm", + 9091: "xmltec-xmlmail", + 9092: "XmlIpcRegSvc", + 9093: "copycat", + 9100: "hp-pdl-datastr", + 9101: "bacula-dir", + 9102: "bacula-fd", + 9103: "bacula-sd", + 9104: "peerwire", + 9105: "xadmin", + 9106: "astergate", + 9107: "astergatefax", + 9119: "mxit", + 9122: "grcmp", + 9123: "grcp", + 9131: "dddp", + 9160: "apani1", + 9161: "apani2", + 9162: "apani3", + 9163: "apani4", + 9164: "apani5", + 9191: "sun-as-jpda", + 9200: "wap-wsp", + 9201: "wap-wsp-wtp", + 9202: "wap-wsp-s", + 9203: "wap-wsp-wtp-s", + 9204: "wap-vcard", + 9205: "wap-vcal", + 9206: "wap-vcard-s", + 9207: "wap-vcal-s", + 9208: "rjcdb-vcards", + 9209: "almobile-system", + 9210: "oma-mlp", + 9211: "oma-mlp-s", + 9212: "serverviewdbms", + 9213: "serverstart", + 9214: "ipdcesgbs", + 9215: "insis", + 9216: "acme", + 9217: "fsc-port", + 9222: "teamcoherence", + 9255: "mon", + 9278: "pegasus", + 9279: "pegasus-ctl", + 9280: "pgps", + 9281: "swtp-port1", + 9282: "swtp-port2", + 9283: "callwaveiam", + 9284: "visd", + 9285: "n2h2server", + 9287: "cumulus", + 9292: "armtechdaemon", + 9293: "storview", + 9294: "armcenterhttp", + 9295: "armcenterhttps", + 9300: "vrace", + 9306: "sphinxql", + 9312: "sphinxapi", + 9318: "secure-ts", + 9321: "guibase", + 9343: "mpidcmgr", + 9344: "mphlpdmc", + 9345: "rancher", + 9346: "ctechlicensing", + 9374: "fjdmimgr", + 9380: "boxp", + 9387: "d2dconfig", + 9388: "d2ddatatrans", + 9389: "adws", + 9390: "otp", + 9396: "fjinvmgr", + 9397: "mpidcagt", + 9400: "sec-t4net-srv", + 9401: "sec-t4net-clt", + 9402: "sec-pc2fax-srv", + 9418: "git", + 9443: "tungsten-https", + 9444: "wso2esb-console", + 9445: "mindarray-ca", + 9450: "sntlkeyssrvr", + 9500: "ismserver", + 9535: "mngsuite", + 9536: "laes-bf", + 9555: "trispen-sra", + 9592: "ldgateway", + 9593: "cba8", + 9594: "msgsys", + 9595: "pds", + 9596: "mercury-disc", + 9597: "pd-admin", + 9598: "vscp", + 9599: "robix", + 9600: "micromuse-ncpw", + 9612: "streamcomm-ds", + 9614: "iadt-tls", + 9616: "erunbook-agent", + 9617: "erunbook-server", + 9618: "condor", + 9628: "odbcpathway", + 9629: "uniport", + 9630: "peoctlr", + 9631: "peocoll", + 9640: "pqsflows", + 9666: "zoomcp", + 9667: "xmms2", + 9668: "tec5-sdctp", + 9694: "client-wakeup", + 9695: "ccnx", + 9700: "board-roar", + 9747: "l5nas-parchan", + 9750: "board-voip", + 9753: "rasadv", + 9762: "tungsten-http", + 9800: "davsrc", + 9801: "sstp-2", + 9802: "davsrcs", + 9875: "sapv1", + 9876: "sd", + 9888: "cyborg-systems", + 9889: "gt-proxy", + 9898: "monkeycom", + 9900: "iua", + 9909: "domaintime", + 9911: "sype-transport", + 9925: "xybrid-cloud", + 9950: "apc-9950", + 9951: "apc-9951", + 9952: "apc-9952", + 9953: "acis", + 9954: "hinp", + 9955: "alljoyn-stm", + 9966: "odnsp", + 9978: "xybrid-rt", + 9987: "dsm-scm-target", + 9988: "nsesrvr", + 9990: "osm-appsrvr", + 9991: "osm-oev", + 9992: "palace-1", + 9993: "palace-2", + 9994: "palace-3", + 9995: "palace-4", + 9996: "palace-5", + 9997: "palace-6", + 9998: "distinct32", + 9999: "distinct", + 10000: "ndmp", + 10001: "scp-config", + 10002: "documentum", + 10003: "documentum-s", + 10004: "emcrmirccd", + 10005: "emcrmird", + 10006: "netapp-sync", + 10007: "mvs-capacity", + 10008: "octopus", + 10009: "swdtp-sv", + 10010: "rxapi", + 10050: "zabbix-agent", + 10051: "zabbix-trapper", + 10055: "qptlmd", + 10080: "amanda", + 10081: "famdc", + 10100: "itap-ddtp", + 10101: "ezmeeting-2", + 10102: "ezproxy-2", + 10103: "ezrelay", + 10104: "swdtp", + 10107: "bctp-server", + 10110: "nmea-0183", + 10113: "netiq-endpoint", + 10114: "netiq-qcheck", + 10115: "netiq-endpt", + 10116: "netiq-voipa", + 10117: "iqrm", + 10125: "cimple", + 10128: "bmc-perf-sd", + 10129: "bmc-gms", + 10160: "qb-db-server", + 10161: "snmptls", + 10162: "snmptls-trap", + 10200: "trisoap", + 10201: "rsms", + 10252: "apollo-relay", + 10260: "axis-wimp-port", + 10261: "tile-ml", + 10288: "blocks", + 10321: "cosir", + 10540: "MOS-lower", + 10541: "MOS-upper", + 10542: "MOS-aux", + 10543: "MOS-soap", + 10544: "MOS-soap-opt", + 10548: "serverdocs", + 10631: "printopia", + 10800: "gap", + 10805: "lpdg", + 10809: "nbd", + 10860: "helix", + 10880: "bveapi", + 10933: "octopustentacle", + 10990: "rmiaux", + 11000: "irisa", + 11001: "metasys", + 11095: "weave", + 11103: "origo-sync", + 11104: "netapp-icmgmt", + 11105: "netapp-icdata", + 11106: "sgi-lk", + 11109: "sgi-dmfmgr", + 11110: "sgi-soap", + 11111: "vce", + 11112: "dicom", + 11161: "suncacao-snmp", + 11162: "suncacao-jmxmp", + 11163: "suncacao-rmi", + 11164: "suncacao-csa", + 11165: "suncacao-websvc", + 11172: "oemcacao-jmxmp", + 11173: "t5-straton", + 11174: "oemcacao-rmi", + 11175: "oemcacao-websvc", + 11201: "smsqp", + 11202: "dcsl-backup", + 11208: "wifree", + 11211: "memcache", + 11319: "imip", + 11320: "imip-channels", + 11321: "arena-server", + 11367: "atm-uhas", + 11371: "hkp", + 11489: "asgcypresstcps", + 11600: "tempest-port", + 11623: "emc-xsw-dconfig", + 11720: "h323callsigalt", + 11723: "emc-xsw-dcache", + 11751: "intrepid-ssl", + 11796: "lanschool", + 11876: "xoraya", + 11967: "sysinfo-sp", + 12000: "entextxid", + 12001: "entextnetwk", + 12002: "entexthigh", + 12003: "entextmed", + 12004: "entextlow", + 12005: "dbisamserver1", + 12006: "dbisamserver2", + 12007: "accuracer", + 12008: "accuracer-dbms", + 12010: "edbsrvr", + 12012: "vipera", + 12013: "vipera-ssl", + 12109: "rets-ssl", + 12121: "nupaper-ss", + 12168: "cawas", + 12172: "hivep", + 12300: "linogridengine", + 12302: "rads", + 12321: "warehouse-sss", + 12322: "warehouse", + 12345: "italk", + 12753: "tsaf", + 12865: "netperf", + 13160: "i-zipqd", + 13216: "bcslogc", + 13217: "rs-pias", + 13218: "emc-vcas-tcp", + 13223: "powwow-client", + 13224: "powwow-server", + 13400: "doip-data", + 13720: "bprd", + 13721: "bpdbm", + 13722: "bpjava-msvc", + 13724: "vnetd", + 13782: "bpcd", + 13783: "vopied", + 13785: "nbdb", + 13786: "nomdb", + 13818: "dsmcc-config", + 13819: "dsmcc-session", + 13820: "dsmcc-passthru", + 13821: "dsmcc-download", + 13822: "dsmcc-ccp", + 13823: "bmdss", + 13894: "ucontrol", + 13929: "dta-systems", + 13930: "medevolve", + 14000: "scotty-ft", + 14001: "sua", + 14033: "sage-best-com1", + 14034: "sage-best-com2", + 14141: "vcs-app", + 14142: "icpp", + 14143: "icpps", + 14145: "gcm-app", + 14149: "vrts-tdd", + 14150: "vcscmd", + 14154: "vad", + 14250: "cps", + 14414: "ca-web-update", + 14500: "xpra", + 14936: "hde-lcesrvr-1", + 14937: "hde-lcesrvr-2", + 15000: "hydap", + 15002: "onep-tls", + 15345: "xpilot", + 15363: "3link", + 15555: "cisco-snat", + 15660: "bex-xr", + 15740: "ptp", + 15999: "programmar", + 16000: "fmsas", + 16001: "fmsascon", + 16002: "gsms", + 16020: "jwpc", + 16021: "jwpc-bin", + 16161: "sun-sea-port", + 16162: "solaris-audit", + 16309: "etb4j", + 16310: "pduncs", + 16311: "pdefmns", + 16360: "netserialext1", + 16361: "netserialext2", + 16367: "netserialext3", + 16368: "netserialext4", + 16384: "connected", + 16385: "rdgs", + 16619: "xoms", + 16665: "axon-tunnel", + 16789: "cadsisvr", + 16900: "newbay-snc-mc", + 16950: "sgcip", + 16991: "intel-rci-mp", + 16992: "amt-soap-http", + 16993: "amt-soap-https", + 16994: "amt-redir-tcp", + 16995: "amt-redir-tls", + 17007: "isode-dua", + 17184: "vestasdlp", + 17185: "soundsvirtual", + 17219: "chipper", + 17220: "avtp", + 17221: "avdecc", + 17223: "isa100-gci", + 17225: "trdp-md", + 17234: "integrius-stp", + 17235: "ssh-mgmt", + 17500: "db-lsp", + 17555: "ailith", + 17729: "ea", + 17754: "zep", + 17755: "zigbee-ip", + 17756: "zigbee-ips", + 17777: "sw-orion", + 18000: "biimenu", + 18104: "radpdf", + 18136: "racf", + 18181: "opsec-cvp", + 18182: "opsec-ufp", + 18183: "opsec-sam", + 18184: "opsec-lea", + 18185: "opsec-omi", + 18186: "ohsc", + 18187: "opsec-ela", + 18241: "checkpoint-rtm", + 18242: "iclid", + 18243: "clusterxl", + 18262: "gv-pf", + 18463: "ac-cluster", + 18634: "rds-ib", + 18635: "rds-ip", + 18668: "vdmmesh", + 18769: "ique", + 18881: "infotos", + 18888: "apc-necmp", + 19000: "igrid", + 19007: "scintilla", + 19020: "j-link", + 19191: "opsec-uaa", + 19194: "ua-secureagent", + 19220: "cora", + 19283: "keysrvr", + 19315: "keyshadow", + 19398: "mtrgtrans", + 19410: "hp-sco", + 19411: "hp-sca", + 19412: "hp-sessmon", + 19539: "fxuptp", + 19540: "sxuptp", + 19541: "jcp", + 19998: "iec-104-sec", + 19999: "dnp-sec", + 20000: "dnp", + 20001: "microsan", + 20002: "commtact-http", + 20003: "commtact-https", + 20005: "openwebnet", + 20013: "ss-idi", + 20014: "opendeploy", + 20034: "nburn-id", + 20046: "tmophl7mts", + 20048: "mountd", + 20049: "nfsrdma", + 20057: "avesterra", + 20167: "tolfab", + 20202: "ipdtp-port", + 20222: "ipulse-ics", + 20480: "emwavemsg", + 20670: "track", + 20999: "athand-mmp", + 21000: "irtrans", + 21010: "notezilla-lan", + 21553: "rdm-tfs", + 21554: "dfserver", + 21590: "vofr-gateway", + 21800: "tvpm", + 21845: "webphone", + 21846: "netspeak-is", + 21847: "netspeak-cs", + 21848: "netspeak-acd", + 21849: "netspeak-cps", + 22000: "snapenetio", + 22001: "optocontrol", + 22002: "optohost002", + 22003: "optohost003", + 22004: "optohost004", + 22005: "optohost004", + 22125: "dcap", + 22128: "gsidcap", + 22222: "easyengine", + 22273: "wnn6", + 22305: "cis", + 22335: "shrewd-control", + 22343: "cis-secure", + 22347: "wibukey", + 22350: "codemeter", + 22351: "codemeter-cmwan", + 22537: "caldsoft-backup", + 22555: "vocaltec-wconf", + 22763: "talikaserver", + 22800: "aws-brf", + 22951: "brf-gw", + 23000: "inovaport1", + 23001: "inovaport2", + 23002: "inovaport3", + 23003: "inovaport4", + 23004: "inovaport5", + 23005: "inovaport6", + 23053: "gntp", + 23294: "5afe-dir", + 23333: "elxmgmt", + 23400: "novar-dbase", + 23401: "novar-alarm", + 23402: "novar-global", + 23456: "aequus", + 23457: "aequus-alt", + 23546: "areaguard-neo", + 24000: "med-ltp", + 24001: "med-fsp-rx", + 24002: "med-fsp-tx", + 24003: "med-supp", + 24004: "med-ovw", + 24005: "med-ci", + 24006: "med-net-svc", + 24242: "filesphere", + 24249: "vista-4gl", + 24321: "ild", + 24386: "intel-rci", + 24465: "tonidods", + 24554: "binkp", + 24577: "bilobit", + 24666: "sdtvwcam", + 24676: "canditv", + 24677: "flashfiler", + 24678: "proactivate", + 24680: "tcc-http", + 24754: "cslg", + 24922: "find", + 25000: "icl-twobase1", + 25001: "icl-twobase2", + 25002: "icl-twobase3", + 25003: "icl-twobase4", + 25004: "icl-twobase5", + 25005: "icl-twobase6", + 25006: "icl-twobase7", + 25007: "icl-twobase8", + 25008: "icl-twobase9", + 25009: "icl-twobase10", + 25576: "sauterdongle", + 25604: "idtp", + 25793: "vocaltec-hos", + 25900: "tasp-net", + 25901: "niobserver", + 25902: "nilinkanalyst", + 25903: "niprobe", + 26000: "quake", + 26133: "scscp", + 26208: "wnn6-ds", + 26257: "cockroach", + 26260: "ezproxy", + 26261: "ezmeeting", + 26262: "k3software-svr", + 26263: "k3software-cli", + 26486: "exoline-tcp", + 26487: "exoconfig", + 26489: "exonet", + 27345: "imagepump", + 27442: "jesmsjc", + 27504: "kopek-httphead", + 27782: "ars-vista", + 27876: "astrolink", + 27999: "tw-auth-key", + 28000: "nxlmd", + 28001: "pqsp", + 28200: "voxelstorm", + 28240: "siemensgsm", + 28589: "bosswave", + 29167: "otmp", + 29999: "bingbang", + 30000: "ndmps", + 30001: "pago-services1", + 30002: "pago-services2", + 30003: "amicon-fpsu-ra", + 30100: "rwp", + 30260: "kingdomsonline", + 30999: "ovobs", + 31016: "ka-sddp", + 31020: "autotrac-acp", + 31400: "pace-licensed", + 31416: "xqosd", + 31457: "tetrinet", + 31620: "lm-mon", + 31685: "dsx-monitor", + 31765: "gamesmith-port", + 31948: "iceedcp-tx", + 31949: "iceedcp-rx", + 32034: "iracinghelper", + 32249: "t1distproc60", + 32400: "plex", + 32483: "apm-link", + 32635: "sec-ntb-clnt", + 32636: "DMExpress", + 32767: "filenet-powsrm", + 32768: "filenet-tms", + 32769: "filenet-rpc", + 32770: "filenet-nch", + 32771: "filenet-rmi", + 32772: "filenet-pa", + 32773: "filenet-cm", + 32774: "filenet-re", + 32775: "filenet-pch", + 32776: "filenet-peior", + 32777: "filenet-obrok", + 32801: "mlsn", + 32811: "retp", + 32896: "idmgratm", + 33060: "mysqlx", + 33123: "aurora-balaena", + 33331: "diamondport", + 33333: "dgi-serv", + 33334: "speedtrace", + 33434: "traceroute", + 33656: "snip-slave", + 34249: "turbonote-2", + 34378: "p-net-local", + 34379: "p-net-remote", + 34567: "dhanalakshmi", + 34962: "profinet-rt", + 34963: "profinet-rtm", + 34964: "profinet-cm", + 34980: "ethercat", + 35000: "heathview", + 35001: "rt-viewer", + 35002: "rt-sound", + 35003: "rt-devicemapper", + 35004: "rt-classmanager", + 35005: "rt-labtracker", + 35006: "rt-helper", + 35100: "axio-disc", + 35354: "kitim", + 35355: "altova-lm", + 35356: "guttersnex", + 35357: "openstack-id", + 36001: "allpeers", + 36524: "febooti-aw", + 36602: "observium-agent", + 36700: "mapx", + 36865: "kastenxpipe", + 37475: "neckar", + 37483: "gdrive-sync", + 37601: "eftp", + 37654: "unisys-eportal", + 38000: "ivs-database", + 38001: "ivs-insertion", + 38002: "cresco-control", + 38201: "galaxy7-data", + 38202: "fairview", + 38203: "agpolicy", + 38800: "sruth", + 38865: "secrmmsafecopya", + 39681: "turbonote-1", + 40000: "safetynetp", + 40404: "sptx", + 40841: "cscp", + 40842: "csccredir", + 40843: "csccfirewall", + 41111: "fs-qos", + 41121: "tentacle", + 41230: "z-wave-s", + 41794: "crestron-cip", + 41795: "crestron-ctp", + 41796: "crestron-cips", + 41797: "crestron-ctps", + 42508: "candp", + 42509: "candrp", + 42510: "caerpc", + 43000: "recvr-rc", + 43188: "reachout", + 43189: "ndm-agent-port", + 43190: "ip-provision", + 43191: "noit-transport", + 43210: "shaperai", + 43439: "eq3-update", + 43440: "ew-mgmt", + 43441: "ciscocsdb", + 44123: "z-wave-tunnel", + 44321: "pmcd", + 44322: "pmcdproxy", + 44323: "pmwebapi", + 44444: "cognex-dataman", + 44553: "rbr-debug", + 44818: "EtherNet-IP-2", + 44900: "m3da", + 45000: "asmp", + 45001: "asmps", + 45002: "rs-status", + 45045: "synctest", + 45054: "invision-ag", + 45514: "cloudcheck", + 45678: "eba", + 45824: "dai-shell", + 45825: "qdb2service", + 45966: "ssr-servermgr", + 46336: "inedo", + 46998: "spremotetablet", + 46999: "mediabox", + 47000: "mbus", + 47001: "winrm", + 47557: "dbbrowse", + 47624: "directplaysrvr", + 47806: "ap", + 47808: "bacnet", + 48000: "nimcontroller", + 48001: "nimspooler", + 48002: "nimhub", + 48003: "nimgtw", + 48004: "nimbusdb", + 48005: "nimbusdbctrl", + 48049: "3gpp-cbsp", + 48050: "weandsf", + 48128: "isnetserv", + 48129: "blp5", + 48556: "com-bardac-dw", + 48619: "iqobject", + 48653: "robotraconteur", + 49000: "matahari", +} +var udpPortNames = map[UDPPort]string{ + 1: "tcpmux", + 2: "compressnet", + 3: "compressnet", + 5: "rje", + 7: "echo", + 9: "discard", + 11: "systat", + 13: "daytime", + 17: "qotd", + 18: "msp", + 19: "chargen", + 20: "ftp-data", + 21: "ftp", + 22: "ssh", + 23: "telnet", + 25: "smtp", + 27: "nsw-fe", + 29: "msg-icp", + 31: "msg-auth", + 33: "dsp", + 37: "time", + 38: "rap", + 39: "rlp", + 41: "graphics", + 42: "name", + 43: "nicname", + 44: "mpm-flags", + 45: "mpm", + 46: "mpm-snd", + 47: "ni-ftp", + 48: "auditd", + 49: "tacacs", + 50: "re-mail-ck", + 52: "xns-time", + 53: "domain", + 54: "xns-ch", + 55: "isi-gl", + 56: "xns-auth", + 58: "xns-mail", + 61: "ni-mail", + 62: "acas", + 63: "whoispp", + 64: "covia", + 65: "tacacs-ds", + 66: "sql-net", + 67: "bootps", + 68: "bootpc", + 69: "tftp", + 70: "gopher", + 71: "netrjs-1", + 72: "netrjs-2", + 73: "netrjs-3", + 74: "netrjs-4", + 76: "deos", + 78: "vettcp", + 79: "finger", + 80: "http", + 82: "xfer", + 83: "mit-ml-dev", + 84: "ctf", + 85: "mit-ml-dev", + 86: "mfcobol", + 88: "kerberos", + 89: "su-mit-tg", + 90: "dnsix", + 91: "mit-dov", + 92: "npp", + 93: "dcp", + 94: "objcall", + 95: "supdup", + 96: "dixie", + 97: "swift-rvf", + 98: "tacnews", + 99: "metagram", + 101: "hostname", + 102: "iso-tsap", + 103: "gppitnp", + 104: "acr-nema", + 105: "cso", + 106: "3com-tsmux", + 107: "rtelnet", + 108: "snagas", + 109: "pop2", + 110: "pop3", + 111: "sunrpc", + 112: "mcidas", + 113: "auth", + 115: "sftp", + 116: "ansanotify", + 117: "uucp-path", + 118: "sqlserv", + 119: "nntp", + 120: "cfdptkt", + 121: "erpc", + 122: "smakynet", + 123: "ntp", + 124: "ansatrader", + 125: "locus-map", + 126: "nxedit", + 127: "locus-con", + 128: "gss-xlicen", + 129: "pwdgen", + 130: "cisco-fna", + 131: "cisco-tna", + 132: "cisco-sys", + 133: "statsrv", + 134: "ingres-net", + 135: "epmap", + 136: "profile", + 137: "netbios-ns", + 138: "netbios-dgm", + 139: "netbios-ssn", + 140: "emfis-data", + 141: "emfis-cntl", + 142: "bl-idm", + 143: "imap", + 144: "uma", + 145: "uaac", + 146: "iso-tp0", + 147: "iso-ip", + 148: "jargon", + 149: "aed-512", + 150: "sql-net", + 151: "hems", + 152: "bftp", + 153: "sgmp", + 154: "netsc-prod", + 155: "netsc-dev", + 156: "sqlsrv", + 157: "knet-cmp", + 158: "pcmail-srv", + 159: "nss-routing", + 160: "sgmp-traps", + 161: "snmp", + 162: "snmptrap", + 163: "cmip-man", + 164: "cmip-agent", + 165: "xns-courier", + 166: "s-net", + 167: "namp", + 168: "rsvd", + 169: "send", + 170: "print-srv", + 171: "multiplex", + 172: "cl-1", + 173: "xyplex-mux", + 174: "mailq", + 175: "vmnet", + 176: "genrad-mux", + 177: "xdmcp", + 178: "nextstep", + 179: "bgp", + 180: "ris", + 181: "unify", + 182: "audit", + 183: "ocbinder", + 184: "ocserver", + 185: "remote-kis", + 186: "kis", + 187: "aci", + 188: "mumps", + 189: "qft", + 190: "gacp", + 191: "prospero", + 192: "osu-nms", + 193: "srmp", + 194: "irc", + 195: "dn6-nlm-aud", + 196: "dn6-smm-red", + 197: "dls", + 198: "dls-mon", + 199: "smux", + 200: "src", + 201: "at-rtmp", + 202: "at-nbp", + 203: "at-3", + 204: "at-echo", + 205: "at-5", + 206: "at-zis", + 207: "at-7", + 208: "at-8", + 209: "qmtp", + 210: "z39-50", + 211: "914c-g", + 212: "anet", + 213: "ipx", + 214: "vmpwscs", + 215: "softpc", + 216: "CAIlic", + 217: "dbase", + 218: "mpp", + 219: "uarps", + 220: "imap3", + 221: "fln-spx", + 222: "rsh-spx", + 223: "cdc", + 224: "masqdialer", + 242: "direct", + 243: "sur-meas", + 244: "inbusiness", + 245: "link", + 246: "dsp3270", + 247: "subntbcst-tftp", + 248: "bhfhs", + 256: "rap", + 257: "set", + 259: "esro-gen", + 260: "openport", + 261: "nsiiops", + 262: "arcisdms", + 263: "hdap", + 264: "bgmp", + 265: "x-bone-ctl", + 266: "sst", + 267: "td-service", + 268: "td-replica", + 269: "manet", + 270: "gist", + 280: "http-mgmt", + 281: "personal-link", + 282: "cableport-ax", + 283: "rescap", + 284: "corerjd", + 286: "fxp", + 287: "k-block", + 308: "novastorbakcup", + 309: "entrusttime", + 310: "bhmds", + 311: "asip-webadmin", + 312: "vslmp", + 313: "magenta-logic", + 314: "opalis-robot", + 315: "dpsi", + 316: "decauth", + 317: "zannet", + 318: "pkix-timestamp", + 319: "ptp-event", + 320: "ptp-general", + 321: "pip", + 322: "rtsps", + 333: "texar", + 344: "pdap", + 345: "pawserv", + 346: "zserv", + 347: "fatserv", + 348: "csi-sgwp", + 349: "mftp", + 350: "matip-type-a", + 351: "matip-type-b", + 352: "dtag-ste-sb", + 353: "ndsauth", + 354: "bh611", + 355: "datex-asn", + 356: "cloanto-net-1", + 357: "bhevent", + 358: "shrinkwrap", + 359: "nsrmp", + 360: "scoi2odialog", + 361: "semantix", + 362: "srssend", + 363: "rsvp-tunnel", + 364: "aurora-cmgr", + 365: "dtk", + 366: "odmr", + 367: "mortgageware", + 368: "qbikgdp", + 369: "rpc2portmap", + 370: "codaauth2", + 371: "clearcase", + 372: "ulistproc", + 373: "legent-1", + 374: "legent-2", + 375: "hassle", + 376: "nip", + 377: "tnETOS", + 378: "dsETOS", + 379: "is99c", + 380: "is99s", + 381: "hp-collector", + 382: "hp-managed-node", + 383: "hp-alarm-mgr", + 384: "arns", + 385: "ibm-app", + 386: "asa", + 387: "aurp", + 388: "unidata-ldm", + 389: "ldap", + 390: "uis", + 391: "synotics-relay", + 392: "synotics-broker", + 393: "meta5", + 394: "embl-ndt", + 395: "netcp", + 396: "netware-ip", + 397: "mptn", + 398: "kryptolan", + 399: "iso-tsap-c2", + 400: "osb-sd", + 401: "ups", + 402: "genie", + 403: "decap", + 404: "nced", + 405: "ncld", + 406: "imsp", + 407: "timbuktu", + 408: "prm-sm", + 409: "prm-nm", + 410: "decladebug", + 411: "rmt", + 412: "synoptics-trap", + 413: "smsp", + 414: "infoseek", + 415: "bnet", + 416: "silverplatter", + 417: "onmux", + 418: "hyper-g", + 419: "ariel1", + 420: "smpte", + 421: "ariel2", + 422: "ariel3", + 423: "opc-job-start", + 424: "opc-job-track", + 425: "icad-el", + 426: "smartsdp", + 427: "svrloc", + 428: "ocs-cmu", + 429: "ocs-amu", + 430: "utmpsd", + 431: "utmpcd", + 432: "iasd", + 433: "nnsp", + 434: "mobileip-agent", + 435: "mobilip-mn", + 436: "dna-cml", + 437: "comscm", + 438: "dsfgw", + 439: "dasp", + 440: "sgcp", + 441: "decvms-sysmgt", + 442: "cvc-hostd", + 443: "https", + 444: "snpp", + 445: "microsoft-ds", + 446: "ddm-rdb", + 447: "ddm-dfm", + 448: "ddm-ssl", + 449: "as-servermap", + 450: "tserver", + 451: "sfs-smp-net", + 452: "sfs-config", + 453: "creativeserver", + 454: "contentserver", + 455: "creativepartnr", + 456: "macon-udp", + 457: "scohelp", + 458: "appleqtc", + 459: "ampr-rcmd", + 460: "skronk", + 461: "datasurfsrv", + 462: "datasurfsrvsec", + 463: "alpes", + 464: "kpasswd", + 465: "igmpv3lite", + 466: "digital-vrc", + 467: "mylex-mapd", + 468: "photuris", + 469: "rcp", + 470: "scx-proxy", + 471: "mondex", + 472: "ljk-login", + 473: "hybrid-pop", + 474: "tn-tl-w2", + 475: "tcpnethaspsrv", + 476: "tn-tl-fd1", + 477: "ss7ns", + 478: "spsc", + 479: "iafserver", + 480: "iafdbase", + 481: "ph", + 482: "bgs-nsi", + 483: "ulpnet", + 484: "integra-sme", + 485: "powerburst", + 486: "avian", + 487: "saft", + 488: "gss-http", + 489: "nest-protocol", + 490: "micom-pfs", + 491: "go-login", + 492: "ticf-1", + 493: "ticf-2", + 494: "pov-ray", + 495: "intecourier", + 496: "pim-rp-disc", + 497: "retrospect", + 498: "siam", + 499: "iso-ill", + 500: "isakmp", + 501: "stmf", + 502: "mbap", + 503: "intrinsa", + 504: "citadel", + 505: "mailbox-lm", + 506: "ohimsrv", + 507: "crs", + 508: "xvttp", + 509: "snare", + 510: "fcp", + 511: "passgo", + 512: "comsat", + 513: "who", + 514: "syslog", + 515: "printer", + 516: "videotex", + 517: "talk", + 518: "ntalk", + 519: "utime", + 520: "router", + 521: "ripng", + 522: "ulp", + 523: "ibm-db2", + 524: "ncp", + 525: "timed", + 526: "tempo", + 527: "stx", + 528: "custix", + 529: "irc-serv", + 530: "courier", + 531: "conference", + 532: "netnews", + 533: "netwall", + 534: "windream", + 535: "iiop", + 536: "opalis-rdv", + 537: "nmsp", + 538: "gdomap", + 539: "apertus-ldp", + 540: "uucp", + 541: "uucp-rlogin", + 542: "commerce", + 543: "klogin", + 544: "kshell", + 545: "appleqtcsrvr", + 546: "dhcpv6-client", + 547: "dhcpv6-server", + 548: "afpovertcp", + 549: "idfp", + 550: "new-rwho", + 551: "cybercash", + 552: "devshr-nts", + 553: "pirp", + 554: "rtsp", + 555: "dsf", + 556: "remotefs", + 557: "openvms-sysipc", + 558: "sdnskmp", + 559: "teedtap", + 560: "rmonitor", + 561: "monitor", + 562: "chshell", + 563: "nntps", + 564: "9pfs", + 565: "whoami", + 566: "streettalk", + 567: "banyan-rpc", + 568: "ms-shuttle", + 569: "ms-rome", + 570: "meter", + 571: "meter", + 572: "sonar", + 573: "banyan-vip", + 574: "ftp-agent", + 575: "vemmi", + 576: "ipcd", + 577: "vnas", + 578: "ipdd", + 579: "decbsrv", + 580: "sntp-heartbeat", + 581: "bdp", + 582: "scc-security", + 583: "philips-vc", + 584: "keyserver", + 586: "password-chg", + 587: "submission", + 588: "cal", + 589: "eyelink", + 590: "tns-cml", + 591: "http-alt", + 592: "eudora-set", + 593: "http-rpc-epmap", + 594: "tpip", + 595: "cab-protocol", + 596: "smsd", + 597: "ptcnameservice", + 598: "sco-websrvrmg3", + 599: "acp", + 600: "ipcserver", + 601: "syslog-conn", + 602: "xmlrpc-beep", + 603: "idxp", + 604: "tunnel", + 605: "soap-beep", + 606: "urm", + 607: "nqs", + 608: "sift-uft", + 609: "npmp-trap", + 610: "npmp-local", + 611: "npmp-gui", + 612: "hmmp-ind", + 613: "hmmp-op", + 614: "sshell", + 615: "sco-inetmgr", + 616: "sco-sysmgr", + 617: "sco-dtmgr", + 618: "dei-icda", + 619: "compaq-evm", + 620: "sco-websrvrmgr", + 621: "escp-ip", + 622: "collaborator", + 623: "asf-rmcp", + 624: "cryptoadmin", + 625: "dec-dlm", + 626: "asia", + 627: "passgo-tivoli", + 628: "qmqp", + 629: "3com-amp3", + 630: "rda", + 631: "ipp", + 632: "bmpp", + 633: "servstat", + 634: "ginad", + 635: "rlzdbase", + 636: "ldaps", + 637: "lanserver", + 638: "mcns-sec", + 639: "msdp", + 640: "entrust-sps", + 641: "repcmd", + 642: "esro-emsdp", + 643: "sanity", + 644: "dwr", + 645: "pssc", + 646: "ldp", + 647: "dhcp-failover", + 648: "rrp", + 649: "cadview-3d", + 650: "obex", + 651: "ieee-mms", + 652: "hello-port", + 653: "repscmd", + 654: "aodv", + 655: "tinc", + 656: "spmp", + 657: "rmc", + 658: "tenfold", + 660: "mac-srvr-admin", + 661: "hap", + 662: "pftp", + 663: "purenoise", + 664: "asf-secure-rmcp", + 665: "sun-dr", + 666: "mdqs", + 667: "disclose", + 668: "mecomm", + 669: "meregister", + 670: "vacdsm-sws", + 671: "vacdsm-app", + 672: "vpps-qua", + 673: "cimplex", + 674: "acap", + 675: "dctp", + 676: "vpps-via", + 677: "vpp", + 678: "ggf-ncp", + 679: "mrm", + 680: "entrust-aaas", + 681: "entrust-aams", + 682: "xfr", + 683: "corba-iiop", + 684: "corba-iiop-ssl", + 685: "mdc-portmapper", + 686: "hcp-wismar", + 687: "asipregistry", + 688: "realm-rusd", + 689: "nmap", + 690: "vatp", + 691: "msexch-routing", + 692: "hyperwave-isp", + 693: "connendp", + 694: "ha-cluster", + 695: "ieee-mms-ssl", + 696: "rushd", + 697: "uuidgen", + 698: "olsr", + 699: "accessnetwork", + 700: "epp", + 701: "lmp", + 702: "iris-beep", + 704: "elcsd", + 705: "agentx", + 706: "silc", + 707: "borland-dsj", + 709: "entrust-kmsh", + 710: "entrust-ash", + 711: "cisco-tdp", + 712: "tbrpf", + 713: "iris-xpc", + 714: "iris-xpcs", + 715: "iris-lwz", + 716: "pana", + 729: "netviewdm1", + 730: "netviewdm2", + 731: "netviewdm3", + 741: "netgw", + 742: "netrcs", + 744: "flexlm", + 747: "fujitsu-dev", + 748: "ris-cm", + 749: "kerberos-adm", + 750: "loadav", + 751: "pump", + 752: "qrh", + 753: "rrh", + 754: "tell", + 758: "nlogin", + 759: "con", + 760: "ns", + 761: "rxe", + 762: "quotad", + 763: "cycleserv", + 764: "omserv", + 765: "webster", + 767: "phonebook", + 769: "vid", + 770: "cadlock", + 771: "rtip", + 772: "cycleserv2", + 773: "notify", + 774: "acmaint-dbd", + 775: "acmaint-transd", + 776: "wpages", + 777: "multiling-http", + 780: "wpgs", + 800: "mdbs-daemon", + 801: "device", + 802: "mbap-s", + 810: "fcp-udp", + 828: "itm-mcell-s", + 829: "pkix-3-ca-ra", + 830: "netconf-ssh", + 831: "netconf-beep", + 832: "netconfsoaphttp", + 833: "netconfsoapbeep", + 847: "dhcp-failover2", + 848: "gdoi", + 853: "domain-s", + 860: "iscsi", + 861: "owamp-control", + 862: "twamp-control", + 873: "rsync", + 886: "iclcnet-locate", + 887: "iclcnet-svinfo", + 888: "accessbuilder", + 900: "omginitialrefs", + 901: "smpnameres", + 902: "ideafarm-door", + 903: "ideafarm-panic", + 910: "kink", + 911: "xact-backup", + 912: "apex-mesh", + 913: "apex-edge", + 989: "ftps-data", + 990: "ftps", + 991: "nas", + 992: "telnets", + 993: "imaps", + 995: "pop3s", + 996: "vsinet", + 997: "maitrd", + 998: "puparp", + 999: "applix", + 1000: "cadlock2", + 1010: "surf", + 1021: "exp1", + 1022: "exp2", + 1025: "blackjack", + 1026: "cap", + 1027: "6a44", + 1029: "solid-mux", + 1033: "netinfo-local", + 1034: "activesync", + 1035: "mxxrlogin", + 1036: "nsstp", + 1037: "ams", + 1038: "mtqp", + 1039: "sbl", + 1040: "netarx", + 1041: "danf-ak2", + 1042: "afrog", + 1043: "boinc-client", + 1044: "dcutility", + 1045: "fpitp", + 1046: "wfremotertm", + 1047: "neod1", + 1048: "neod2", + 1049: "td-postman", + 1050: "cma", + 1051: "optima-vnet", + 1052: "ddt", + 1053: "remote-as", + 1054: "brvread", + 1055: "ansyslmd", + 1056: "vfo", + 1057: "startron", + 1058: "nim", + 1059: "nimreg", + 1060: "polestar", + 1061: "kiosk", + 1062: "veracity", + 1063: "kyoceranetdev", + 1064: "jstel", + 1065: "syscomlan", + 1066: "fpo-fns", + 1067: "instl-boots", + 1068: "instl-bootc", + 1069: "cognex-insight", + 1070: "gmrupdateserv", + 1071: "bsquare-voip", + 1072: "cardax", + 1073: "bridgecontrol", + 1074: "warmspotMgmt", + 1075: "rdrmshc", + 1076: "dab-sti-c", + 1077: "imgames", + 1078: "avocent-proxy", + 1079: "asprovatalk", + 1080: "socks", + 1081: "pvuniwien", + 1082: "amt-esd-prot", + 1083: "ansoft-lm-1", + 1084: "ansoft-lm-2", + 1085: "webobjects", + 1086: "cplscrambler-lg", + 1087: "cplscrambler-in", + 1088: "cplscrambler-al", + 1089: "ff-annunc", + 1090: "ff-fms", + 1091: "ff-sm", + 1092: "obrpd", + 1093: "proofd", + 1094: "rootd", + 1095: "nicelink", + 1096: "cnrprotocol", + 1097: "sunclustermgr", + 1098: "rmiactivation", + 1099: "rmiregistry", + 1100: "mctp", + 1101: "pt2-discover", + 1102: "adobeserver-1", + 1103: "adobeserver-2", + 1104: "xrl", + 1105: "ftranhc", + 1106: "isoipsigport-1", + 1107: "isoipsigport-2", + 1108: "ratio-adp", + 1110: "nfsd-keepalive", + 1111: "lmsocialserver", + 1112: "icp", + 1113: "ltp-deepspace", + 1114: "mini-sql", + 1115: "ardus-trns", + 1116: "ardus-cntl", + 1117: "ardus-mtrns", + 1118: "sacred", + 1119: "bnetgame", + 1120: "bnetfile", + 1121: "rmpp", + 1122: "availant-mgr", + 1123: "murray", + 1124: "hpvmmcontrol", + 1125: "hpvmmagent", + 1126: "hpvmmdata", + 1127: "kwdb-commn", + 1128: "saphostctrl", + 1129: "saphostctrls", + 1130: "casp", + 1131: "caspssl", + 1132: "kvm-via-ip", + 1133: "dfn", + 1134: "aplx", + 1135: "omnivision", + 1136: "hhb-gateway", + 1137: "trim", + 1138: "encrypted-admin", + 1139: "evm", + 1140: "autonoc", + 1141: "mxomss", + 1142: "edtools", + 1143: "imyx", + 1144: "fuscript", + 1145: "x9-icue", + 1146: "audit-transfer", + 1147: "capioverlan", + 1148: "elfiq-repl", + 1149: "bvtsonar", + 1150: "blaze", + 1151: "unizensus", + 1152: "winpoplanmess", + 1153: "c1222-acse", + 1154: "resacommunity", + 1155: "nfa", + 1156: "iascontrol-oms", + 1157: "iascontrol", + 1158: "dbcontrol-oms", + 1159: "oracle-oms", + 1160: "olsv", + 1161: "health-polling", + 1162: "health-trap", + 1163: "sddp", + 1164: "qsm-proxy", + 1165: "qsm-gui", + 1166: "qsm-remote", + 1167: "cisco-ipsla", + 1168: "vchat", + 1169: "tripwire", + 1170: "atc-lm", + 1171: "atc-appserver", + 1172: "dnap", + 1173: "d-cinema-rrp", + 1174: "fnet-remote-ui", + 1175: "dossier", + 1176: "indigo-server", + 1177: "dkmessenger", + 1178: "sgi-storman", + 1179: "b2n", + 1180: "mc-client", + 1181: "3comnetman", + 1182: "accelenet-data", + 1183: "llsurfup-http", + 1184: "llsurfup-https", + 1185: "catchpole", + 1186: "mysql-cluster", + 1187: "alias", + 1188: "hp-webadmin", + 1189: "unet", + 1190: "commlinx-avl", + 1191: "gpfs", + 1192: "caids-sensor", + 1193: "fiveacross", + 1194: "openvpn", + 1195: "rsf-1", + 1196: "netmagic", + 1197: "carrius-rshell", + 1198: "cajo-discovery", + 1199: "dmidi", + 1200: "scol", + 1201: "nucleus-sand", + 1202: "caiccipc", + 1203: "ssslic-mgr", + 1204: "ssslog-mgr", + 1205: "accord-mgc", + 1206: "anthony-data", + 1207: "metasage", + 1208: "seagull-ais", + 1209: "ipcd3", + 1210: "eoss", + 1211: "groove-dpp", + 1212: "lupa", + 1213: "mpc-lifenet", + 1214: "kazaa", + 1215: "scanstat-1", + 1216: "etebac5", + 1217: "hpss-ndapi", + 1218: "aeroflight-ads", + 1219: "aeroflight-ret", + 1220: "qt-serveradmin", + 1221: "sweetware-apps", + 1222: "nerv", + 1223: "tgp", + 1224: "vpnz", + 1225: "slinkysearch", + 1226: "stgxfws", + 1227: "dns2go", + 1228: "florence", + 1229: "zented", + 1230: "periscope", + 1231: "menandmice-lpm", + 1232: "first-defense", + 1233: "univ-appserver", + 1234: "search-agent", + 1235: "mosaicsyssvc1", + 1236: "bvcontrol", + 1237: "tsdos390", + 1238: "hacl-qs", + 1239: "nmsd", + 1240: "instantia", + 1241: "nessus", + 1242: "nmasoverip", + 1243: "serialgateway", + 1244: "isbconference1", + 1245: "isbconference2", + 1246: "payrouter", + 1247: "visionpyramid", + 1248: "hermes", + 1249: "mesavistaco", + 1250: "swldy-sias", + 1251: "servergraph", + 1252: "bspne-pcc", + 1253: "q55-pcc", + 1254: "de-noc", + 1255: "de-cache-query", + 1256: "de-server", + 1257: "shockwave2", + 1258: "opennl", + 1259: "opennl-voice", + 1260: "ibm-ssd", + 1261: "mpshrsv", + 1262: "qnts-orb", + 1263: "dka", + 1264: "prat", + 1265: "dssiapi", + 1266: "dellpwrappks", + 1267: "epc", + 1268: "propel-msgsys", + 1269: "watilapp", + 1270: "opsmgr", + 1271: "excw", + 1272: "cspmlockmgr", + 1273: "emc-gateway", + 1274: "t1distproc", + 1275: "ivcollector", + 1277: "miva-mqs", + 1278: "dellwebadmin-1", + 1279: "dellwebadmin-2", + 1280: "pictrography", + 1281: "healthd", + 1282: "emperion", + 1283: "productinfo", + 1284: "iee-qfx", + 1285: "neoiface", + 1286: "netuitive", + 1287: "routematch", + 1288: "navbuddy", + 1289: "jwalkserver", + 1290: "winjaserver", + 1291: "seagulllms", + 1292: "dsdn", + 1293: "pkt-krb-ipsec", + 1294: "cmmdriver", + 1295: "ehtp", + 1296: "dproxy", + 1297: "sdproxy", + 1298: "lpcp", + 1299: "hp-sci", + 1300: "h323hostcallsc", + 1301: "ci3-software-1", + 1302: "ci3-software-2", + 1303: "sftsrv", + 1304: "boomerang", + 1305: "pe-mike", + 1306: "re-conn-proto", + 1307: "pacmand", + 1308: "odsi", + 1309: "jtag-server", + 1310: "husky", + 1311: "rxmon", + 1312: "sti-envision", + 1313: "bmc-patroldb", + 1314: "pdps", + 1315: "els", + 1316: "exbit-escp", + 1317: "vrts-ipcserver", + 1318: "krb5gatekeeper", + 1319: "amx-icsp", + 1320: "amx-axbnet", + 1321: "pip", + 1322: "novation", + 1323: "brcd", + 1324: "delta-mcp", + 1325: "dx-instrument", + 1326: "wimsic", + 1327: "ultrex", + 1328: "ewall", + 1329: "netdb-export", + 1330: "streetperfect", + 1331: "intersan", + 1332: "pcia-rxp-b", + 1333: "passwrd-policy", + 1334: "writesrv", + 1335: "digital-notary", + 1336: "ischat", + 1337: "menandmice-dns", + 1338: "wmc-log-svc", + 1339: "kjtsiteserver", + 1340: "naap", + 1341: "qubes", + 1342: "esbroker", + 1343: "re101", + 1344: "icap", + 1345: "vpjp", + 1346: "alta-ana-lm", + 1347: "bbn-mmc", + 1348: "bbn-mmx", + 1349: "sbook", + 1350: "editbench", + 1351: "equationbuilder", + 1352: "lotusnote", + 1353: "relief", + 1354: "XSIP-network", + 1355: "intuitive-edge", + 1356: "cuillamartin", + 1357: "pegboard", + 1358: "connlcli", + 1359: "ftsrv", + 1360: "mimer", + 1361: "linx", + 1362: "timeflies", + 1363: "ndm-requester", + 1364: "ndm-server", + 1365: "adapt-sna", + 1366: "netware-csp", + 1367: "dcs", + 1368: "screencast", + 1369: "gv-us", + 1370: "us-gv", + 1371: "fc-cli", + 1372: "fc-ser", + 1373: "chromagrafx", + 1374: "molly", + 1375: "bytex", + 1376: "ibm-pps", + 1377: "cichlid", + 1378: "elan", + 1379: "dbreporter", + 1380: "telesis-licman", + 1381: "apple-licman", + 1382: "udt-os", + 1383: "gwha", + 1384: "os-licman", + 1385: "atex-elmd", + 1386: "checksum", + 1387: "cadsi-lm", + 1388: "objective-dbc", + 1389: "iclpv-dm", + 1390: "iclpv-sc", + 1391: "iclpv-sas", + 1392: "iclpv-pm", + 1393: "iclpv-nls", + 1394: "iclpv-nlc", + 1395: "iclpv-wsm", + 1396: "dvl-activemail", + 1397: "audio-activmail", + 1398: "video-activmail", + 1399: "cadkey-licman", + 1400: "cadkey-tablet", + 1401: "goldleaf-licman", + 1402: "prm-sm-np", + 1403: "prm-nm-np", + 1404: "igi-lm", + 1405: "ibm-res", + 1406: "netlabs-lm", + 1408: "sophia-lm", + 1409: "here-lm", + 1410: "hiq", + 1411: "af", + 1412: "innosys", + 1413: "innosys-acl", + 1414: "ibm-mqseries", + 1415: "dbstar", + 1416: "novell-lu6-2", + 1417: "timbuktu-srv1", + 1418: "timbuktu-srv2", + 1419: "timbuktu-srv3", + 1420: "timbuktu-srv4", + 1421: "gandalf-lm", + 1422: "autodesk-lm", + 1423: "essbase", + 1424: "hybrid", + 1425: "zion-lm", + 1426: "sais", + 1427: "mloadd", + 1428: "informatik-lm", + 1429: "nms", + 1430: "tpdu", + 1431: "rgtp", + 1432: "blueberry-lm", + 1433: "ms-sql-s", + 1434: "ms-sql-m", + 1435: "ibm-cics", + 1436: "saism", + 1437: "tabula", + 1438: "eicon-server", + 1439: "eicon-x25", + 1440: "eicon-slp", + 1441: "cadis-1", + 1442: "cadis-2", + 1443: "ies-lm", + 1444: "marcam-lm", + 1445: "proxima-lm", + 1446: "ora-lm", + 1447: "apri-lm", + 1448: "oc-lm", + 1449: "peport", + 1450: "dwf", + 1451: "infoman", + 1452: "gtegsc-lm", + 1453: "genie-lm", + 1454: "interhdl-elmd", + 1455: "esl-lm", + 1456: "dca", + 1457: "valisys-lm", + 1458: "nrcabq-lm", + 1459: "proshare1", + 1460: "proshare2", + 1461: "ibm-wrless-lan", + 1462: "world-lm", + 1463: "nucleus", + 1464: "msl-lmd", + 1465: "pipes", + 1466: "oceansoft-lm", + 1467: "csdmbase", + 1468: "csdm", + 1469: "aal-lm", + 1470: "uaiact", + 1471: "csdmbase", + 1472: "csdm", + 1473: "openmath", + 1474: "telefinder", + 1475: "taligent-lm", + 1476: "clvm-cfg", + 1477: "ms-sna-server", + 1478: "ms-sna-base", + 1479: "dberegister", + 1480: "pacerforum", + 1481: "airs", + 1482: "miteksys-lm", + 1483: "afs", + 1484: "confluent", + 1485: "lansource", + 1486: "nms-topo-serv", + 1487: "localinfosrvr", + 1488: "docstor", + 1489: "dmdocbroker", + 1490: "insitu-conf", + 1492: "stone-design-1", + 1493: "netmap-lm", + 1494: "ica", + 1495: "cvc", + 1496: "liberty-lm", + 1497: "rfx-lm", + 1498: "sybase-sqlany", + 1499: "fhc", + 1500: "vlsi-lm", + 1501: "saiscm", + 1502: "shivadiscovery", + 1503: "imtc-mcs", + 1504: "evb-elm", + 1505: "funkproxy", + 1506: "utcd", + 1507: "symplex", + 1508: "diagmond", + 1509: "robcad-lm", + 1510: "mvx-lm", + 1511: "3l-l1", + 1512: "wins", + 1513: "fujitsu-dtc", + 1514: "fujitsu-dtcns", + 1515: "ifor-protocol", + 1516: "vpad", + 1517: "vpac", + 1518: "vpvd", + 1519: "vpvc", + 1520: "atm-zip-office", + 1521: "ncube-lm", + 1522: "ricardo-lm", + 1523: "cichild-lm", + 1524: "ingreslock", + 1525: "orasrv", + 1526: "pdap-np", + 1527: "tlisrv", + 1528: "ngr-t", + 1529: "coauthor", + 1530: "rap-service", + 1531: "rap-listen", + 1532: "miroconnect", + 1533: "virtual-places", + 1534: "micromuse-lm", + 1535: "ampr-info", + 1536: "ampr-inter", + 1537: "sdsc-lm", + 1538: "3ds-lm", + 1539: "intellistor-lm", + 1540: "rds", + 1541: "rds2", + 1542: "gridgen-elmd", + 1543: "simba-cs", + 1544: "aspeclmd", + 1545: "vistium-share", + 1546: "abbaccuray", + 1547: "laplink", + 1548: "axon-lm", + 1549: "shivasound", + 1550: "3m-image-lm", + 1551: "hecmtl-db", + 1552: "pciarray", + 1553: "sna-cs", + 1554: "caci-lm", + 1555: "livelan", + 1556: "veritas-pbx", + 1557: "arbortext-lm", + 1558: "xingmpeg", + 1559: "web2host", + 1560: "asci-val", + 1561: "facilityview", + 1562: "pconnectmgr", + 1563: "cadabra-lm", + 1564: "pay-per-view", + 1565: "winddlb", + 1566: "corelvideo", + 1567: "jlicelmd", + 1568: "tsspmap", + 1569: "ets", + 1570: "orbixd", + 1571: "rdb-dbs-disp", + 1572: "chip-lm", + 1573: "itscomm-ns", + 1574: "mvel-lm", + 1575: "oraclenames", + 1576: "moldflow-lm", + 1577: "hypercube-lm", + 1578: "jacobus-lm", + 1579: "ioc-sea-lm", + 1580: "tn-tl-r2", + 1581: "mil-2045-47001", + 1582: "msims", + 1583: "simbaexpress", + 1584: "tn-tl-fd2", + 1585: "intv", + 1586: "ibm-abtact", + 1587: "pra-elmd", + 1588: "triquest-lm", + 1589: "vqp", + 1590: "gemini-lm", + 1591: "ncpm-pm", + 1592: "commonspace", + 1593: "mainsoft-lm", + 1594: "sixtrak", + 1595: "radio", + 1596: "radio-bc", + 1597: "orbplus-iiop", + 1598: "picknfs", + 1599: "simbaservices", + 1600: "issd", + 1601: "aas", + 1602: "inspect", + 1603: "picodbc", + 1604: "icabrowser", + 1605: "slp", + 1606: "slm-api", + 1607: "stt", + 1608: "smart-lm", + 1609: "isysg-lm", + 1610: "taurus-wh", + 1611: "ill", + 1612: "netbill-trans", + 1613: "netbill-keyrep", + 1614: "netbill-cred", + 1615: "netbill-auth", + 1616: "netbill-prod", + 1617: "nimrod-agent", + 1618: "skytelnet", + 1619: "xs-openstorage", + 1620: "faxportwinport", + 1621: "softdataphone", + 1622: "ontime", + 1623: "jaleosnd", + 1624: "udp-sr-port", + 1625: "svs-omagent", + 1626: "shockwave", + 1627: "t128-gateway", + 1628: "lontalk-norm", + 1629: "lontalk-urgnt", + 1630: "oraclenet8cman", + 1631: "visitview", + 1632: "pammratc", + 1633: "pammrpc", + 1634: "loaprobe", + 1635: "edb-server1", + 1636: "isdc", + 1637: "islc", + 1638: "ismc", + 1639: "cert-initiator", + 1640: "cert-responder", + 1641: "invision", + 1642: "isis-am", + 1643: "isis-ambc", + 1644: "saiseh", + 1645: "sightline", + 1646: "sa-msg-port", + 1647: "rsap", + 1648: "concurrent-lm", + 1649: "kermit", + 1650: "nkd", + 1651: "shiva-confsrvr", + 1652: "xnmp", + 1653: "alphatech-lm", + 1654: "stargatealerts", + 1655: "dec-mbadmin", + 1656: "dec-mbadmin-h", + 1657: "fujitsu-mmpdc", + 1658: "sixnetudr", + 1659: "sg-lm", + 1660: "skip-mc-gikreq", + 1661: "netview-aix-1", + 1662: "netview-aix-2", + 1663: "netview-aix-3", + 1664: "netview-aix-4", + 1665: "netview-aix-5", + 1666: "netview-aix-6", + 1667: "netview-aix-7", + 1668: "netview-aix-8", + 1669: "netview-aix-9", + 1670: "netview-aix-10", + 1671: "netview-aix-11", + 1672: "netview-aix-12", + 1673: "proshare-mc-1", + 1674: "proshare-mc-2", + 1675: "pdp", + 1676: "netcomm2", + 1677: "groupwise", + 1678: "prolink", + 1679: "darcorp-lm", + 1680: "microcom-sbp", + 1681: "sd-elmd", + 1682: "lanyon-lantern", + 1683: "ncpm-hip", + 1684: "snaresecure", + 1685: "n2nremote", + 1686: "cvmon", + 1687: "nsjtp-ctrl", + 1688: "nsjtp-data", + 1689: "firefox", + 1690: "ng-umds", + 1691: "empire-empuma", + 1692: "sstsys-lm", + 1693: "rrirtr", + 1694: "rrimwm", + 1695: "rrilwm", + 1696: "rrifmm", + 1697: "rrisat", + 1698: "rsvp-encap-1", + 1699: "rsvp-encap-2", + 1700: "mps-raft", + 1701: "l2f", + 1702: "deskshare", + 1703: "hb-engine", + 1704: "bcs-broker", + 1705: "slingshot", + 1706: "jetform", + 1707: "vdmplay", + 1708: "gat-lmd", + 1709: "centra", + 1710: "impera", + 1711: "pptconference", + 1712: "registrar", + 1713: "conferencetalk", + 1714: "sesi-lm", + 1715: "houdini-lm", + 1716: "xmsg", + 1717: "fj-hdnet", + 1718: "h323gatedisc", + 1719: "h323gatestat", + 1720: "h323hostcall", + 1721: "caicci", + 1722: "hks-lm", + 1723: "pptp", + 1724: "csbphonemaster", + 1725: "iden-ralp", + 1726: "iberiagames", + 1727: "winddx", + 1728: "telindus", + 1729: "citynl", + 1730: "roketz", + 1731: "msiccp", + 1732: "proxim", + 1733: "siipat", + 1734: "cambertx-lm", + 1735: "privatechat", + 1736: "street-stream", + 1737: "ultimad", + 1738: "gamegen1", + 1739: "webaccess", + 1740: "encore", + 1741: "cisco-net-mgmt", + 1742: "3Com-nsd", + 1743: "cinegrfx-lm", + 1744: "ncpm-ft", + 1745: "remote-winsock", + 1746: "ftrapid-1", + 1747: "ftrapid-2", + 1748: "oracle-em1", + 1749: "aspen-services", + 1750: "sslp", + 1751: "swiftnet", + 1752: "lofr-lm", + 1754: "oracle-em2", + 1755: "ms-streaming", + 1756: "capfast-lmd", + 1757: "cnhrp", + 1758: "tftp-mcast", + 1759: "spss-lm", + 1760: "www-ldap-gw", + 1761: "cft-0", + 1762: "cft-1", + 1763: "cft-2", + 1764: "cft-3", + 1765: "cft-4", + 1766: "cft-5", + 1767: "cft-6", + 1768: "cft-7", + 1769: "bmc-net-adm", + 1770: "bmc-net-svc", + 1771: "vaultbase", + 1772: "essweb-gw", + 1773: "kmscontrol", + 1774: "global-dtserv", + 1776: "femis", + 1777: "powerguardian", + 1778: "prodigy-intrnet", + 1779: "pharmasoft", + 1780: "dpkeyserv", + 1781: "answersoft-lm", + 1782: "hp-hcip", + 1784: "finle-lm", + 1785: "windlm", + 1786: "funk-logger", + 1787: "funk-license", + 1788: "psmond", + 1789: "hello", + 1790: "nmsp", + 1791: "ea1", + 1792: "ibm-dt-2", + 1793: "rsc-robot", + 1794: "cera-bcm", + 1795: "dpi-proxy", + 1796: "vocaltec-admin", + 1797: "uma", + 1798: "etp", + 1799: "netrisk", + 1800: "ansys-lm", + 1801: "msmq", + 1802: "concomp1", + 1803: "hp-hcip-gwy", + 1804: "enl", + 1805: "enl-name", + 1806: "musiconline", + 1807: "fhsp", + 1808: "oracle-vp2", + 1809: "oracle-vp1", + 1810: "jerand-lm", + 1811: "scientia-sdb", + 1812: "radius", + 1813: "radius-acct", + 1814: "tdp-suite", + 1815: "mmpft", + 1816: "harp", + 1817: "rkb-oscs", + 1818: "etftp", + 1819: "plato-lm", + 1820: "mcagent", + 1821: "donnyworld", + 1822: "es-elmd", + 1823: "unisys-lm", + 1824: "metrics-pas", + 1825: "direcpc-video", + 1826: "ardt", + 1827: "asi", + 1828: "itm-mcell-u", + 1829: "optika-emedia", + 1830: "net8-cman", + 1831: "myrtle", + 1832: "tht-treasure", + 1833: "udpradio", + 1834: "ardusuni", + 1835: "ardusmul", + 1836: "ste-smsc", + 1837: "csoft1", + 1838: "talnet", + 1839: "netopia-vo1", + 1840: "netopia-vo2", + 1841: "netopia-vo3", + 1842: "netopia-vo4", + 1843: "netopia-vo5", + 1844: "direcpc-dll", + 1845: "altalink", + 1846: "tunstall-pnc", + 1847: "slp-notify", + 1848: "fjdocdist", + 1849: "alpha-sms", + 1850: "gsi", + 1851: "ctcd", + 1852: "virtual-time", + 1853: "vids-avtp", + 1854: "buddy-draw", + 1855: "fiorano-rtrsvc", + 1856: "fiorano-msgsvc", + 1857: "datacaptor", + 1858: "privateark", + 1859: "gammafetchsvr", + 1860: "sunscalar-svc", + 1861: "lecroy-vicp", + 1862: "mysql-cm-agent", + 1863: "msnp", + 1864: "paradym-31port", + 1865: "entp", + 1866: "swrmi", + 1867: "udrive", + 1868: "viziblebrowser", + 1869: "transact", + 1870: "sunscalar-dns", + 1871: "canocentral0", + 1872: "canocentral1", + 1873: "fjmpjps", + 1874: "fjswapsnp", + 1875: "westell-stats", + 1876: "ewcappsrv", + 1877: "hp-webqosdb", + 1878: "drmsmc", + 1879: "nettgain-nms", + 1880: "vsat-control", + 1881: "ibm-mqseries2", + 1882: "ecsqdmn", + 1883: "mqtt", + 1884: "idmaps", + 1885: "vrtstrapserver", + 1886: "leoip", + 1887: "filex-lport", + 1888: "ncconfig", + 1889: "unify-adapter", + 1890: "wilkenlistener", + 1891: "childkey-notif", + 1892: "childkey-ctrl", + 1893: "elad", + 1894: "o2server-port", + 1896: "b-novative-ls", + 1897: "metaagent", + 1898: "cymtec-port", + 1899: "mc2studios", + 1900: "ssdp", + 1901: "fjicl-tep-a", + 1902: "fjicl-tep-b", + 1903: "linkname", + 1904: "fjicl-tep-c", + 1905: "sugp", + 1906: "tpmd", + 1907: "intrastar", + 1908: "dawn", + 1909: "global-wlink", + 1910: "ultrabac", + 1911: "mtp", + 1912: "rhp-iibp", + 1913: "armadp", + 1914: "elm-momentum", + 1915: "facelink", + 1916: "persona", + 1917: "noagent", + 1918: "can-nds", + 1919: "can-dch", + 1920: "can-ferret", + 1921: "noadmin", + 1922: "tapestry", + 1923: "spice", + 1924: "xiip", + 1925: "discovery-port", + 1926: "egs", + 1927: "videte-cipc", + 1928: "emsd-port", + 1929: "bandwiz-system", + 1930: "driveappserver", + 1931: "amdsched", + 1932: "ctt-broker", + 1933: "xmapi", + 1934: "xaapi", + 1935: "macromedia-fcs", + 1936: "jetcmeserver", + 1937: "jwserver", + 1938: "jwclient", + 1939: "jvserver", + 1940: "jvclient", + 1941: "dic-aida", + 1942: "res", + 1943: "beeyond-media", + 1944: "close-combat", + 1945: "dialogic-elmd", + 1946: "tekpls", + 1947: "sentinelsrm", + 1948: "eye2eye", + 1949: "ismaeasdaqlive", + 1950: "ismaeasdaqtest", + 1951: "bcs-lmserver", + 1952: "mpnjsc", + 1953: "rapidbase", + 1954: "abr-api", + 1955: "abr-secure", + 1956: "vrtl-vmf-ds", + 1957: "unix-status", + 1958: "dxadmind", + 1959: "simp-all", + 1960: "nasmanager", + 1961: "bts-appserver", + 1962: "biap-mp", + 1963: "webmachine", + 1964: "solid-e-engine", + 1965: "tivoli-npm", + 1966: "slush", + 1967: "sns-quote", + 1968: "lipsinc", + 1969: "lipsinc1", + 1970: "netop-rc", + 1971: "netop-school", + 1972: "intersys-cache", + 1973: "dlsrap", + 1974: "drp", + 1975: "tcoflashagent", + 1976: "tcoregagent", + 1977: "tcoaddressbook", + 1978: "unisql", + 1979: "unisql-java", + 1980: "pearldoc-xact", + 1981: "p2pq", + 1982: "estamp", + 1983: "lhtp", + 1984: "bb", + 1985: "hsrp", + 1986: "licensedaemon", + 1987: "tr-rsrb-p1", + 1988: "tr-rsrb-p2", + 1989: "tr-rsrb-p3", + 1990: "stun-p1", + 1991: "stun-p2", + 1992: "stun-p3", + 1993: "snmp-tcp-port", + 1994: "stun-port", + 1995: "perf-port", + 1996: "tr-rsrb-port", + 1997: "gdp-port", + 1998: "x25-svc-port", + 1999: "tcp-id-port", + 2000: "cisco-sccp", + 2001: "wizard", + 2002: "globe", + 2003: "brutus", + 2004: "emce", + 2005: "oracle", + 2006: "raid-cd", + 2007: "raid-am", + 2008: "terminaldb", + 2009: "whosockami", + 2010: "pipe-server", + 2011: "servserv", + 2012: "raid-ac", + 2013: "raid-cd", + 2014: "raid-sf", + 2015: "raid-cs", + 2016: "bootserver", + 2017: "bootclient", + 2018: "rellpack", + 2019: "about", + 2020: "xinupageserver", + 2021: "xinuexpansion1", + 2022: "xinuexpansion2", + 2023: "xinuexpansion3", + 2024: "xinuexpansion4", + 2025: "xribs", + 2026: "scrabble", + 2027: "shadowserver", + 2028: "submitserver", + 2029: "hsrpv6", + 2030: "device2", + 2031: "mobrien-chat", + 2032: "blackboard", + 2033: "glogger", + 2034: "scoremgr", + 2035: "imsldoc", + 2036: "e-dpnet", + 2037: "applus", + 2038: "objectmanager", + 2039: "prizma", + 2040: "lam", + 2041: "interbase", + 2042: "isis", + 2043: "isis-bcast", + 2044: "rimsl", + 2045: "cdfunc", + 2046: "sdfunc", + 2047: "dls", + 2048: "dls-monitor", + 2049: "shilp", + 2050: "av-emb-config", + 2051: "epnsdp", + 2052: "clearvisn", + 2053: "lot105-ds-upd", + 2054: "weblogin", + 2055: "iop", + 2056: "omnisky", + 2057: "rich-cp", + 2058: "newwavesearch", + 2059: "bmc-messaging", + 2060: "teleniumdaemon", + 2061: "netmount", + 2062: "icg-swp", + 2063: "icg-bridge", + 2064: "icg-iprelay", + 2065: "dlsrpn", + 2066: "aura", + 2067: "dlswpn", + 2068: "avauthsrvprtcl", + 2069: "event-port", + 2070: "ah-esp-encap", + 2071: "acp-port", + 2072: "msync", + 2073: "gxs-data-port", + 2074: "vrtl-vmf-sa", + 2075: "newlixengine", + 2076: "newlixconfig", + 2077: "tsrmagt", + 2078: "tpcsrvr", + 2079: "idware-router", + 2080: "autodesk-nlm", + 2081: "kme-trap-port", + 2082: "infowave", + 2083: "radsec", + 2084: "sunclustergeo", + 2085: "ada-cip", + 2086: "gnunet", + 2087: "eli", + 2088: "ip-blf", + 2089: "sep", + 2090: "lrp", + 2091: "prp", + 2092: "descent3", + 2093: "nbx-cc", + 2094: "nbx-au", + 2095: "nbx-ser", + 2096: "nbx-dir", + 2097: "jetformpreview", + 2098: "dialog-port", + 2099: "h2250-annex-g", + 2100: "amiganetfs", + 2101: "rtcm-sc104", + 2102: "zephyr-srv", + 2103: "zephyr-clt", + 2104: "zephyr-hm", + 2105: "minipay", + 2106: "mzap", + 2107: "bintec-admin", + 2108: "comcam", + 2109: "ergolight", + 2110: "umsp", + 2111: "dsatp", + 2112: "idonix-metanet", + 2113: "hsl-storm", + 2114: "newheights", + 2115: "kdm", + 2116: "ccowcmr", + 2117: "mentaclient", + 2118: "mentaserver", + 2119: "gsigatekeeper", + 2120: "qencp", + 2121: "scientia-ssdb", + 2122: "caupc-remote", + 2123: "gtp-control", + 2124: "elatelink", + 2125: "lockstep", + 2126: "pktcable-cops", + 2127: "index-pc-wb", + 2128: "net-steward", + 2129: "cs-live", + 2130: "xds", + 2131: "avantageb2b", + 2132: "solera-epmap", + 2133: "zymed-zpp", + 2134: "avenue", + 2135: "gris", + 2136: "appworxsrv", + 2137: "connect", + 2138: "unbind-cluster", + 2139: "ias-auth", + 2140: "ias-reg", + 2141: "ias-admind", + 2142: "tdmoip", + 2143: "lv-jc", + 2144: "lv-ffx", + 2145: "lv-pici", + 2146: "lv-not", + 2147: "lv-auth", + 2148: "veritas-ucl", + 2149: "acptsys", + 2150: "dynamic3d", + 2151: "docent", + 2152: "gtp-user", + 2153: "ctlptc", + 2154: "stdptc", + 2155: "brdptc", + 2156: "trp", + 2157: "xnds", + 2158: "touchnetplus", + 2159: "gdbremote", + 2160: "apc-2160", + 2161: "apc-2161", + 2162: "navisphere", + 2163: "navisphere-sec", + 2164: "ddns-v3", + 2165: "x-bone-api", + 2166: "iwserver", + 2167: "raw-serial", + 2168: "easy-soft-mux", + 2169: "brain", + 2170: "eyetv", + 2171: "msfw-storage", + 2172: "msfw-s-storage", + 2173: "msfw-replica", + 2174: "msfw-array", + 2175: "airsync", + 2176: "rapi", + 2177: "qwave", + 2178: "bitspeer", + 2179: "vmrdp", + 2180: "mc-gt-srv", + 2181: "eforward", + 2182: "cgn-stat", + 2183: "cgn-config", + 2184: "nvd", + 2185: "onbase-dds", + 2186: "gtaua", + 2187: "ssmd", + 2190: "tivoconnect", + 2191: "tvbus", + 2192: "asdis", + 2193: "drwcs", + 2197: "mnp-exchange", + 2198: "onehome-remote", + 2199: "onehome-help", + 2200: "ici", + 2201: "ats", + 2202: "imtc-map", + 2203: "b2-runtime", + 2204: "b2-license", + 2205: "jps", + 2206: "hpocbus", + 2207: "hpssd", + 2208: "hpiod", + 2209: "rimf-ps", + 2210: "noaaport", + 2211: "emwin", + 2212: "leecoposserver", + 2213: "kali", + 2214: "rpi", + 2215: "ipcore", + 2216: "vtu-comms", + 2217: "gotodevice", + 2218: "bounzza", + 2219: "netiq-ncap", + 2220: "netiq", + 2221: "ethernet-ip-s", + 2222: "EtherNet-IP-1", + 2223: "rockwell-csp2", + 2224: "efi-mg", + 2226: "di-drm", + 2227: "di-msg", + 2228: "ehome-ms", + 2229: "datalens", + 2230: "queueadm", + 2231: "wimaxasncp", + 2232: "ivs-video", + 2233: "infocrypt", + 2234: "directplay", + 2235: "sercomm-wlink", + 2236: "nani", + 2237: "optech-port1-lm", + 2238: "aviva-sna", + 2239: "imagequery", + 2240: "recipe", + 2241: "ivsd", + 2242: "foliocorp", + 2243: "magicom", + 2244: "nmsserver", + 2245: "hao", + 2246: "pc-mta-addrmap", + 2247: "antidotemgrsvr", + 2248: "ums", + 2249: "rfmp", + 2250: "remote-collab", + 2251: "dif-port", + 2252: "njenet-ssl", + 2253: "dtv-chan-req", + 2254: "seispoc", + 2255: "vrtp", + 2256: "pcc-mfp", + 2257: "simple-tx-rx", + 2258: "rcts", + 2260: "apc-2260", + 2261: "comotionmaster", + 2262: "comotionback", + 2263: "ecwcfg", + 2264: "apx500api-1", + 2265: "apx500api-2", + 2266: "mfserver", + 2267: "ontobroker", + 2268: "amt", + 2269: "mikey", + 2270: "starschool", + 2271: "mmcals", + 2272: "mmcal", + 2273: "mysql-im", + 2274: "pcttunnell", + 2275: "ibridge-data", + 2276: "ibridge-mgmt", + 2277: "bluectrlproxy", + 2278: "s3db", + 2279: "xmquery", + 2280: "lnvpoller", + 2281: "lnvconsole", + 2282: "lnvalarm", + 2283: "lnvstatus", + 2284: "lnvmaps", + 2285: "lnvmailmon", + 2286: "nas-metering", + 2287: "dna", + 2288: "netml", + 2289: "dict-lookup", + 2290: "sonus-logging", + 2291: "eapsp", + 2292: "mib-streaming", + 2293: "npdbgmngr", + 2294: "konshus-lm", + 2295: "advant-lm", + 2296: "theta-lm", + 2297: "d2k-datamover1", + 2298: "d2k-datamover2", + 2299: "pc-telecommute", + 2300: "cvmmon", + 2301: "cpq-wbem", + 2302: "binderysupport", + 2303: "proxy-gateway", + 2304: "attachmate-uts", + 2305: "mt-scaleserver", + 2306: "tappi-boxnet", + 2307: "pehelp", + 2308: "sdhelp", + 2309: "sdserver", + 2310: "sdclient", + 2311: "messageservice", + 2312: "wanscaler", + 2313: "iapp", + 2314: "cr-websystems", + 2315: "precise-sft", + 2316: "sent-lm", + 2317: "attachmate-g32", + 2318: "cadencecontrol", + 2319: "infolibria", + 2320: "siebel-ns", + 2321: "rdlap", + 2322: "ofsd", + 2323: "3d-nfsd", + 2324: "cosmocall", + 2325: "ansysli", + 2326: "idcp", + 2327: "xingcsm", + 2328: "netrix-sftm", + 2329: "nvd", + 2330: "tscchat", + 2331: "agentview", + 2332: "rcc-host", + 2333: "snapp", + 2334: "ace-client", + 2335: "ace-proxy", + 2336: "appleugcontrol", + 2337: "ideesrv", + 2338: "norton-lambert", + 2339: "3com-webview", + 2340: "wrs-registry", + 2341: "xiostatus", + 2342: "manage-exec", + 2343: "nati-logos", + 2344: "fcmsys", + 2345: "dbm", + 2346: "redstorm-join", + 2347: "redstorm-find", + 2348: "redstorm-info", + 2349: "redstorm-diag", + 2350: "psbserver", + 2351: "psrserver", + 2352: "pslserver", + 2353: "pspserver", + 2354: "psprserver", + 2355: "psdbserver", + 2356: "gxtelmd", + 2357: "unihub-server", + 2358: "futrix", + 2359: "flukeserver", + 2360: "nexstorindltd", + 2361: "tl1", + 2362: "digiman", + 2363: "mediacntrlnfsd", + 2364: "oi-2000", + 2365: "dbref", + 2366: "qip-login", + 2367: "service-ctrl", + 2368: "opentable", + 2370: "l3-hbmon", + 2372: "lanmessenger", + 2381: "compaq-https", + 2382: "ms-olap3", + 2383: "ms-olap4", + 2384: "sd-capacity", + 2385: "sd-data", + 2386: "virtualtape", + 2387: "vsamredirector", + 2388: "mynahautostart", + 2389: "ovsessionmgr", + 2390: "rsmtp", + 2391: "3com-net-mgmt", + 2392: "tacticalauth", + 2393: "ms-olap1", + 2394: "ms-olap2", + 2395: "lan900-remote", + 2396: "wusage", + 2397: "ncl", + 2398: "orbiter", + 2399: "fmpro-fdal", + 2400: "opequus-server", + 2401: "cvspserver", + 2402: "taskmaster2000", + 2403: "taskmaster2000", + 2404: "iec-104", + 2405: "trc-netpoll", + 2406: "jediserver", + 2407: "orion", + 2409: "sns-protocol", + 2410: "vrts-registry", + 2411: "netwave-ap-mgmt", + 2412: "cdn", + 2413: "orion-rmi-reg", + 2414: "beeyond", + 2415: "codima-rtp", + 2416: "rmtserver", + 2417: "composit-server", + 2418: "cas", + 2419: "attachmate-s2s", + 2420: "dslremote-mgmt", + 2421: "g-talk", + 2422: "crmsbits", + 2423: "rnrp", + 2424: "kofax-svr", + 2425: "fjitsuappmgr", + 2426: "vcmp", + 2427: "mgcp-gateway", + 2428: "ott", + 2429: "ft-role", + 2430: "venus", + 2431: "venus-se", + 2432: "codasrv", + 2433: "codasrv-se", + 2434: "pxc-epmap", + 2435: "optilogic", + 2436: "topx", + 2437: "unicontrol", + 2438: "msp", + 2439: "sybasedbsynch", + 2440: "spearway", + 2441: "pvsw-inet", + 2442: "netangel", + 2443: "powerclientcsf", + 2444: "btpp2sectrans", + 2445: "dtn1", + 2446: "bues-service", + 2447: "ovwdb", + 2448: "hpppssvr", + 2449: "ratl", + 2450: "netadmin", + 2451: "netchat", + 2452: "snifferclient", + 2453: "madge-ltd", + 2454: "indx-dds", + 2455: "wago-io-system", + 2456: "altav-remmgt", + 2457: "rapido-ip", + 2458: "griffin", + 2459: "community", + 2460: "ms-theater", + 2461: "qadmifoper", + 2462: "qadmifevent", + 2463: "lsi-raid-mgmt", + 2464: "direcpc-si", + 2465: "lbm", + 2466: "lbf", + 2467: "high-criteria", + 2468: "qip-msgd", + 2469: "mti-tcs-comm", + 2470: "taskman-port", + 2471: "seaodbc", + 2472: "c3", + 2473: "aker-cdp", + 2474: "vitalanalysis", + 2475: "ace-server", + 2476: "ace-svr-prop", + 2477: "ssm-cvs", + 2478: "ssm-cssps", + 2479: "ssm-els", + 2480: "powerexchange", + 2481: "giop", + 2482: "giop-ssl", + 2483: "ttc", + 2484: "ttc-ssl", + 2485: "netobjects1", + 2486: "netobjects2", + 2487: "pns", + 2488: "moy-corp", + 2489: "tsilb", + 2490: "qip-qdhcp", + 2491: "conclave-cpp", + 2492: "groove", + 2493: "talarian-mqs", + 2494: "bmc-ar", + 2495: "fast-rem-serv", + 2496: "dirgis", + 2497: "quaddb", + 2498: "odn-castraq", + 2499: "unicontrol", + 2500: "rtsserv", + 2501: "rtsclient", + 2502: "kentrox-prot", + 2503: "nms-dpnss", + 2504: "wlbs", + 2505: "ppcontrol", + 2506: "jbroker", + 2507: "spock", + 2508: "jdatastore", + 2509: "fjmpss", + 2510: "fjappmgrbulk", + 2511: "metastorm", + 2512: "citrixima", + 2513: "citrixadmin", + 2514: "facsys-ntp", + 2515: "facsys-router", + 2516: "maincontrol", + 2517: "call-sig-trans", + 2518: "willy", + 2519: "globmsgsvc", + 2520: "pvsw", + 2521: "adaptecmgr", + 2522: "windb", + 2523: "qke-llc-v3", + 2524: "optiwave-lm", + 2525: "ms-v-worlds", + 2526: "ema-sent-lm", + 2527: "iqserver", + 2528: "ncr-ccl", + 2529: "utsftp", + 2530: "vrcommerce", + 2531: "ito-e-gui", + 2532: "ovtopmd", + 2533: "snifferserver", + 2534: "combox-web-acc", + 2535: "madcap", + 2536: "btpp2audctr1", + 2537: "upgrade", + 2538: "vnwk-prapi", + 2539: "vsiadmin", + 2540: "lonworks", + 2541: "lonworks2", + 2542: "udrawgraph", + 2543: "reftek", + 2544: "novell-zen", + 2545: "sis-emt", + 2546: "vytalvaultbrtp", + 2547: "vytalvaultvsmp", + 2548: "vytalvaultpipe", + 2549: "ipass", + 2550: "ads", + 2551: "isg-uda-server", + 2552: "call-logging", + 2553: "efidiningport", + 2554: "vcnet-link-v10", + 2555: "compaq-wcp", + 2556: "nicetec-nmsvc", + 2557: "nicetec-mgmt", + 2558: "pclemultimedia", + 2559: "lstp", + 2560: "labrat", + 2561: "mosaixcc", + 2562: "delibo", + 2563: "cti-redwood", + 2564: "hp-3000-telnet", + 2565: "coord-svr", + 2566: "pcs-pcw", + 2567: "clp", + 2568: "spamtrap", + 2569: "sonuscallsig", + 2570: "hs-port", + 2571: "cecsvc", + 2572: "ibp", + 2573: "trustestablish", + 2574: "blockade-bpsp", + 2575: "hl7", + 2576: "tclprodebugger", + 2577: "scipticslsrvr", + 2578: "rvs-isdn-dcp", + 2579: "mpfoncl", + 2580: "tributary", + 2581: "argis-te", + 2582: "argis-ds", + 2583: "mon", + 2584: "cyaserv", + 2585: "netx-server", + 2586: "netx-agent", + 2587: "masc", + 2588: "privilege", + 2589: "quartus-tcl", + 2590: "idotdist", + 2591: "maytagshuffle", + 2592: "netrek", + 2593: "mns-mail", + 2594: "dts", + 2595: "worldfusion1", + 2596: "worldfusion2", + 2597: "homesteadglory", + 2598: "citriximaclient", + 2599: "snapd", + 2600: "hpstgmgr", + 2601: "discp-client", + 2602: "discp-server", + 2603: "servicemeter", + 2604: "nsc-ccs", + 2605: "nsc-posa", + 2606: "netmon", + 2607: "connection", + 2608: "wag-service", + 2609: "system-monitor", + 2610: "versa-tek", + 2611: "lionhead", + 2612: "qpasa-agent", + 2613: "smntubootstrap", + 2614: "neveroffline", + 2615: "firepower", + 2616: "appswitch-emp", + 2617: "cmadmin", + 2618: "priority-e-com", + 2619: "bruce", + 2620: "lpsrecommender", + 2621: "miles-apart", + 2622: "metricadbc", + 2623: "lmdp", + 2624: "aria", + 2625: "blwnkl-port", + 2626: "gbjd816", + 2627: "moshebeeri", + 2628: "dict", + 2629: "sitaraserver", + 2630: "sitaramgmt", + 2631: "sitaradir", + 2632: "irdg-post", + 2633: "interintelli", + 2634: "pk-electronics", + 2635: "backburner", + 2636: "solve", + 2637: "imdocsvc", + 2638: "sybaseanywhere", + 2639: "aminet", + 2640: "ami-control", + 2641: "hdl-srv", + 2642: "tragic", + 2643: "gte-samp", + 2644: "travsoft-ipx-t", + 2645: "novell-ipx-cmd", + 2646: "and-lm", + 2647: "syncserver", + 2648: "upsnotifyprot", + 2649: "vpsipport", + 2650: "eristwoguns", + 2651: "ebinsite", + 2652: "interpathpanel", + 2653: "sonus", + 2654: "corel-vncadmin", + 2655: "unglue", + 2656: "kana", + 2657: "sns-dispatcher", + 2658: "sns-admin", + 2659: "sns-query", + 2660: "gcmonitor", + 2661: "olhost", + 2662: "bintec-capi", + 2663: "bintec-tapi", + 2664: "patrol-mq-gm", + 2665: "patrol-mq-nm", + 2666: "extensis", + 2667: "alarm-clock-s", + 2668: "alarm-clock-c", + 2669: "toad", + 2670: "tve-announce", + 2671: "newlixreg", + 2672: "nhserver", + 2673: "firstcall42", + 2674: "ewnn", + 2675: "ttc-etap", + 2676: "simslink", + 2677: "gadgetgate1way", + 2678: "gadgetgate2way", + 2679: "syncserverssl", + 2680: "pxc-sapxom", + 2681: "mpnjsomb", + 2683: "ncdloadbalance", + 2684: "mpnjsosv", + 2685: "mpnjsocl", + 2686: "mpnjsomg", + 2687: "pq-lic-mgmt", + 2688: "md-cg-http", + 2689: "fastlynx", + 2690: "hp-nnm-data", + 2691: "itinternet", + 2692: "admins-lms", + 2694: "pwrsevent", + 2695: "vspread", + 2696: "unifyadmin", + 2697: "oce-snmp-trap", + 2698: "mck-ivpip", + 2699: "csoft-plusclnt", + 2700: "tqdata", + 2701: "sms-rcinfo", + 2702: "sms-xfer", + 2703: "sms-chat", + 2704: "sms-remctrl", + 2705: "sds-admin", + 2706: "ncdmirroring", + 2707: "emcsymapiport", + 2708: "banyan-net", + 2709: "supermon", + 2710: "sso-service", + 2711: "sso-control", + 2712: "aocp", + 2713: "raventbs", + 2714: "raventdm", + 2715: "hpstgmgr2", + 2716: "inova-ip-disco", + 2717: "pn-requester", + 2718: "pn-requester2", + 2719: "scan-change", + 2720: "wkars", + 2721: "smart-diagnose", + 2722: "proactivesrvr", + 2723: "watchdog-nt", + 2724: "qotps", + 2725: "msolap-ptp2", + 2726: "tams", + 2727: "mgcp-callagent", + 2728: "sqdr", + 2729: "tcim-control", + 2730: "nec-raidplus", + 2731: "fyre-messanger", + 2732: "g5m", + 2733: "signet-ctf", + 2734: "ccs-software", + 2735: "netiq-mc", + 2736: "radwiz-nms-srv", + 2737: "srp-feedback", + 2738: "ndl-tcp-ois-gw", + 2739: "tn-timing", + 2740: "alarm", + 2741: "tsb", + 2742: "tsb2", + 2743: "murx", + 2744: "honyaku", + 2745: "urbisnet", + 2746: "cpudpencap", + 2747: "fjippol-swrly", + 2748: "fjippol-polsvr", + 2749: "fjippol-cnsl", + 2750: "fjippol-port1", + 2751: "fjippol-port2", + 2752: "rsisysaccess", + 2753: "de-spot", + 2754: "apollo-cc", + 2755: "expresspay", + 2756: "simplement-tie", + 2757: "cnrp", + 2758: "apollo-status", + 2759: "apollo-gms", + 2760: "sabams", + 2761: "dicom-iscl", + 2762: "dicom-tls", + 2763: "desktop-dna", + 2764: "data-insurance", + 2765: "qip-audup", + 2766: "compaq-scp", + 2767: "uadtc", + 2768: "uacs", + 2769: "exce", + 2770: "veronica", + 2771: "vergencecm", + 2772: "auris", + 2773: "rbakcup1", + 2774: "rbakcup2", + 2775: "smpp", + 2776: "ridgeway1", + 2777: "ridgeway2", + 2778: "gwen-sonya", + 2779: "lbc-sync", + 2780: "lbc-control", + 2781: "whosells", + 2782: "everydayrc", + 2783: "aises", + 2784: "www-dev", + 2785: "aic-np", + 2786: "aic-oncrpc", + 2787: "piccolo", + 2788: "fryeserv", + 2789: "media-agent", + 2790: "plgproxy", + 2791: "mtport-regist", + 2792: "f5-globalsite", + 2793: "initlsmsad", + 2795: "livestats", + 2796: "ac-tech", + 2797: "esp-encap", + 2798: "tmesis-upshot", + 2799: "icon-discover", + 2800: "acc-raid", + 2801: "igcp", + 2802: "veritas-udp1", + 2803: "btprjctrl", + 2804: "dvr-esm", + 2805: "wta-wsp-s", + 2806: "cspuni", + 2807: "cspmulti", + 2808: "j-lan-p", + 2809: "corbaloc", + 2810: "netsteward", + 2811: "gsiftp", + 2812: "atmtcp", + 2813: "llm-pass", + 2814: "llm-csv", + 2815: "lbc-measure", + 2816: "lbc-watchdog", + 2817: "nmsigport", + 2818: "rmlnk", + 2819: "fc-faultnotify", + 2820: "univision", + 2821: "vrts-at-port", + 2822: "ka0wuc", + 2823: "cqg-netlan", + 2824: "cqg-netlan-1", + 2826: "slc-systemlog", + 2827: "slc-ctrlrloops", + 2828: "itm-lm", + 2829: "silkp1", + 2830: "silkp2", + 2831: "silkp3", + 2832: "silkp4", + 2833: "glishd", + 2834: "evtp", + 2835: "evtp-data", + 2836: "catalyst", + 2837: "repliweb", + 2838: "starbot", + 2839: "nmsigport", + 2840: "l3-exprt", + 2841: "l3-ranger", + 2842: "l3-hawk", + 2843: "pdnet", + 2844: "bpcp-poll", + 2845: "bpcp-trap", + 2846: "aimpp-hello", + 2847: "aimpp-port-req", + 2848: "amt-blc-port", + 2849: "fxp", + 2850: "metaconsole", + 2851: "webemshttp", + 2852: "bears-01", + 2853: "ispipes", + 2854: "infomover", + 2856: "cesdinv", + 2857: "simctlp", + 2858: "ecnp", + 2859: "activememory", + 2860: "dialpad-voice1", + 2861: "dialpad-voice2", + 2862: "ttg-protocol", + 2863: "sonardata", + 2864: "astromed-main", + 2865: "pit-vpn", + 2866: "iwlistener", + 2867: "esps-portal", + 2868: "npep-messaging", + 2869: "icslap", + 2870: "daishi", + 2871: "msi-selectplay", + 2872: "radix", + 2874: "dxmessagebase1", + 2875: "dxmessagebase2", + 2876: "sps-tunnel", + 2877: "bluelance", + 2878: "aap", + 2879: "ucentric-ds", + 2880: "synapse", + 2881: "ndsp", + 2882: "ndtp", + 2883: "ndnp", + 2884: "flashmsg", + 2885: "topflow", + 2886: "responselogic", + 2887: "aironetddp", + 2888: "spcsdlobby", + 2889: "rsom", + 2890: "cspclmulti", + 2891: "cinegrfx-elmd", + 2892: "snifferdata", + 2893: "vseconnector", + 2894: "abacus-remote", + 2895: "natuslink", + 2896: "ecovisiong6-1", + 2897: "citrix-rtmp", + 2898: "appliance-cfg", + 2899: "powergemplus", + 2900: "quicksuite", + 2901: "allstorcns", + 2902: "netaspi", + 2903: "suitcase", + 2904: "m2ua", + 2906: "caller9", + 2907: "webmethods-b2b", + 2908: "mao", + 2909: "funk-dialout", + 2910: "tdaccess", + 2911: "blockade", + 2912: "epicon", + 2913: "boosterware", + 2914: "gamelobby", + 2915: "tksocket", + 2916: "elvin-server", + 2917: "elvin-client", + 2918: "kastenchasepad", + 2919: "roboer", + 2920: "roboeda", + 2921: "cesdcdman", + 2922: "cesdcdtrn", + 2923: "wta-wsp-wtp-s", + 2924: "precise-vip", + 2926: "mobile-file-dl", + 2927: "unimobilectrl", + 2928: "redstone-cpss", + 2929: "amx-webadmin", + 2930: "amx-weblinx", + 2931: "circle-x", + 2932: "incp", + 2933: "4-tieropmgw", + 2934: "4-tieropmcli", + 2935: "qtp", + 2936: "otpatch", + 2937: "pnaconsult-lm", + 2938: "sm-pas-1", + 2939: "sm-pas-2", + 2940: "sm-pas-3", + 2941: "sm-pas-4", + 2942: "sm-pas-5", + 2943: "ttnrepository", + 2944: "megaco-h248", + 2945: "h248-binary", + 2946: "fjsvmpor", + 2947: "gpsd", + 2948: "wap-push", + 2949: "wap-pushsecure", + 2950: "esip", + 2951: "ottp", + 2952: "mpfwsas", + 2953: "ovalarmsrv", + 2954: "ovalarmsrv-cmd", + 2955: "csnotify", + 2956: "ovrimosdbman", + 2957: "jmact5", + 2958: "jmact6", + 2959: "rmopagt", + 2960: "dfoxserver", + 2961: "boldsoft-lm", + 2962: "iph-policy-cli", + 2963: "iph-policy-adm", + 2964: "bullant-srap", + 2965: "bullant-rap", + 2966: "idp-infotrieve", + 2967: "ssc-agent", + 2968: "enpp", + 2969: "essp", + 2970: "index-net", + 2971: "netclip", + 2972: "pmsm-webrctl", + 2973: "svnetworks", + 2974: "signal", + 2975: "fjmpcm", + 2976: "cns-srv-port", + 2977: "ttc-etap-ns", + 2978: "ttc-etap-ds", + 2979: "h263-video", + 2980: "wimd", + 2981: "mylxamport", + 2982: "iwb-whiteboard", + 2983: "netplan", + 2984: "hpidsadmin", + 2985: "hpidsagent", + 2986: "stonefalls", + 2987: "identify", + 2988: "hippad", + 2989: "zarkov", + 2990: "boscap", + 2991: "wkstn-mon", + 2992: "avenyo", + 2993: "veritas-vis1", + 2994: "veritas-vis2", + 2995: "idrs", + 2996: "vsixml", + 2997: "rebol", + 2998: "realsecure", + 2999: "remoteware-un", + 3000: "hbci", + 3002: "exlm-agent", + 3003: "cgms", + 3004: "csoftragent", + 3005: "geniuslm", + 3006: "ii-admin", + 3007: "lotusmtap", + 3008: "midnight-tech", + 3009: "pxc-ntfy", + 3010: "ping-pong", + 3011: "trusted-web", + 3012: "twsdss", + 3013: "gilatskysurfer", + 3014: "broker-service", + 3015: "nati-dstp", + 3016: "notify-srvr", + 3017: "event-listener", + 3018: "srvc-registry", + 3019: "resource-mgr", + 3020: "cifs", + 3021: "agriserver", + 3022: "csregagent", + 3023: "magicnotes", + 3024: "nds-sso", + 3025: "arepa-raft", + 3026: "agri-gateway", + 3027: "LiebDevMgmt-C", + 3028: "LiebDevMgmt-DM", + 3029: "LiebDevMgmt-A", + 3030: "arepa-cas", + 3031: "eppc", + 3032: "redwood-chat", + 3033: "pdb", + 3034: "osmosis-aeea", + 3035: "fjsv-gssagt", + 3036: "hagel-dump", + 3037: "hp-san-mgmt", + 3038: "santak-ups", + 3039: "cogitate", + 3040: "tomato-springs", + 3041: "di-traceware", + 3042: "journee", + 3043: "brp", + 3044: "epp", + 3045: "responsenet", + 3046: "di-ase", + 3047: "hlserver", + 3048: "pctrader", + 3049: "nsws", + 3050: "gds-db", + 3051: "galaxy-server", + 3052: "apc-3052", + 3053: "dsom-server", + 3054: "amt-cnf-prot", + 3055: "policyserver", + 3056: "cdl-server", + 3057: "goahead-fldup", + 3058: "videobeans", + 3059: "qsoft", + 3060: "interserver", + 3061: "cautcpd", + 3062: "ncacn-ip-tcp", + 3063: "ncadg-ip-udp", + 3064: "rprt", + 3065: "slinterbase", + 3066: "netattachsdmp", + 3067: "fjhpjp", + 3068: "ls3bcast", + 3069: "ls3", + 3070: "mgxswitch", + 3071: "csd-mgmt-port", + 3072: "csd-monitor", + 3073: "vcrp", + 3074: "xbox", + 3075: "orbix-locator", + 3076: "orbix-config", + 3077: "orbix-loc-ssl", + 3078: "orbix-cfg-ssl", + 3079: "lv-frontpanel", + 3080: "stm-pproc", + 3081: "tl1-lv", + 3082: "tl1-raw", + 3083: "tl1-telnet", + 3084: "itm-mccs", + 3085: "pcihreq", + 3086: "jdl-dbkitchen", + 3087: "asoki-sma", + 3088: "xdtp", + 3089: "ptk-alink", + 3090: "stss", + 3091: "1ci-smcs", + 3093: "rapidmq-center", + 3094: "rapidmq-reg", + 3095: "panasas", + 3096: "ndl-aps", + 3098: "umm-port", + 3099: "chmd", + 3100: "opcon-xps", + 3101: "hp-pxpib", + 3102: "slslavemon", + 3103: "autocuesmi", + 3104: "autocuetime", + 3105: "cardbox", + 3106: "cardbox-http", + 3107: "business", + 3108: "geolocate", + 3109: "personnel", + 3110: "sim-control", + 3111: "wsynch", + 3112: "ksysguard", + 3113: "cs-auth-svr", + 3114: "ccmad", + 3115: "mctet-master", + 3116: "mctet-gateway", + 3117: "mctet-jserv", + 3118: "pkagent", + 3119: "d2000kernel", + 3120: "d2000webserver", + 3122: "vtr-emulator", + 3123: "edix", + 3124: "beacon-port", + 3125: "a13-an", + 3127: "ctx-bridge", + 3128: "ndl-aas", + 3129: "netport-id", + 3130: "icpv2", + 3131: "netbookmark", + 3132: "ms-rule-engine", + 3133: "prism-deploy", + 3134: "ecp", + 3135: "peerbook-port", + 3136: "grubd", + 3137: "rtnt-1", + 3138: "rtnt-2", + 3139: "incognitorv", + 3140: "ariliamulti", + 3141: "vmodem", + 3142: "rdc-wh-eos", + 3143: "seaview", + 3144: "tarantella", + 3145: "csi-lfap", + 3146: "bears-02", + 3147: "rfio", + 3148: "nm-game-admin", + 3149: "nm-game-server", + 3150: "nm-asses-admin", + 3151: "nm-assessor", + 3152: "feitianrockey", + 3153: "s8-client-port", + 3154: "ccmrmi", + 3155: "jpegmpeg", + 3156: "indura", + 3157: "e3consultants", + 3158: "stvp", + 3159: "navegaweb-port", + 3160: "tip-app-server", + 3161: "doc1lm", + 3162: "sflm", + 3163: "res-sap", + 3164: "imprs", + 3165: "newgenpay", + 3166: "sossecollector", + 3167: "nowcontact", + 3168: "poweronnud", + 3169: "serverview-as", + 3170: "serverview-asn", + 3171: "serverview-gf", + 3172: "serverview-rm", + 3173: "serverview-icc", + 3174: "armi-server", + 3175: "t1-e1-over-ip", + 3176: "ars-master", + 3177: "phonex-port", + 3178: "radclientport", + 3179: "h2gf-w-2m", + 3180: "mc-brk-srv", + 3181: "bmcpatrolagent", + 3182: "bmcpatrolrnvu", + 3183: "cops-tls", + 3184: "apogeex-port", + 3185: "smpppd", + 3186: "iiw-port", + 3187: "odi-port", + 3188: "brcm-comm-port", + 3189: "pcle-infex", + 3190: "csvr-proxy", + 3191: "csvr-sslproxy", + 3192: "firemonrcc", + 3193: "spandataport", + 3194: "magbind", + 3195: "ncu-1", + 3196: "ncu-2", + 3197: "embrace-dp-s", + 3198: "embrace-dp-c", + 3199: "dmod-workspace", + 3200: "tick-port", + 3201: "cpq-tasksmart", + 3202: "intraintra", + 3203: "netwatcher-mon", + 3204: "netwatcher-db", + 3205: "isns", + 3206: "ironmail", + 3207: "vx-auth-port", + 3208: "pfu-prcallback", + 3209: "netwkpathengine", + 3210: "flamenco-proxy", + 3211: "avsecuremgmt", + 3212: "surveyinst", + 3213: "neon24x7", + 3214: "jmq-daemon-1", + 3215: "jmq-daemon-2", + 3216: "ferrari-foam", + 3217: "unite", + 3218: "smartpackets", + 3219: "wms-messenger", + 3220: "xnm-ssl", + 3221: "xnm-clear-text", + 3222: "glbp", + 3223: "digivote", + 3224: "aes-discovery", + 3225: "fcip-port", + 3226: "isi-irp", + 3227: "dwnmshttp", + 3228: "dwmsgserver", + 3229: "global-cd-port", + 3230: "sftdst-port", + 3231: "vidigo", + 3232: "mdtp", + 3233: "whisker", + 3234: "alchemy", + 3235: "mdap-port", + 3236: "apparenet-ts", + 3237: "apparenet-tps", + 3238: "apparenet-as", + 3239: "apparenet-ui", + 3240: "triomotion", + 3241: "sysorb", + 3242: "sdp-id-port", + 3243: "timelot", + 3244: "onesaf", + 3245: "vieo-fe", + 3246: "dvt-system", + 3247: "dvt-data", + 3248: "procos-lm", + 3249: "ssp", + 3250: "hicp", + 3251: "sysscanner", + 3252: "dhe", + 3253: "pda-data", + 3254: "pda-sys", + 3255: "semaphore", + 3256: "cpqrpm-agent", + 3257: "cpqrpm-server", + 3258: "ivecon-port", + 3259: "epncdp2", + 3260: "iscsi-target", + 3261: "winshadow", + 3262: "necp", + 3263: "ecolor-imager", + 3264: "ccmail", + 3265: "altav-tunnel", + 3266: "ns-cfg-server", + 3267: "ibm-dial-out", + 3268: "msft-gc", + 3269: "msft-gc-ssl", + 3270: "verismart", + 3271: "csoft-prev", + 3272: "user-manager", + 3273: "sxmp", + 3274: "ordinox-server", + 3275: "samd", + 3276: "maxim-asics", + 3277: "awg-proxy", + 3278: "lkcmserver", + 3279: "admind", + 3280: "vs-server", + 3281: "sysopt", + 3282: "datusorb", + 3283: "Apple Remote Desktop (Net Assistant)", + 3284: "4talk", + 3285: "plato", + 3286: "e-net", + 3287: "directvdata", + 3288: "cops", + 3289: "enpc", + 3290: "caps-lm", + 3291: "sah-lm", + 3292: "cart-o-rama", + 3293: "fg-fps", + 3294: "fg-gip", + 3295: "dyniplookup", + 3296: "rib-slm", + 3297: "cytel-lm", + 3298: "deskview", + 3299: "pdrncs", + 3302: "mcs-fastmail", + 3303: "opsession-clnt", + 3304: "opsession-srvr", + 3305: "odette-ftp", + 3306: "mysql", + 3307: "opsession-prxy", + 3308: "tns-server", + 3309: "tns-adv", + 3310: "dyna-access", + 3311: "mcns-tel-ret", + 3312: "appman-server", + 3313: "uorb", + 3314: "uohost", + 3315: "cdid", + 3316: "aicc-cmi", + 3317: "vsaiport", + 3318: "ssrip", + 3319: "sdt-lmd", + 3320: "officelink2000", + 3321: "vnsstr", + 3326: "sftu", + 3327: "bbars", + 3328: "egptlm", + 3329: "hp-device-disc", + 3330: "mcs-calypsoicf", + 3331: "mcs-messaging", + 3332: "mcs-mailsvr", + 3333: "dec-notes", + 3334: "directv-web", + 3335: "directv-soft", + 3336: "directv-tick", + 3337: "directv-catlg", + 3338: "anet-b", + 3339: "anet-l", + 3340: "anet-m", + 3341: "anet-h", + 3342: "webtie", + 3343: "ms-cluster-net", + 3344: "bnt-manager", + 3345: "influence", + 3346: "trnsprntproxy", + 3347: "phoenix-rpc", + 3348: "pangolin-laser", + 3349: "chevinservices", + 3350: "findviatv", + 3351: "btrieve", + 3352: "ssql", + 3353: "fatpipe", + 3354: "suitjd", + 3355: "ordinox-dbase", + 3356: "upnotifyps", + 3357: "adtech-test", + 3358: "mpsysrmsvr", + 3359: "wg-netforce", + 3360: "kv-server", + 3361: "kv-agent", + 3362: "dj-ilm", + 3363: "nati-vi-server", + 3364: "creativeserver", + 3365: "contentserver", + 3366: "creativepartnr", + 3372: "tip2", + 3373: "lavenir-lm", + 3374: "cluster-disc", + 3375: "vsnm-agent", + 3376: "cdbroker", + 3377: "cogsys-lm", + 3378: "wsicopy", + 3379: "socorfs", + 3380: "sns-channels", + 3381: "geneous", + 3382: "fujitsu-neat", + 3383: "esp-lm", + 3384: "hp-clic", + 3385: "qnxnetman", + 3386: "gprs-sig", + 3387: "backroomnet", + 3388: "cbserver", + 3389: "ms-wbt-server", + 3390: "dsc", + 3391: "savant", + 3392: "efi-lm", + 3393: "d2k-tapestry1", + 3394: "d2k-tapestry2", + 3395: "dyna-lm", + 3396: "printer-agent", + 3397: "cloanto-lm", + 3398: "mercantile", + 3399: "csms", + 3400: "csms2", + 3401: "filecast", + 3402: "fxaengine-net", + 3405: "nokia-ann-ch1", + 3406: "nokia-ann-ch2", + 3407: "ldap-admin", + 3408: "BESApi", + 3409: "networklens", + 3410: "networklenss", + 3411: "biolink-auth", + 3412: "xmlblaster", + 3413: "svnet", + 3414: "wip-port", + 3415: "bcinameservice", + 3416: "commandport", + 3417: "csvr", + 3418: "rnmap", + 3419: "softaudit", + 3420: "ifcp-port", + 3421: "bmap", + 3422: "rusb-sys-port", + 3423: "xtrm", + 3424: "xtrms", + 3425: "agps-port", + 3426: "arkivio", + 3427: "websphere-snmp", + 3428: "twcss", + 3429: "gcsp", + 3430: "ssdispatch", + 3431: "ndl-als", + 3432: "osdcp", + 3433: "opnet-smp", + 3434: "opencm", + 3435: "pacom", + 3436: "gc-config", + 3437: "autocueds", + 3438: "spiral-admin", + 3439: "hri-port", + 3440: "ans-console", + 3441: "connect-client", + 3442: "connect-server", + 3443: "ov-nnm-websrv", + 3444: "denali-server", + 3445: "monp", + 3446: "3comfaxrpc", + 3447: "directnet", + 3448: "dnc-port", + 3449: "hotu-chat", + 3450: "castorproxy", + 3451: "asam", + 3452: "sabp-signal", + 3453: "pscupd", + 3454: "mira", + 3455: "prsvp", + 3456: "vat", + 3457: "vat-control", + 3458: "d3winosfi", + 3459: "integral", + 3460: "edm-manager", + 3461: "edm-stager", + 3462: "edm-std-notify", + 3463: "edm-adm-notify", + 3464: "edm-mgr-sync", + 3465: "edm-mgr-cntrl", + 3466: "workflow", + 3467: "rcst", + 3468: "ttcmremotectrl", + 3469: "pluribus", + 3470: "jt400", + 3471: "jt400-ssl", + 3472: "jaugsremotec-1", + 3473: "jaugsremotec-2", + 3474: "ttntspauto", + 3475: "genisar-port", + 3476: "nppmp", + 3477: "ecomm", + 3478: "stun", + 3479: "twrpc", + 3480: "plethora", + 3481: "cleanerliverc", + 3482: "vulture", + 3483: "slim-devices", + 3484: "gbs-stp", + 3485: "celatalk", + 3486: "ifsf-hb-port", + 3487: "ltcudp", + 3488: "fs-rh-srv", + 3489: "dtp-dia", + 3490: "colubris", + 3491: "swr-port", + 3492: "tvdumtray-port", + 3493: "nut", + 3494: "ibm3494", + 3495: "seclayer-tcp", + 3496: "seclayer-tls", + 3497: "ipether232port", + 3498: "dashpas-port", + 3499: "sccip-media", + 3500: "rtmp-port", + 3501: "isoft-p2p", + 3502: "avinstalldisc", + 3503: "lsp-ping", + 3504: "ironstorm", + 3505: "ccmcomm", + 3506: "apc-3506", + 3507: "nesh-broker", + 3508: "interactionweb", + 3509: "vt-ssl", + 3510: "xss-port", + 3511: "webmail-2", + 3512: "aztec", + 3513: "arcpd", + 3514: "must-p2p", + 3515: "must-backplane", + 3516: "smartcard-port", + 3517: "802-11-iapp", + 3518: "artifact-msg", + 3519: "galileo", + 3520: "galileolog", + 3521: "mc3ss", + 3522: "nssocketport", + 3523: "odeumservlink", + 3524: "ecmport", + 3525: "eisport", + 3526: "starquiz-port", + 3527: "beserver-msg-q", + 3528: "jboss-iiop", + 3529: "jboss-iiop-ssl", + 3530: "gf", + 3531: "joltid", + 3532: "raven-rmp", + 3533: "raven-rdp", + 3534: "urld-port", + 3535: "ms-la", + 3536: "snac", + 3537: "ni-visa-remote", + 3538: "ibm-diradm", + 3539: "ibm-diradm-ssl", + 3540: "pnrp-port", + 3541: "voispeed-port", + 3542: "hacl-monitor", + 3543: "qftest-lookup", + 3544: "teredo", + 3545: "camac", + 3547: "symantec-sim", + 3548: "interworld", + 3549: "tellumat-nms", + 3550: "ssmpp", + 3551: "apcupsd", + 3552: "taserver", + 3553: "rbr-discovery", + 3554: "questnotify", + 3555: "razor", + 3556: "sky-transport", + 3557: "personalos-001", + 3558: "mcp-port", + 3559: "cctv-port", + 3560: "iniserve-port", + 3561: "bmc-onekey", + 3562: "sdbproxy", + 3563: "watcomdebug", + 3564: "esimport", + 3567: "dof-eps", + 3568: "dof-tunnel-sec", + 3569: "mbg-ctrl", + 3570: "mccwebsvr-port", + 3571: "megardsvr-port", + 3572: "megaregsvrport", + 3573: "tag-ups-1", + 3574: "dmaf-caster", + 3575: "ccm-port", + 3576: "cmc-port", + 3577: "config-port", + 3578: "data-port", + 3579: "ttat3lb", + 3580: "nati-svrloc", + 3581: "kfxaclicensing", + 3582: "press", + 3583: "canex-watch", + 3584: "u-dbap", + 3585: "emprise-lls", + 3586: "emprise-lsc", + 3587: "p2pgroup", + 3588: "sentinel", + 3589: "isomair", + 3590: "wv-csp-sms", + 3591: "gtrack-server", + 3592: "gtrack-ne", + 3593: "bpmd", + 3594: "mediaspace", + 3595: "shareapp", + 3596: "iw-mmogame", + 3597: "a14", + 3598: "a15", + 3599: "quasar-server", + 3600: "trap-daemon", + 3601: "visinet-gui", + 3602: "infiniswitchcl", + 3603: "int-rcv-cntrl", + 3604: "bmc-jmx-port", + 3605: "comcam-io", + 3606: "splitlock", + 3607: "precise-i3", + 3608: "trendchip-dcp", + 3609: "cpdi-pidas-cm", + 3610: "echonet", + 3611: "six-degrees", + 3612: "hp-dataprotect", + 3613: "alaris-disc", + 3614: "sigma-port", + 3615: "start-network", + 3616: "cd3o-protocol", + 3617: "sharp-server", + 3618: "aairnet-1", + 3619: "aairnet-2", + 3620: "ep-pcp", + 3621: "ep-nsp", + 3622: "ff-lr-port", + 3623: "haipe-discover", + 3624: "dist-upgrade", + 3625: "volley", + 3626: "bvcdaemon-port", + 3627: "jamserverport", + 3628: "ept-machine", + 3629: "escvpnet", + 3630: "cs-remote-db", + 3631: "cs-services", + 3632: "distcc", + 3633: "wacp", + 3634: "hlibmgr", + 3635: "sdo", + 3636: "servistaitsm", + 3637: "scservp", + 3638: "ehp-backup", + 3639: "xap-ha", + 3640: "netplay-port1", + 3641: "netplay-port2", + 3642: "juxml-port", + 3643: "audiojuggler", + 3644: "ssowatch", + 3645: "cyc", + 3646: "xss-srv-port", + 3647: "splitlock-gw", + 3648: "fjcp", + 3649: "nmmp", + 3650: "prismiq-plugin", + 3651: "xrpc-registry", + 3652: "vxcrnbuport", + 3653: "tsp", + 3654: "vaprtm", + 3655: "abatemgr", + 3656: "abatjss", + 3657: "immedianet-bcn", + 3658: "ps-ams", + 3659: "apple-sasl", + 3660: "can-nds-ssl", + 3661: "can-ferret-ssl", + 3662: "pserver", + 3663: "dtp", + 3664: "ups-engine", + 3665: "ent-engine", + 3666: "eserver-pap", + 3667: "infoexch", + 3668: "dell-rm-port", + 3669: "casanswmgmt", + 3670: "smile", + 3671: "efcp", + 3672: "lispworks-orb", + 3673: "mediavault-gui", + 3674: "wininstall-ipc", + 3675: "calltrax", + 3676: "va-pacbase", + 3677: "roverlog", + 3678: "ipr-dglt", + 3679: "Escale (Newton Dock)", + 3680: "npds-tracker", + 3681: "bts-x73", + 3682: "cas-mapi", + 3683: "bmc-ea", + 3684: "faxstfx-port", + 3685: "dsx-agent", + 3686: "tnmpv2", + 3687: "simple-push", + 3688: "simple-push-s", + 3689: "daap", + 3690: "svn", + 3691: "magaya-network", + 3692: "intelsync", + 3695: "bmc-data-coll", + 3696: "telnetcpcd", + 3697: "nw-license", + 3698: "sagectlpanel", + 3699: "kpn-icw", + 3700: "lrs-paging", + 3701: "netcelera", + 3702: "ws-discovery", + 3703: "adobeserver-3", + 3704: "adobeserver-4", + 3705: "adobeserver-5", + 3706: "rt-event", + 3707: "rt-event-s", + 3708: "sun-as-iiops", + 3709: "ca-idms", + 3710: "portgate-auth", + 3711: "edb-server2", + 3712: "sentinel-ent", + 3713: "tftps", + 3714: "delos-dms", + 3715: "anoto-rendezv", + 3716: "wv-csp-sms-cir", + 3717: "wv-csp-udp-cir", + 3718: "opus-services", + 3719: "itelserverport", + 3720: "ufastro-instr", + 3721: "xsync", + 3722: "xserveraid", + 3723: "sychrond", + 3724: "blizwow", + 3725: "na-er-tip", + 3726: "array-manager", + 3727: "e-mdu", + 3728: "e-woa", + 3729: "fksp-audit", + 3730: "client-ctrl", + 3731: "smap", + 3732: "m-wnn", + 3733: "multip-msg", + 3734: "synel-data", + 3735: "pwdis", + 3736: "rs-rmi", + 3738: "versatalk", + 3739: "launchbird-lm", + 3740: "heartbeat", + 3741: "wysdma", + 3742: "cst-port", + 3743: "ipcs-command", + 3744: "sasg", + 3745: "gw-call-port", + 3746: "linktest", + 3747: "linktest-s", + 3748: "webdata", + 3749: "cimtrak", + 3750: "cbos-ip-port", + 3751: "gprs-cube", + 3752: "vipremoteagent", + 3753: "nattyserver", + 3754: "timestenbroker", + 3755: "sas-remote-hlp", + 3756: "canon-capt", + 3757: "grf-port", + 3758: "apw-registry", + 3759: "exapt-lmgr", + 3760: "adtempusclient", + 3761: "gsakmp", + 3762: "gbs-smp", + 3763: "xo-wave", + 3764: "mni-prot-rout", + 3765: "rtraceroute", + 3767: "listmgr-port", + 3768: "rblcheckd", + 3769: "haipe-otnk", + 3770: "cindycollab", + 3771: "paging-port", + 3772: "ctp", + 3773: "ctdhercules", + 3774: "zicom", + 3775: "ispmmgr", + 3776: "dvcprov-port", + 3777: "jibe-eb", + 3778: "c-h-it-port", + 3779: "cognima", + 3780: "nnp", + 3781: "abcvoice-port", + 3782: "iso-tp0s", + 3783: "bim-pem", + 3784: "bfd-control", + 3785: "bfd-echo", + 3786: "upstriggervsw", + 3787: "fintrx", + 3788: "isrp-port", + 3789: "remotedeploy", + 3790: "quickbooksrds", + 3791: "tvnetworkvideo", + 3792: "sitewatch", + 3793: "dcsoftware", + 3794: "jaus", + 3795: "myblast", + 3796: "spw-dialer", + 3797: "idps", + 3798: "minilock", + 3799: "radius-dynauth", + 3800: "pwgpsi", + 3801: "ibm-mgr", + 3802: "vhd", + 3803: "soniqsync", + 3804: "iqnet-port", + 3805: "tcpdataserver", + 3806: "wsmlb", + 3807: "spugna", + 3808: "sun-as-iiops-ca", + 3809: "apocd", + 3810: "wlanauth", + 3811: "amp", + 3812: "neto-wol-server", + 3813: "rap-ip", + 3814: "neto-dcs", + 3815: "lansurveyorxml", + 3816: "sunlps-http", + 3817: "tapeware", + 3818: "crinis-hb", + 3819: "epl-slp", + 3820: "scp", + 3821: "pmcp", + 3822: "acp-discovery", + 3823: "acp-conduit", + 3824: "acp-policy", + 3825: "ffserver", + 3826: "warmux", + 3827: "netmpi", + 3828: "neteh", + 3829: "neteh-ext", + 3830: "cernsysmgmtagt", + 3831: "dvapps", + 3832: "xxnetserver", + 3833: "aipn-auth", + 3834: "spectardata", + 3835: "spectardb", + 3836: "markem-dcp", + 3837: "mkm-discovery", + 3838: "sos", + 3839: "amx-rms", + 3840: "flirtmitmir", + 3842: "nhci", + 3843: "quest-agent", + 3844: "rnm", + 3845: "v-one-spp", + 3846: "an-pcp", + 3847: "msfw-control", + 3848: "item", + 3849: "spw-dnspreload", + 3850: "qtms-bootstrap", + 3851: "spectraport", + 3852: "sse-app-config", + 3853: "sscan", + 3854: "stryker-com", + 3855: "opentrac", + 3856: "informer", + 3857: "trap-port", + 3858: "trap-port-mom", + 3859: "nav-port", + 3860: "sasp", + 3861: "winshadow-hd", + 3862: "giga-pocket", + 3863: "asap-udp", + 3865: "xpl", + 3866: "dzdaemon", + 3867: "dzoglserver", + 3869: "ovsam-mgmt", + 3870: "ovsam-d-agent", + 3871: "avocent-adsap", + 3872: "oem-agent", + 3873: "fagordnc", + 3874: "sixxsconfig", + 3875: "pnbscada", + 3876: "dl-agent", + 3877: "xmpcr-interface", + 3878: "fotogcad", + 3879: "appss-lm", + 3880: "igrs", + 3881: "idac", + 3882: "msdts1", + 3883: "vrpn", + 3884: "softrack-meter", + 3885: "topflow-ssl", + 3886: "nei-management", + 3887: "ciphire-data", + 3888: "ciphire-serv", + 3889: "dandv-tester", + 3890: "ndsconnect", + 3891: "rtc-pm-port", + 3892: "pcc-image-port", + 3893: "cgi-starapi", + 3894: "syam-agent", + 3895: "syam-smc", + 3896: "sdo-tls", + 3897: "sdo-ssh", + 3898: "senip", + 3899: "itv-control", + 3900: "udt-os", + 3901: "nimsh", + 3902: "nimaux", + 3903: "charsetmgr", + 3904: "omnilink-port", + 3905: "mupdate", + 3906: "topovista-data", + 3907: "imoguia-port", + 3908: "hppronetman", + 3909: "surfcontrolcpa", + 3910: "prnrequest", + 3911: "prnstatus", + 3912: "gbmt-stars", + 3913: "listcrt-port", + 3914: "listcrt-port-2", + 3915: "agcat", + 3916: "wysdmc", + 3917: "aftmux", + 3918: "pktcablemmcops", + 3919: "hyperip", + 3920: "exasoftport1", + 3921: "herodotus-net", + 3922: "sor-update", + 3923: "symb-sb-port", + 3924: "mpl-gprs-port", + 3925: "zmp", + 3926: "winport", + 3927: "natdataservice", + 3928: "netboot-pxe", + 3929: "smauth-port", + 3930: "syam-webserver", + 3931: "msr-plugin-port", + 3932: "dyn-site", + 3933: "plbserve-port", + 3934: "sunfm-port", + 3935: "sdp-portmapper", + 3936: "mailprox", + 3937: "dvbservdsc", + 3938: "dbcontrol-agent", + 3939: "aamp", + 3940: "xecp-node", + 3941: "homeportal-web", + 3942: "srdp", + 3943: "tig", + 3944: "sops", + 3945: "emcads", + 3946: "backupedge", + 3947: "ccp", + 3948: "apdap", + 3949: "drip", + 3950: "namemunge", + 3951: "pwgippfax", + 3952: "i3-sessionmgr", + 3953: "xmlink-connect", + 3954: "adrep", + 3955: "p2pcommunity", + 3956: "gvcp", + 3957: "mqe-broker", + 3958: "mqe-agent", + 3959: "treehopper", + 3960: "bess", + 3961: "proaxess", + 3962: "sbi-agent", + 3963: "thrp", + 3964: "sasggprs", + 3965: "ati-ip-to-ncpe", + 3966: "bflckmgr", + 3967: "ppsms", + 3968: "ianywhere-dbns", + 3969: "landmarks", + 3970: "lanrevagent", + 3971: "lanrevserver", + 3972: "iconp", + 3973: "progistics", + 3974: "citysearch", + 3975: "airshot", + 3976: "opswagent", + 3977: "opswmanager", + 3978: "secure-cfg-svr", + 3979: "smwan", + 3980: "acms", + 3981: "starfish", + 3982: "eis", + 3983: "eisp", + 3984: "mapper-nodemgr", + 3985: "mapper-mapethd", + 3986: "mapper-ws-ethd", + 3987: "centerline", + 3988: "dcs-config", + 3989: "bv-queryengine", + 3990: "bv-is", + 3991: "bv-smcsrv", + 3992: "bv-ds", + 3993: "bv-agent", + 3995: "iss-mgmt-ssl", + 3996: "abcsoftware", + 3997: "agentsease-db", + 3998: "dnx", + 3999: "nvcnet", + 4000: "terabase", + 4001: "newoak", + 4002: "pxc-spvr-ft", + 4003: "pxc-splr-ft", + 4004: "pxc-roid", + 4005: "pxc-pin", + 4006: "pxc-spvr", + 4007: "pxc-splr", + 4008: "netcheque", + 4009: "chimera-hwm", + 4010: "samsung-unidex", + 4011: "altserviceboot", + 4012: "pda-gate", + 4013: "acl-manager", + 4014: "taiclock", + 4015: "talarian-mcast1", + 4016: "talarian-mcast2", + 4017: "talarian-mcast3", + 4018: "talarian-mcast4", + 4019: "talarian-mcast5", + 4020: "trap", + 4021: "nexus-portal", + 4022: "dnox", + 4023: "esnm-zoning", + 4024: "tnp1-port", + 4025: "partimage", + 4026: "as-debug", + 4027: "bxp", + 4028: "dtserver-port", + 4029: "ip-qsig", + 4030: "jdmn-port", + 4031: "suucp", + 4032: "vrts-auth-port", + 4033: "sanavigator", + 4034: "ubxd", + 4035: "wap-push-http", + 4036: "wap-push-https", + 4037: "ravehd", + 4038: "fazzt-ptp", + 4039: "fazzt-admin", + 4040: "yo-main", + 4041: "houston", + 4042: "ldxp", + 4043: "nirp", + 4044: "ltp", + 4045: "npp", + 4046: "acp-proto", + 4047: "ctp-state", + 4049: "wafs", + 4050: "cisco-wafs", + 4051: "cppdp", + 4052: "interact", + 4053: "ccu-comm-1", + 4054: "ccu-comm-2", + 4055: "ccu-comm-3", + 4056: "lms", + 4057: "wfm", + 4058: "kingfisher", + 4059: "dlms-cosem", + 4060: "dsmeter-iatc", + 4061: "ice-location", + 4062: "ice-slocation", + 4063: "ice-router", + 4064: "ice-srouter", + 4065: "avanti-cdp", + 4066: "pmas", + 4067: "idp", + 4068: "ipfltbcst", + 4069: "minger", + 4070: "tripe", + 4071: "aibkup", + 4072: "zieto-sock", + 4073: "iRAPP", + 4074: "cequint-cityid", + 4075: "perimlan", + 4076: "seraph", + 4077: "ascomalarm", + 4079: "santools", + 4080: "lorica-in", + 4081: "lorica-in-sec", + 4082: "lorica-out", + 4083: "lorica-out-sec", + 4084: "fortisphere-vm", + 4086: "ftsync", + 4089: "opencore", + 4090: "omasgport", + 4091: "ewinstaller", + 4092: "ewdgs", + 4093: "pvxpluscs", + 4094: "sysrqd", + 4095: "xtgui", + 4096: "bre", + 4097: "patrolview", + 4098: "drmsfsd", + 4099: "dpcp", + 4100: "igo-incognito", + 4101: "brlp-0", + 4102: "brlp-1", + 4103: "brlp-2", + 4104: "brlp-3", + 4105: "shofar", + 4106: "synchronite", + 4107: "j-ac", + 4108: "accel", + 4109: "izm", + 4110: "g2tag", + 4111: "xgrid", + 4112: "apple-vpns-rp", + 4113: "aipn-reg", + 4114: "jomamqmonitor", + 4115: "cds", + 4116: "smartcard-tls", + 4117: "hillrserv", + 4118: "netscript", + 4119: "assuria-slm", + 4121: "e-builder", + 4122: "fprams", + 4123: "z-wave", + 4124: "tigv2", + 4125: "opsview-envoy", + 4126: "ddrepl", + 4127: "unikeypro", + 4128: "nufw", + 4129: "nuauth", + 4130: "fronet", + 4131: "stars", + 4132: "nuts-dem", + 4133: "nuts-bootp", + 4134: "nifty-hmi", + 4135: "cl-db-attach", + 4136: "cl-db-request", + 4137: "cl-db-remote", + 4138: "nettest", + 4139: "thrtx", + 4140: "cedros-fds", + 4141: "oirtgsvc", + 4142: "oidocsvc", + 4143: "oidsr", + 4145: "vvr-control", + 4146: "tgcconnect", + 4147: "vrxpservman", + 4148: "hhb-handheld", + 4149: "agslb", + 4150: "PowerAlert-nsa", + 4151: "menandmice-noh", + 4152: "idig-mux", + 4153: "mbl-battd", + 4154: "atlinks", + 4155: "bzr", + 4156: "stat-results", + 4157: "stat-scanner", + 4158: "stat-cc", + 4159: "nss", + 4160: "jini-discovery", + 4161: "omscontact", + 4162: "omstopology", + 4163: "silverpeakpeer", + 4164: "silverpeakcomm", + 4165: "altcp", + 4166: "joost", + 4167: "ddgn", + 4168: "pslicser", + 4169: "iadt-disc", + 4172: "pcoip", + 4173: "mma-discovery", + 4174: "sm-disc", + 4177: "wello", + 4178: "storman", + 4179: "MaxumSP", + 4180: "httpx", + 4181: "macbak", + 4182: "pcptcpservice", + 4183: "cyborgnet", + 4184: "universe-suite", + 4185: "wcpp", + 4188: "vatata", + 4191: "dsmipv6", + 4192: "azeti-bd", + 4199: "eims-admin", + 4300: "corelccam", + 4301: "d-data", + 4302: "d-data-control", + 4303: "srcp", + 4304: "owserver", + 4305: "batman", + 4306: "pinghgl", + 4307: "visicron-vs", + 4308: "compx-lockview", + 4309: "dserver", + 4310: "mirrtex", + 4320: "fdt-rcatp", + 4321: "rwhois", + 4322: "trim-event", + 4323: "trim-ice", + 4325: "geognosisman", + 4326: "geognosis", + 4327: "jaxer-web", + 4328: "jaxer-manager", + 4333: "ahsp", + 4340: "gaia", + 4341: "lisp-data", + 4342: "lisp-control", + 4343: "unicall", + 4344: "vinainstall", + 4345: "m4-network-as", + 4346: "elanlm", + 4347: "lansurveyor", + 4348: "itose", + 4349: "fsportmap", + 4350: "net-device", + 4351: "plcy-net-svcs", + 4352: "pjlink", + 4353: "f5-iquery", + 4354: "qsnet-trans", + 4355: "qsnet-workst", + 4356: "qsnet-assist", + 4357: "qsnet-cond", + 4358: "qsnet-nucl", + 4359: "omabcastltkm", + 4361: "nacnl", + 4362: "afore-vdp-disc", + 4366: "shadowstream", + 4368: "wxbrief", + 4369: "epmd", + 4370: "elpro-tunnel", + 4371: "l2c-disc", + 4372: "l2c-data", + 4373: "remctl", + 4375: "tolteces", + 4376: "bip", + 4377: "cp-spxsvr", + 4378: "cp-spxdpy", + 4379: "ctdb", + 4389: "xandros-cms", + 4390: "wiegand", + 4394: "apwi-disc", + 4395: "omnivisionesx", + 4400: "ds-srv", + 4401: "ds-srvr", + 4402: "ds-clnt", + 4403: "ds-user", + 4404: "ds-admin", + 4405: "ds-mail", + 4406: "ds-slp", + 4412: "smallchat", + 4413: "avi-nms-disc", + 4416: "pjj-player-disc", + 4418: "axysbridge", + 4420: "nvm-express", + 4425: "netrockey6", + 4426: "beacon-port-2", + 4430: "rsqlserver", + 4432: "l-acoustics", + 4441: "netblox", + 4442: "saris", + 4443: "pharos", + 4444: "krb524", + 4445: "upnotifyp", + 4446: "n1-fwp", + 4447: "n1-rmgmt", + 4448: "asc-slmd", + 4449: "privatewire", + 4450: "camp", + 4451: "ctisystemmsg", + 4452: "ctiprogramload", + 4453: "nssalertmgr", + 4454: "nssagentmgr", + 4455: "prchat-user", + 4456: "prchat-server", + 4457: "prRegister", + 4458: "mcp", + 4484: "hpssmgmt", + 4486: "icms", + 4488: "awacs-ice", + 4500: "ipsec-nat-t", + 4534: "armagetronad", + 4535: "ehs", + 4536: "ehs-ssl", + 4537: "wssauthsvc", + 4538: "swx-gate", + 4545: "worldscores", + 4546: "sf-lm", + 4547: "lanner-lm", + 4548: "synchromesh", + 4549: "aegate", + 4550: "gds-adppiw-db", + 4551: "ieee-mih", + 4552: "menandmice-mon", + 4554: "msfrs", + 4555: "rsip", + 4556: "dtn-bundle", + 4557: "mtcevrunqss", + 4558: "mtcevrunqman", + 4559: "hylafax", + 4566: "kwtc", + 4567: "tram", + 4568: "bmc-reporting", + 4569: "iax", + 4591: "l3t-at-an", + 4592: "hrpd-ith-at-an", + 4593: "ipt-anri-anri", + 4594: "ias-session", + 4595: "ias-paging", + 4596: "ias-neighbor", + 4597: "a21-an-1xbs", + 4598: "a16-an-an", + 4599: "a17-an-an", + 4600: "piranha1", + 4601: "piranha2", + 4621: "ventoso", + 4658: "playsta2-app", + 4659: "playsta2-lob", + 4660: "smaclmgr", + 4661: "kar2ouche", + 4662: "oms", + 4663: "noteit", + 4664: "ems", + 4665: "contclientms", + 4666: "eportcomm", + 4667: "mmacomm", + 4668: "mmaeds", + 4669: "eportcommdata", + 4670: "light", + 4671: "acter", + 4672: "rfa", + 4673: "cxws", + 4674: "appiq-mgmt", + 4675: "dhct-status", + 4676: "dhct-alerts", + 4677: "bcs", + 4678: "traversal", + 4679: "mgesupervision", + 4680: "mgemanagement", + 4681: "parliant", + 4682: "finisar", + 4683: "spike", + 4684: "rfid-rp1", + 4685: "autopac", + 4686: "msp-os", + 4687: "nst", + 4688: "mobile-p2p", + 4689: "altovacentral", + 4690: "prelude", + 4691: "mtn", + 4692: "conspiracy", + 4700: "netxms-agent", + 4701: "netxms-mgmt", + 4702: "netxms-sync", + 4725: "truckstar", + 4726: "a26-fap-fgw", + 4727: "fcis-disc", + 4728: "capmux", + 4729: "gsmtap", + 4730: "gearman", + 4732: "ohmtrigger", + 4737: "ipdr-sp", + 4738: "solera-lpn", + 4739: "ipfix", + 4740: "ipfixs", + 4741: "lumimgrd", + 4742: "sicct-sdp", + 4743: "openhpid", + 4744: "ifsp", + 4745: "fmp", + 4746: "intelliadm-disc", + 4747: "buschtrommel", + 4749: "profilemac", + 4750: "ssad", + 4751: "spocp", + 4752: "snap", + 4753: "simon-disc", + 4754: "gre-in-udp", + 4755: "gre-udp-dtls", + 4784: "bfd-multi-ctl", + 4785: "cncp", + 4789: "vxlan", + 4790: "vxlan-gpe", + 4791: "roce", + 4800: "iims", + 4801: "iwec", + 4802: "ilss", + 4803: "notateit-disc", + 4804: "aja-ntv4-disc", + 4827: "htcp", + 4837: "varadero-0", + 4838: "varadero-1", + 4839: "varadero-2", + 4840: "opcua-udp", + 4841: "quosa", + 4842: "gw-asv", + 4843: "opcua-tls", + 4844: "gw-log", + 4845: "wcr-remlib", + 4846: "contamac-icm", + 4847: "wfc", + 4848: "appserv-http", + 4849: "appserv-https", + 4850: "sun-as-nodeagt", + 4851: "derby-repli", + 4867: "unify-debug", + 4868: "phrelay", + 4869: "phrelaydbg", + 4870: "cc-tracking", + 4871: "wired", + 4876: "tritium-can", + 4877: "lmcs", + 4878: "inst-discovery", + 4881: "socp-t", + 4882: "socp-c", + 4884: "hivestor", + 4885: "abbs", + 4894: "lyskom", + 4899: "radmin-port", + 4900: "hfcs", + 4914: "bones", + 4936: "an-signaling", + 4937: "atsc-mh-ssc", + 4940: "eq-office-4940", + 4941: "eq-office-4941", + 4942: "eq-office-4942", + 4949: "munin", + 4950: "sybasesrvmon", + 4951: "pwgwims", + 4952: "sagxtsds", + 4969: "ccss-qmm", + 4970: "ccss-qsm", + 4980: "ctxs-vpp", + 4986: "mrip", + 4987: "smar-se-port1", + 4988: "smar-se-port2", + 4989: "parallel", + 4990: "busycal", + 4991: "vrt", + 4999: "hfcs-manager", + 5000: "commplex-main", + 5001: "commplex-link", + 5002: "rfe", + 5003: "fmpro-internal", + 5004: "avt-profile-1", + 5005: "avt-profile-2", + 5006: "wsm-server", + 5007: "wsm-server-ssl", + 5008: "synapsis-edge", + 5009: "winfs", + 5010: "telelpathstart", + 5011: "telelpathattack", + 5012: "nsp", + 5013: "fmpro-v6", + 5014: "onpsocket", + 5020: "zenginkyo-1", + 5021: "zenginkyo-2", + 5022: "mice", + 5023: "htuilsrv", + 5024: "scpi-telnet", + 5025: "scpi-raw", + 5026: "strexec-d", + 5027: "strexec-s", + 5029: "infobright", + 5030: "surfpass", + 5031: "dmp", + 5042: "asnaacceler8db", + 5043: "swxadmin", + 5044: "lxi-evntsvc", + 5046: "vpm-udp", + 5047: "iscape", + 5049: "ivocalize", + 5050: "mmcc", + 5051: "ita-agent", + 5052: "ita-manager", + 5053: "rlm-disc", + 5055: "unot", + 5056: "intecom-ps1", + 5057: "intecom-ps2", + 5058: "locus-disc", + 5059: "sds", + 5060: "sip", + 5061: "sips", + 5062: "na-localise", + 5064: "ca-1", + 5065: "ca-2", + 5066: "stanag-5066", + 5067: "authentx", + 5069: "i-net-2000-npr", + 5070: "vtsas", + 5071: "powerschool", + 5072: "ayiya", + 5073: "tag-pm", + 5074: "alesquery", + 5078: "pixelpusher", + 5079: "cp-spxrpts", + 5080: "onscreen", + 5081: "sdl-ets", + 5082: "qcp", + 5083: "qfp", + 5084: "llrp", + 5085: "encrypted-llrp", + 5092: "magpie", + 5093: "sentinel-lm", + 5094: "hart-ip", + 5099: "sentlm-srv2srv", + 5100: "socalia", + 5101: "talarian-udp", + 5102: "oms-nonsecure", + 5104: "tinymessage", + 5105: "hughes-ap", + 5111: "taep-as-svc", + 5112: "pm-cmdsvr", + 5116: "emb-proj-cmd", + 5120: "barracuda-bbs", + 5133: "nbt-pc", + 5136: "minotaur-sa", + 5137: "ctsd", + 5145: "rmonitor-secure", + 5150: "atmp", + 5151: "esri-sde", + 5152: "sde-discovery", + 5154: "bzflag", + 5155: "asctrl-agent", + 5164: "vpa-disc", + 5165: "ife-icorp", + 5166: "winpcs", + 5167: "scte104", + 5168: "scte30", + 5190: "aol", + 5191: "aol-1", + 5192: "aol-2", + 5193: "aol-3", + 5200: "targus-getdata", + 5201: "targus-getdata1", + 5202: "targus-getdata2", + 5203: "targus-getdata3", + 5223: "hpvirtgrp", + 5224: "hpvirtctrl", + 5225: "hp-server", + 5226: "hp-status", + 5227: "perfd", + 5234: "eenet", + 5235: "galaxy-network", + 5236: "padl2sim", + 5237: "mnet-discovery", + 5245: "downtools-disc", + 5246: "capwap-control", + 5247: "capwap-data", + 5248: "caacws", + 5249: "caaclang2", + 5250: "soagateway", + 5251: "caevms", + 5252: "movaz-ssc", + 5264: "3com-njack-1", + 5265: "3com-njack-2", + 5270: "cartographerxmp", + 5271: "cuelink-disc", + 5272: "pk", + 5282: "transmit-port", + 5298: "presence", + 5299: "nlg-data", + 5300: "hacl-hb", + 5301: "hacl-gs", + 5302: "hacl-cfg", + 5303: "hacl-probe", + 5304: "hacl-local", + 5305: "hacl-test", + 5306: "sun-mc-grp", + 5307: "sco-aip", + 5308: "cfengine", + 5309: "jprinter", + 5310: "outlaws", + 5312: "permabit-cs", + 5313: "rrdp", + 5314: "opalis-rbt-ipc", + 5315: "hacl-poll", + 5343: "kfserver", + 5344: "xkotodrcp", + 5349: "stuns", + 5350: "pcp-multicast", + 5351: "pcp", + 5352: "dns-llq", + 5353: "mdns", + 5354: "mdnsresponder", + 5355: "llmnr", + 5356: "ms-smlbiz", + 5357: "wsdapi", + 5358: "wsdapi-s", + 5359: "ms-alerter", + 5360: "ms-sideshow", + 5361: "ms-s-sideshow", + 5362: "serverwsd2", + 5363: "net-projection", + 5364: "kdnet", + 5397: "stresstester", + 5398: "elektron-admin", + 5399: "securitychase", + 5400: "excerpt", + 5401: "excerpts", + 5402: "mftp", + 5403: "hpoms-ci-lstn", + 5404: "hpoms-dps-lstn", + 5405: "netsupport", + 5406: "systemics-sox", + 5407: "foresyte-clear", + 5408: "foresyte-sec", + 5409: "salient-dtasrv", + 5410: "salient-usrmgr", + 5411: "actnet", + 5412: "continuus", + 5413: "wwiotalk", + 5414: "statusd", + 5415: "ns-server", + 5416: "sns-gateway", + 5417: "sns-agent", + 5418: "mcntp", + 5419: "dj-ice", + 5420: "cylink-c", + 5421: "netsupport2", + 5422: "salient-mux", + 5423: "virtualuser", + 5424: "beyond-remote", + 5425: "br-channel", + 5426: "devbasic", + 5427: "sco-peer-tta", + 5428: "telaconsole", + 5429: "base", + 5430: "radec-corp", + 5431: "park-agent", + 5432: "postgresql", + 5433: "pyrrho", + 5434: "sgi-arrayd", + 5435: "sceanics", + 5436: "pmip6-cntl", + 5437: "pmip6-data", + 5443: "spss", + 5450: "tiepie-disc", + 5453: "surebox", + 5454: "apc-5454", + 5455: "apc-5455", + 5456: "apc-5456", + 5461: "silkmeter", + 5462: "ttl-publisher", + 5463: "ttlpriceproxy", + 5464: "quailnet", + 5465: "netops-broker", + 5474: "apsolab-rpc", + 5500: "fcp-addr-srvr1", + 5501: "fcp-addr-srvr2", + 5502: "fcp-srvr-inst1", + 5503: "fcp-srvr-inst2", + 5504: "fcp-cics-gw1", + 5505: "checkoutdb", + 5506: "amc", + 5553: "sgi-eventmond", + 5554: "sgi-esphttp", + 5555: "personal-agent", + 5556: "freeciv", + 5567: "dof-dps-mc-sec", + 5568: "sdt", + 5569: "rdmnet-device", + 5573: "sdmmp", + 5580: "tmosms0", + 5581: "tmosms1", + 5582: "fac-restore", + 5583: "tmo-icon-sync", + 5584: "bis-web", + 5585: "bis-sync", + 5597: "ininmessaging", + 5598: "mctfeed", + 5599: "esinstall", + 5600: "esmmanager", + 5601: "esmagent", + 5602: "a1-msc", + 5603: "a1-bs", + 5604: "a3-sdunode", + 5605: "a4-sdunode", + 5627: "ninaf", + 5628: "htrust", + 5629: "symantec-sfdb", + 5630: "precise-comm", + 5631: "pcanywheredata", + 5632: "pcanywherestat", + 5633: "beorl", + 5634: "xprtld", + 5670: "zre-disc", + 5671: "amqps", + 5672: "amqp", + 5673: "jms", + 5674: "hyperscsi-port", + 5675: "v5ua", + 5676: "raadmin", + 5677: "questdb2-lnchr", + 5678: "rrac", + 5679: "dccm", + 5680: "auriga-router", + 5681: "ncxcp", + 5682: "brightcore", + 5683: "coap", + 5684: "coaps", + 5687: "gog-multiplayer", + 5688: "ggz", + 5689: "qmvideo", + 5713: "proshareaudio", + 5714: "prosharevideo", + 5715: "prosharedata", + 5716: "prosharerequest", + 5717: "prosharenotify", + 5718: "dpm", + 5719: "dpm-agent", + 5720: "ms-licensing", + 5721: "dtpt", + 5722: "msdfsr", + 5723: "omhs", + 5724: "omsdk", + 5728: "io-dist-group", + 5729: "openmail", + 5730: "unieng", + 5741: "ida-discover1", + 5742: "ida-discover2", + 5743: "watchdoc-pod", + 5744: "watchdoc", + 5745: "fcopy-server", + 5746: "fcopys-server", + 5747: "tunatic", + 5748: "tunalyzer", + 5750: "rscd", + 5755: "openmailg", + 5757: "x500ms", + 5766: "openmailns", + 5767: "s-openmail", + 5768: "openmailpxy", + 5769: "spramsca", + 5770: "spramsd", + 5771: "netagent", + 5777: "dali-port", + 5781: "3par-evts", + 5782: "3par-mgmt", + 5783: "3par-mgmt-ssl", + 5784: "ibar", + 5785: "3par-rcopy", + 5786: "cisco-redu", + 5787: "waascluster", + 5793: "xtreamx", + 5794: "spdp", + 5813: "icmpd", + 5814: "spt-automation", + 5859: "wherehoo", + 5863: "ppsuitemsg", + 5900: "rfb", + 5910: "cm", + 5911: "cpdlc", + 5912: "fis", + 5913: "ads-c", + 5963: "indy", + 5968: "mppolicy-v5", + 5969: "mppolicy-mgr", + 5984: "couchdb", + 5985: "wsman", + 5986: "wsmans", + 5987: "wbem-rmi", + 5988: "wbem-http", + 5989: "wbem-https", + 5990: "wbem-exp-https", + 5991: "nuxsl", + 5992: "consul-insight", + 5999: "cvsup", + 6064: "ndl-ahp-svc", + 6065: "winpharaoh", + 6066: "ewctsp", + 6069: "trip", + 6070: "messageasap", + 6071: "ssdtp", + 6072: "diagnose-proc", + 6073: "directplay8", + 6074: "max", + 6080: "gue", + 6081: "geneve", + 6082: "p25cai", + 6083: "miami-bcast", + 6085: "konspire2b", + 6086: "pdtp", + 6087: "ldss", + 6088: "doglms-notify", + 6100: "synchronet-db", + 6101: "synchronet-rtc", + 6102: "synchronet-upd", + 6103: "rets", + 6104: "dbdb", + 6105: "primaserver", + 6106: "mpsserver", + 6107: "etc-control", + 6108: "sercomm-scadmin", + 6109: "globecast-id", + 6110: "softcm", + 6111: "spc", + 6112: "dtspcd", + 6118: "tipc", + 6122: "bex-webadmin", + 6123: "backup-express", + 6124: "pnbs", + 6133: "nbt-wol", + 6140: "pulsonixnls", + 6141: "meta-corp", + 6142: "aspentec-lm", + 6143: "watershed-lm", + 6144: "statsci1-lm", + 6145: "statsci2-lm", + 6146: "lonewolf-lm", + 6147: "montage-lm", + 6148: "ricardo-lm", + 6149: "tal-pod", + 6160: "ecmp-data", + 6161: "patrol-ism", + 6162: "patrol-coll", + 6163: "pscribe", + 6200: "lm-x", + 6201: "thermo-calc", + 6209: "qmtps", + 6222: "radmind", + 6241: "jeol-nsddp-1", + 6242: "jeol-nsddp-2", + 6243: "jeol-nsddp-3", + 6244: "jeol-nsddp-4", + 6251: "tl1-raw-ssl", + 6252: "tl1-ssh", + 6253: "crip", + 6268: "grid", + 6269: "grid-alt", + 6300: "bmc-grx", + 6301: "bmc-ctd-ldap", + 6306: "ufmp", + 6315: "scup-disc", + 6316: "abb-escp", + 6317: "nav-data", + 6320: "repsvc", + 6321: "emp-server1", + 6322: "emp-server2", + 6324: "hrd-ns-disc", + 6343: "sflow", + 6346: "gnutella-svc", + 6347: "gnutella-rtr", + 6350: "adap", + 6355: "pmcs", + 6360: "metaedit-mu", + 6363: "ndn", + 6370: "metaedit-se", + 6382: "metatude-mds", + 6389: "clariion-evr01", + 6390: "metaedit-ws", + 6417: "faxcomservice", + 6419: "svdrp-disc", + 6420: "nim-vdrshell", + 6421: "nim-wan", + 6443: "sun-sr-https", + 6444: "sge-qmaster", + 6445: "sge-execd", + 6446: "mysql-proxy", + 6455: "skip-cert-recv", + 6456: "skip-cert-send", + 6471: "lvision-lm", + 6480: "sun-sr-http", + 6481: "servicetags", + 6482: "ldoms-mgmt", + 6483: "SunVTS-RMI", + 6484: "sun-sr-jms", + 6485: "sun-sr-iiop", + 6486: "sun-sr-iiops", + 6487: "sun-sr-iiop-aut", + 6488: "sun-sr-jmx", + 6489: "sun-sr-admin", + 6500: "boks", + 6501: "boks-servc", + 6502: "boks-servm", + 6503: "boks-clntd", + 6505: "badm-priv", + 6506: "badm-pub", + 6507: "bdir-priv", + 6508: "bdir-pub", + 6509: "mgcs-mfp-port", + 6510: "mcer-port", + 6511: "dccp-udp", + 6514: "syslog-tls", + 6515: "elipse-rec", + 6543: "lds-distrib", + 6544: "lds-dump", + 6547: "apc-6547", + 6548: "apc-6548", + 6549: "apc-6549", + 6550: "fg-sysupdate", + 6551: "sum", + 6558: "xdsxdm", + 6566: "sane-port", + 6568: "rp-reputation", + 6579: "affiliate", + 6580: "parsec-master", + 6581: "parsec-peer", + 6582: "parsec-game", + 6583: "joaJewelSuite", + 6619: "odette-ftps", + 6620: "kftp-data", + 6621: "kftp", + 6622: "mcftp", + 6623: "ktelnet", + 6626: "wago-service", + 6627: "nexgen", + 6628: "afesc-mc", + 6629: "nexgen-aux", + 6633: "cisco-vpath-tun", + 6634: "mpls-pm", + 6635: "mpls-udp", + 6636: "mpls-udp-dtls", + 6653: "openflow", + 6657: "palcom-disc", + 6670: "vocaltec-gold", + 6671: "p4p-portal", + 6672: "vision-server", + 6673: "vision-elmd", + 6678: "vfbp-disc", + 6679: "osaut", + 6689: "tsa", + 6696: "babel", + 6701: "kti-icad-srvr", + 6702: "e-design-net", + 6703: "e-design-web", + 6714: "ibprotocol", + 6715: "fibotrader-com", + 6767: "bmc-perf-agent", + 6768: "bmc-perf-mgrd", + 6769: "adi-gxp-srvprt", + 6770: "plysrv-http", + 6771: "plysrv-https", + 6784: "bfd-lag", + 6785: "dgpf-exchg", + 6786: "smc-jmx", + 6787: "smc-admin", + 6788: "smc-http", + 6790: "hnmp", + 6791: "hnm", + 6801: "acnet", + 6831: "ambit-lm", + 6841: "netmo-default", + 6842: "netmo-http", + 6850: "iccrushmore", + 6868: "acctopus-st", + 6888: "muse", + 6935: "ethoscan", + 6936: "xsmsvc", + 6946: "bioserver", + 6951: "otlp", + 6961: "jmact3", + 6962: "jmevt2", + 6963: "swismgr1", + 6964: "swismgr2", + 6965: "swistrap", + 6966: "swispol", + 6969: "acmsoda", + 6997: "MobilitySrv", + 6998: "iatp-highpri", + 6999: "iatp-normalpri", + 7000: "afs3-fileserver", + 7001: "afs3-callback", + 7002: "afs3-prserver", + 7003: "afs3-vlserver", + 7004: "afs3-kaserver", + 7005: "afs3-volser", + 7006: "afs3-errors", + 7007: "afs3-bos", + 7008: "afs3-update", + 7009: "afs3-rmtsys", + 7010: "ups-onlinet", + 7011: "talon-disc", + 7012: "talon-engine", + 7013: "microtalon-dis", + 7014: "microtalon-com", + 7015: "talon-webserver", + 7019: "doceri-view", + 7020: "dpserve", + 7021: "dpserveadmin", + 7022: "ctdp", + 7023: "ct2nmcs", + 7024: "vmsvc", + 7025: "vmsvc-2", + 7030: "op-probe", + 7040: "quest-disc", + 7070: "arcp", + 7071: "iwg1", + 7080: "empowerid", + 7088: "zixi-transport", + 7095: "jdp-disc", + 7099: "lazy-ptop", + 7100: "font-service", + 7101: "elcn", + 7107: "aes-x170", + 7121: "virprot-lm", + 7128: "scenidm", + 7129: "scenccs", + 7161: "cabsm-comm", + 7162: "caistoragemgr", + 7163: "cacsambroker", + 7164: "fsr", + 7165: "doc-server", + 7166: "aruba-server", + 7169: "ccag-pib", + 7170: "nsrp", + 7171: "drm-production", + 7174: "clutild", + 7181: "janus-disc", + 7200: "fodms", + 7201: "dlip", + 7227: "ramp", + 7235: "aspcoordination", + 7244: "frc-hicp-disc", + 7262: "cnap", + 7272: "watchme-7272", + 7273: "oma-rlp", + 7274: "oma-rlp-s", + 7275: "oma-ulp", + 7276: "oma-ilp", + 7277: "oma-ilp-s", + 7278: "oma-dcdocbs", + 7279: "ctxlic", + 7280: "itactionserver1", + 7281: "itactionserver2", + 7282: "mzca-alert", + 7365: "lcm-server", + 7391: "mindfilesys", + 7392: "mrssrendezvous", + 7393: "nfoldman", + 7394: "fse", + 7395: "winqedit", + 7397: "hexarc", + 7400: "rtps-discovery", + 7401: "rtps-dd-ut", + 7402: "rtps-dd-mt", + 7410: "ionixnetmon", + 7411: "daqstream", + 7421: "mtportmon", + 7426: "pmdmgr", + 7427: "oveadmgr", + 7428: "ovladmgr", + 7429: "opi-sock", + 7430: "xmpv7", + 7431: "pmd", + 7437: "faximum", + 7443: "oracleas-https", + 7473: "rise", + 7491: "telops-lmd", + 7500: "silhouette", + 7501: "ovbus", + 7510: "ovhpas", + 7511: "pafec-lm", + 7542: "saratoga", + 7543: "atul", + 7544: "nta-ds", + 7545: "nta-us", + 7546: "cfs", + 7547: "cwmp", + 7548: "tidp", + 7549: "nls-tl", + 7550: "cloudsignaling", + 7560: "sncp", + 7566: "vsi-omega", + 7570: "aries-kfinder", + 7574: "coherence-disc", + 7588: "sun-lm", + 7606: "mipi-debug", + 7624: "indi", + 7627: "soap-http", + 7628: "zen-pawn", + 7629: "xdas", + 7633: "pmdfmgt", + 7648: "cuseeme", + 7674: "imqtunnels", + 7675: "imqtunnel", + 7676: "imqbrokerd", + 7677: "sun-user-https", + 7680: "pando-pub", + 7689: "collaber", + 7697: "klio", + 7707: "sync-em7", + 7708: "scinet", + 7720: "medimageportal", + 7724: "nsdeepfreezectl", + 7725: "nitrogen", + 7726: "freezexservice", + 7727: "trident-data", + 7728: "osvr", + 7734: "smip", + 7738: "aiagent", + 7741: "scriptview", + 7743: "sstp-1", + 7744: "raqmon-pdu", + 7747: "prgp", + 7777: "cbt", + 7778: "interwise", + 7779: "vstat", + 7781: "accu-lmgr", + 7784: "s-bfd", + 7786: "minivend", + 7787: "popup-reminders", + 7789: "office-tools", + 7794: "q3ade", + 7797: "pnet-conn", + 7798: "pnet-enc", + 7799: "altbsdp", + 7800: "asr", + 7801: "ssp-client", + 7802: "vns-tp", + 7810: "rbt-wanopt", + 7845: "apc-7845", + 7846: "apc-7846", + 7872: "mipv6tls", + 7880: "pss", + 7887: "ubroker", + 7900: "mevent", + 7901: "tnos-sp", + 7902: "tnos-dp", + 7903: "tnos-dps", + 7913: "qo-secure", + 7932: "t2-drm", + 7933: "t2-brm", + 7962: "generalsync", + 7967: "supercell", + 7979: "micromuse-ncps", + 7980: "quest-vista", + 7982: "sossd-disc", + 7998: "usicontentpush", + 7999: "irdmi2", + 8000: "irdmi", + 8001: "vcom-tunnel", + 8002: "teradataordbms", + 8003: "mcreport", + 8005: "mxi", + 8008: "http-alt", + 8019: "qbdb", + 8020: "intu-ec-svcdisc", + 8021: "intu-ec-client", + 8022: "oa-system", + 8025: "ca-audit-da", + 8026: "ca-audit-ds", + 8032: "pro-ed", + 8033: "mindprint", + 8034: "vantronix-mgmt", + 8040: "ampify", + 8052: "senomix01", + 8053: "senomix02", + 8054: "senomix03", + 8055: "senomix04", + 8056: "senomix05", + 8057: "senomix06", + 8058: "senomix07", + 8059: "senomix08", + 8060: "aero", + 8074: "gadugadu", + 8080: "http-alt", + 8081: "sunproxyadmin", + 8082: "us-cli", + 8083: "us-srv", + 8086: "d-s-n", + 8087: "simplifymedia", + 8088: "radan-http", + 8097: "sac", + 8100: "xprint-server", + 8115: "mtl8000-matrix", + 8116: "cp-cluster", + 8118: "privoxy", + 8121: "apollo-data", + 8122: "apollo-admin", + 8128: "paycash-online", + 8129: "paycash-wbp", + 8130: "indigo-vrmi", + 8131: "indigo-vbcp", + 8132: "dbabble", + 8148: "isdd", + 8149: "eor-game", + 8160: "patrol", + 8161: "patrol-snmp", + 8182: "vmware-fdm", + 8184: "itach", + 8192: "spytechphone", + 8194: "blp1", + 8195: "blp2", + 8199: "vvr-data", + 8200: "trivnet1", + 8201: "trivnet2", + 8202: "aesop", + 8204: "lm-perfworks", + 8205: "lm-instmgr", + 8206: "lm-dta", + 8207: "lm-sserver", + 8208: "lm-webwatcher", + 8230: "rexecj", + 8231: "hncp-udp-port", + 8232: "hncp-dtls-port", + 8243: "synapse-nhttps", + 8276: "pando-sec", + 8280: "synapse-nhttp", + 8282: "libelle-disc", + 8292: "blp3", + 8294: "blp4", + 8300: "tmi", + 8301: "amberon", + 8320: "tnp-discover", + 8321: "tnp", + 8322: "garmin-marine", + 8351: "server-find", + 8376: "cruise-enum", + 8377: "cruise-swroute", + 8378: "cruise-config", + 8379: "cruise-diags", + 8380: "cruise-update", + 8383: "m2mservices", + 8384: "marathontp", + 8400: "cvd", + 8401: "sabarsd", + 8402: "abarsd", + 8403: "admind", + 8416: "espeech", + 8417: "espeech-rtp", + 8442: "cybro-a-bus", + 8443: "pcsync-https", + 8444: "pcsync-http", + 8445: "copy-disc", + 8450: "npmp", + 8472: "otv", + 8473: "vp2p", + 8474: "noteshare", + 8500: "fmtp", + 8501: "cmtp-av", + 8503: "lsp-self-ping", + 8554: "rtsp-alt", + 8555: "d-fence", + 8567: "dof-tunnel", + 8600: "asterix", + 8609: "canon-cpp-disc", + 8610: "canon-mfnp", + 8611: "canon-bjnp1", + 8612: "canon-bjnp2", + 8613: "canon-bjnp3", + 8614: "canon-bjnp4", + 8675: "msi-cps-rm-disc", + 8686: "sun-as-jmxrmi", + 8732: "dtp-net", + 8733: "ibus", + 8763: "mc-appserver", + 8764: "openqueue", + 8765: "ultraseek-http", + 8766: "amcs", + 8770: "dpap", + 8786: "msgclnt", + 8787: "msgsrvr", + 8793: "acd-pm", + 8800: "sunwebadmin", + 8804: "truecm", + 8808: "ssports-bcast", + 8873: "dxspider", + 8880: "cddbp-alt", + 8883: "secure-mqtt", + 8888: "ddi-udp-1", + 8889: "ddi-udp-2", + 8890: "ddi-udp-3", + 8891: "ddi-udp-4", + 8892: "ddi-udp-5", + 8893: "ddi-udp-6", + 8894: "ddi-udp-7", + 8899: "ospf-lite", + 8900: "jmb-cds1", + 8901: "jmb-cds2", + 8910: "manyone-http", + 8911: "manyone-xml", + 8912: "wcbackup", + 8913: "dragonfly", + 8954: "cumulus-admin", + 8980: "nod-provider", + 8981: "nod-client", + 8989: "sunwebadmins", + 8990: "http-wmap", + 8991: "https-wmap", + 8999: "bctp", + 9000: "cslistener", + 9001: "etlservicemgr", + 9002: "dynamid", + 9007: "ogs-client", + 9009: "pichat", + 9020: "tambora", + 9021: "panagolin-ident", + 9022: "paragent", + 9023: "swa-1", + 9024: "swa-2", + 9025: "swa-3", + 9026: "swa-4", + 9060: "CardWeb-RT", + 9080: "glrpc", + 9084: "aurora", + 9085: "ibm-rsyscon", + 9086: "net2display", + 9087: "classic", + 9088: "sqlexec", + 9089: "sqlexec-ssl", + 9090: "websm", + 9091: "xmltec-xmlmail", + 9092: "XmlIpcRegSvc", + 9100: "hp-pdl-datastr", + 9101: "bacula-dir", + 9102: "bacula-fd", + 9103: "bacula-sd", + 9104: "peerwire", + 9105: "xadmin", + 9106: "astergate-disc", + 9119: "mxit", + 9131: "dddp", + 9160: "apani1", + 9161: "apani2", + 9162: "apani3", + 9163: "apani4", + 9164: "apani5", + 9191: "sun-as-jpda", + 9200: "wap-wsp", + 9201: "wap-wsp-wtp", + 9202: "wap-wsp-s", + 9203: "wap-wsp-wtp-s", + 9204: "wap-vcard", + 9205: "wap-vcal", + 9206: "wap-vcard-s", + 9207: "wap-vcal-s", + 9208: "rjcdb-vcards", + 9209: "almobile-system", + 9210: "oma-mlp", + 9211: "oma-mlp-s", + 9212: "serverviewdbms", + 9213: "serverstart", + 9214: "ipdcesgbs", + 9215: "insis", + 9216: "acme", + 9217: "fsc-port", + 9222: "teamcoherence", + 9255: "mon", + 9277: "traingpsdata", + 9278: "pegasus", + 9279: "pegasus-ctl", + 9280: "pgps", + 9281: "swtp-port1", + 9282: "swtp-port2", + 9283: "callwaveiam", + 9284: "visd", + 9285: "n2h2server", + 9286: "n2receive", + 9287: "cumulus", + 9292: "armtechdaemon", + 9293: "storview", + 9294: "armcenterhttp", + 9295: "armcenterhttps", + 9300: "vrace", + 9318: "secure-ts", + 9321: "guibase", + 9343: "mpidcmgr", + 9344: "mphlpdmc", + 9346: "ctechlicensing", + 9374: "fjdmimgr", + 9380: "boxp", + 9396: "fjinvmgr", + 9397: "mpidcagt", + 9400: "sec-t4net-srv", + 9401: "sec-t4net-clt", + 9402: "sec-pc2fax-srv", + 9418: "git", + 9443: "tungsten-https", + 9444: "wso2esb-console", + 9450: "sntlkeyssrvr", + 9500: "ismserver", + 9522: "sma-spw", + 9535: "mngsuite", + 9536: "laes-bf", + 9555: "trispen-sra", + 9592: "ldgateway", + 9593: "cba8", + 9594: "msgsys", + 9595: "pds", + 9596: "mercury-disc", + 9597: "pd-admin", + 9598: "vscp", + 9599: "robix", + 9600: "micromuse-ncpw", + 9612: "streamcomm-ds", + 9618: "condor", + 9628: "odbcpathway", + 9629: "uniport", + 9632: "mc-comm", + 9667: "xmms2", + 9668: "tec5-sdctp", + 9694: "client-wakeup", + 9695: "ccnx", + 9700: "board-roar", + 9747: "l5nas-parchan", + 9750: "board-voip", + 9753: "rasadv", + 9762: "tungsten-http", + 9800: "davsrc", + 9801: "sstp-2", + 9802: "davsrcs", + 9875: "sapv1", + 9878: "kca-service", + 9888: "cyborg-systems", + 9889: "gt-proxy", + 9898: "monkeycom", + 9899: "sctp-tunneling", + 9900: "iua", + 9901: "enrp", + 9903: "multicast-ping", + 9909: "domaintime", + 9911: "sype-transport", + 9950: "apc-9950", + 9951: "apc-9951", + 9952: "apc-9952", + 9953: "acis", + 9955: "alljoyn-mcm", + 9956: "alljoyn", + 9966: "odnsp", + 9987: "dsm-scm-target", + 9990: "osm-appsrvr", + 9991: "osm-oev", + 9992: "palace-1", + 9993: "palace-2", + 9994: "palace-3", + 9995: "palace-4", + 9996: "palace-5", + 9997: "palace-6", + 9998: "distinct32", + 9999: "distinct", + 10000: "ndmp", + 10001: "scp-config", + 10002: "documentum", + 10003: "documentum-s", + 10007: "mvs-capacity", + 10008: "octopus", + 10009: "swdtp-sv", + 10050: "zabbix-agent", + 10051: "zabbix-trapper", + 10080: "amanda", + 10081: "famdc", + 10100: "itap-ddtp", + 10101: "ezmeeting-2", + 10102: "ezproxy-2", + 10103: "ezrelay", + 10104: "swdtp", + 10107: "bctp-server", + 10110: "nmea-0183", + 10111: "nmea-onenet", + 10113: "netiq-endpoint", + 10114: "netiq-qcheck", + 10115: "netiq-endpt", + 10116: "netiq-voipa", + 10117: "iqrm", + 10128: "bmc-perf-sd", + 10160: "qb-db-server", + 10161: "snmpdtls", + 10162: "snmpdtls-trap", + 10200: "trisoap", + 10201: "rscs", + 10252: "apollo-relay", + 10253: "eapol-relay", + 10260: "axis-wimp-port", + 10288: "blocks", + 10439: "bngsync", + 10500: "hip-nat-t", + 10540: "MOS-lower", + 10541: "MOS-upper", + 10542: "MOS-aux", + 10543: "MOS-soap", + 10544: "MOS-soap-opt", + 10800: "gap", + 10805: "lpdg", + 10810: "nmc-disc", + 10860: "helix", + 10880: "bveapi", + 10990: "rmiaux", + 11000: "irisa", + 11001: "metasys", + 10023: "cefd-vmp", + 11095: "weave", + 11106: "sgi-lk", + 11108: "myq-termlink", + 11111: "vce", + 11112: "dicom", + 11161: "suncacao-snmp", + 11162: "suncacao-jmxmp", + 11163: "suncacao-rmi", + 11164: "suncacao-csa", + 11165: "suncacao-websvc", + 11171: "snss", + 11201: "smsqp", + 11208: "wifree", + 11211: "memcache", + 11319: "imip", + 11320: "imip-channels", + 11321: "arena-server", + 11367: "atm-uhas", + 11371: "hkp", + 11430: "lsdp", + 11600: "tempest-port", + 11720: "h323callsigalt", + 11723: "emc-xsw-dcache", + 11751: "intrepid-ssl", + 11796: "lanschool-mpt", + 11876: "xoraya", + 11877: "x2e-disc", + 11967: "sysinfo-sp", + 12000: "entextxid", + 12001: "entextnetwk", + 12002: "entexthigh", + 12003: "entextmed", + 12004: "entextlow", + 12005: "dbisamserver1", + 12006: "dbisamserver2", + 12007: "accuracer", + 12008: "accuracer-dbms", + 12009: "ghvpn", + 12012: "vipera", + 12013: "vipera-ssl", + 12109: "rets-ssl", + 12121: "nupaper-ss", + 12168: "cawas", + 12172: "hivep", + 12300: "linogridengine", + 12321: "warehouse-sss", + 12322: "warehouse", + 12345: "italk", + 12753: "tsaf", + 13160: "i-zipqd", + 13216: "bcslogc", + 13217: "rs-pias", + 13218: "emc-vcas-udp", + 13223: "powwow-client", + 13224: "powwow-server", + 13400: "doip-disc", + 13720: "bprd", + 13721: "bpdbm", + 13722: "bpjava-msvc", + 13724: "vnetd", + 13782: "bpcd", + 13783: "vopied", + 13785: "nbdb", + 13786: "nomdb", + 13818: "dsmcc-config", + 13819: "dsmcc-session", + 13820: "dsmcc-passthru", + 13821: "dsmcc-download", + 13822: "dsmcc-ccp", + 13894: "ucontrol", + 13929: "dta-systems", + 14000: "scotty-ft", + 14001: "sua", + 14002: "scotty-disc", + 14033: "sage-best-com1", + 14034: "sage-best-com2", + 14141: "vcs-app", + 14142: "icpp", + 14145: "gcm-app", + 14149: "vrts-tdd", + 14154: "vad", + 14250: "cps", + 14414: "ca-web-update", + 14936: "hde-lcesrvr-1", + 14937: "hde-lcesrvr-2", + 15000: "hydap", + 15118: "v2g-secc", + 15345: "xpilot", + 15363: "3link", + 15555: "cisco-snat", + 15660: "bex-xr", + 15740: "ptp", + 15998: "2ping", + 16003: "alfin", + 16161: "sun-sea-port", + 16309: "etb4j", + 16310: "pduncs", + 16311: "pdefmns", + 16360: "netserialext1", + 16361: "netserialext2", + 16367: "netserialext3", + 16368: "netserialext4", + 16384: "connected", + 16666: "vtp", + 16900: "newbay-snc-mc", + 16950: "sgcip", + 16991: "intel-rci-mp", + 16992: "amt-soap-http", + 16993: "amt-soap-https", + 16994: "amt-redir-tcp", + 16995: "amt-redir-tls", + 17007: "isode-dua", + 17185: "soundsvirtual", + 17219: "chipper", + 17220: "avtp", + 17221: "avdecc", + 17222: "cpsp", + 17224: "trdp-pd", + 17225: "trdp-md", + 17234: "integrius-stp", + 17235: "ssh-mgmt", + 17500: "db-lsp-disc", + 17729: "ea", + 17754: "zep", + 17755: "zigbee-ip", + 17756: "zigbee-ips", + 18000: "biimenu", + 18181: "opsec-cvp", + 18182: "opsec-ufp", + 18183: "opsec-sam", + 18184: "opsec-lea", + 18185: "opsec-omi", + 18186: "ohsc", + 18187: "opsec-ela", + 18241: "checkpoint-rtm", + 18262: "gv-pf", + 18463: "ac-cluster", + 18634: "rds-ib", + 18635: "rds-ip", + 18668: "vdmmesh-disc", + 18769: "ique", + 18881: "infotos", + 18888: "apc-necmp", + 19000: "igrid", + 19007: "scintilla", + 19191: "opsec-uaa", + 19194: "ua-secureagent", + 19220: "cora-disc", + 19283: "keysrvr", + 19315: "keyshadow", + 19398: "mtrgtrans", + 19410: "hp-sco", + 19411: "hp-sca", + 19412: "hp-sessmon", + 19539: "fxuptp", + 19540: "sxuptp", + 19541: "jcp", + 19788: "mle", + 19999: "dnp-sec", + 20000: "dnp", + 20001: "microsan", + 20002: "commtact-http", + 20003: "commtact-https", + 20005: "openwebnet", + 20012: "ss-idi-disc", + 20014: "opendeploy", + 20034: "nburn-id", + 20046: "tmophl7mts", + 20048: "mountd", + 20049: "nfsrdma", + 20167: "tolfab", + 20202: "ipdtp-port", + 20222: "ipulse-ics", + 20480: "emwavemsg", + 20670: "track", + 20999: "athand-mmp", + 21000: "irtrans", + 21554: "dfserver", + 21590: "vofr-gateway", + 21800: "tvpm", + 21845: "webphone", + 21846: "netspeak-is", + 21847: "netspeak-cs", + 21848: "netspeak-acd", + 21849: "netspeak-cps", + 22000: "snapenetio", + 22001: "optocontrol", + 22002: "optohost002", + 22003: "optohost003", + 22004: "optohost004", + 22005: "optohost004", + 22273: "wnn6", + 22305: "cis", + 22335: "shrewd-stream", + 22343: "cis-secure", + 22347: "wibukey", + 22350: "codemeter", + 22555: "vocaltec-phone", + 22763: "talikaserver", + 22800: "aws-brf", + 22951: "brf-gw", + 23000: "inovaport1", + 23001: "inovaport2", + 23002: "inovaport3", + 23003: "inovaport4", + 23004: "inovaport5", + 23005: "inovaport6", + 23272: "s102", + 23294: "5afe-disc", + 23333: "elxmgmt", + 23400: "novar-dbase", + 23401: "novar-alarm", + 23402: "novar-global", + 24000: "med-ltp", + 24001: "med-fsp-rx", + 24002: "med-fsp-tx", + 24003: "med-supp", + 24004: "med-ovw", + 24005: "med-ci", + 24006: "med-net-svc", + 24242: "filesphere", + 24249: "vista-4gl", + 24321: "ild", + 24322: "hid", + 24386: "intel-rci", + 24465: "tonidods", + 24554: "binkp", + 24577: "bilobit-update", + 24676: "canditv", + 24677: "flashfiler", + 24678: "proactivate", + 24680: "tcc-http", + 24850: "assoc-disc", + 24922: "find", + 25000: "icl-twobase1", + 25001: "icl-twobase2", + 25002: "icl-twobase3", + 25003: "icl-twobase4", + 25004: "icl-twobase5", + 25005: "icl-twobase6", + 25006: "icl-twobase7", + 25007: "icl-twobase8", + 25008: "icl-twobase9", + 25009: "icl-twobase10", + 25793: "vocaltec-hos", + 25900: "tasp-net", + 25901: "niobserver", + 25902: "nilinkanalyst", + 25903: "niprobe", + 25954: "bf-game", + 25955: "bf-master", + 26000: "quake", + 26133: "scscp", + 26208: "wnn6-ds", + 26260: "ezproxy", + 26261: "ezmeeting", + 26262: "k3software-svr", + 26263: "k3software-cli", + 26486: "exoline-udp", + 26487: "exoconfig", + 26489: "exonet", + 27345: "imagepump", + 27442: "jesmsjc", + 27504: "kopek-httphead", + 27782: "ars-vista", + 27999: "tw-auth-key", + 28000: "nxlmd", + 28119: "a27-ran-ran", + 28200: "voxelstorm", + 28240: "siemensgsm", + 29167: "otmp", + 30001: "pago-services1", + 30002: "pago-services2", + 30003: "amicon-fpsu-ra", + 30004: "amicon-fpsu-s", + 30260: "kingdomsonline", + 30832: "samsung-disc", + 30999: "ovobs", + 31016: "ka-kdp", + 31029: "yawn", + 31416: "xqosd", + 31457: "tetrinet", + 31620: "lm-mon", + 31765: "gamesmith-port", + 31948: "iceedcp-tx", + 31949: "iceedcp-rx", + 32034: "iracinghelper", + 32249: "t1distproc60", + 32483: "apm-link", + 32635: "sec-ntb-clnt", + 32636: "DMExpress", + 32767: "filenet-powsrm", + 32768: "filenet-tms", + 32769: "filenet-rpc", + 32770: "filenet-nch", + 32771: "filenet-rmi", + 32772: "filenet-pa", + 32773: "filenet-cm", + 32774: "filenet-re", + 32775: "filenet-pch", + 32776: "filenet-peior", + 32777: "filenet-obrok", + 32801: "mlsn", + 32896: "idmgratm", + 33123: "aurora-balaena", + 33331: "diamondport", + 33334: "speedtrace-disc", + 33434: "traceroute", + 33656: "snip-slave", + 34249: "turbonote-2", + 34378: "p-net-local", + 34379: "p-net-remote", + 34567: "edi_service", + 34962: "profinet-rt", + 34963: "profinet-rtm", + 34964: "profinet-cm", + 34980: "ethercat", + 35001: "rt-viewer", + 35004: "rt-classmanager", + 35100: "axio-disc", + 35355: "altova-lm-disc", + 36001: "allpeers", + 36411: "wlcp", + 36865: "kastenxpipe", + 37475: "neckar", + 37654: "unisys-eportal", + 38002: "crescoctrl-disc", + 38201: "galaxy7-data", + 38202: "fairview", + 38203: "agpolicy", + 39681: "turbonote-1", + 40000: "safetynetp", + 40023: "k-patentssensor", + 40841: "cscp", + 40842: "csccredir", + 40843: "csccfirewall", + 40853: "ortec-disc", + 41111: "fs-qos", + 41230: "z-wave-s", + 41794: "crestron-cip", + 41795: "crestron-ctp", + 42508: "candp", + 42509: "candrp", + 42510: "caerpc", + 43000: "recvr-rc-disc", + 43188: "reachout", + 43189: "ndm-agent-port", + 43190: "ip-provision", + 43210: "shaperai-disc", + 43439: "eq3-config", + 43440: "ew-disc-cmd", + 43441: "ciscocsdb", + 44321: "pmcd", + 44322: "pmcdproxy", + 44544: "domiq", + 44553: "rbr-debug", + 44600: "asihpi", + 44818: "EtherNet-IP-2", + 44900: "m3da-disc", + 45000: "asmp-mon", + 45054: "invision-ag", + 45514: "cloudcheck-ping", + 45678: "eba", + 45825: "qdb2service", + 45966: "ssr-servermgr", + 46999: "mediabox", + 47000: "mbus", + 47100: "jvl-mactalk", + 47557: "dbbrowse", + 47624: "directplaysrvr", + 47806: "ap", + 47808: "bacnet", + 47809: "presonus-ucnet", + 48000: "nimcontroller", + 48001: "nimspooler", + 48002: "nimhub", + 48003: "nimgtw", + 48128: "isnetserv", + 48129: "blp5", + 48556: "com-bardac-dw", + 48619: "iqobject", + 48653: "robotraconteur", +} +var sctpPortNames = map[SCTPPort]string{ + 9: "discard", + 20: "ftp-data", + 21: "ftp", + 22: "ssh", + 80: "http", + 179: "bgp", + 443: "https", + 1021: "exp1", + 1022: "exp2", + 1167: "cisco-ipsla", + 1720: "h323hostcall", + 2049: "nfs", + 2225: "rcip-itu", + 2904: "m2ua", + 2905: "m3ua", + 2944: "megaco-h248", + 2945: "h248-binary", + 3097: "itu-bicc-stc", + 3565: "m2pa", + 3863: "asap-sctp", + 3864: "asap-sctp-tls", + 3868: "diameter", + 4333: "ahsp", + 4502: "a25-fap-fgw", + 4739: "ipfix", + 4740: "ipfixs", + 5060: "sip", + 5061: "sips", + 5090: "car", + 5091: "cxtp", + 5215: "noteza", + 5445: "smbdirect", + 5672: "amqp", + 5675: "v5ua", + 5868: "diameters", + 5910: "cm", + 5911: "cpdlc", + 5912: "fis", + 5913: "ads-c", + 6704: "frc-hp", + 6705: "frc-mp", + 6706: "frc-lp", + 6970: "conductor-mpx", + 7626: "simco", + 7728: "osvr", + 8471: "pim-port", + 9082: "lcs-ap", + 9084: "aurora", + 9900: "iua", + 9901: "enrp-sctp", + 9902: "enrp-sctp-tls", + 11997: "wmereceiving", + 11998: "wmedistribution", + 11999: "wmereporting", + 14001: "sua", + 20049: "nfsrdma", + 25471: "rna", + 29118: "sgsap", + 29168: "sbcap", + 29169: "iuhsctpassoc", + 30100: "rwp", + 36412: "s1-control", + 36422: "x2-control", + 36423: "slmap", + 36424: "nq-ap", + 36443: "m2ap", + 36444: "m3ap", + 36462: "xw-control", +} diff --git a/vendor/github.com/google/gopacket/layers/icmp4.go b/vendor/github.com/google/gopacket/layers/icmp4.go new file mode 100644 index 00000000..bd3f03f0 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/icmp4.go @@ -0,0 +1,267 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "reflect" + + "github.com/google/gopacket" +) + +const ( + ICMPv4TypeEchoReply = 0 + ICMPv4TypeDestinationUnreachable = 3 + ICMPv4TypeSourceQuench = 4 + ICMPv4TypeRedirect = 5 + ICMPv4TypeEchoRequest = 8 + ICMPv4TypeRouterAdvertisement = 9 + ICMPv4TypeRouterSolicitation = 10 + ICMPv4TypeTimeExceeded = 11 + ICMPv4TypeParameterProblem = 12 + ICMPv4TypeTimestampRequest = 13 + ICMPv4TypeTimestampReply = 14 + ICMPv4TypeInfoRequest = 15 + ICMPv4TypeInfoReply = 16 + ICMPv4TypeAddressMaskRequest = 17 + ICMPv4TypeAddressMaskReply = 18 +) + +const ( + // DestinationUnreachable + ICMPv4CodeNet = 0 + ICMPv4CodeHost = 1 + ICMPv4CodeProtocol = 2 + ICMPv4CodePort = 3 + ICMPv4CodeFragmentationNeeded = 4 + ICMPv4CodeSourceRoutingFailed = 5 + ICMPv4CodeNetUnknown = 6 + ICMPv4CodeHostUnknown = 7 + ICMPv4CodeSourceIsolated = 8 + ICMPv4CodeNetAdminProhibited = 9 + ICMPv4CodeHostAdminProhibited = 10 + ICMPv4CodeNetTOS = 11 + ICMPv4CodeHostTOS = 12 + ICMPv4CodeCommAdminProhibited = 13 + ICMPv4CodeHostPrecedence = 14 + ICMPv4CodePrecedenceCutoff = 15 + + // TimeExceeded + ICMPv4CodeTTLExceeded = 0 + ICMPv4CodeFragmentReassemblyTimeExceeded = 1 + + // ParameterProblem + ICMPv4CodePointerIndicatesError = 0 + ICMPv4CodeMissingOption = 1 + ICMPv4CodeBadLength = 2 + + // Redirect + // ICMPv4CodeNet = same as for DestinationUnreachable + // ICMPv4CodeHost = same as for DestinationUnreachable + ICMPv4CodeTOSNet = 2 + ICMPv4CodeTOSHost = 3 +) + +type icmpv4TypeCodeInfoStruct struct { + typeStr string + codeStr *map[uint8]string +} + +var ( + icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{ + ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{ + "DestinationUnreachable", &map[uint8]string{ + ICMPv4CodeNet: "Net", + ICMPv4CodeHost: "Host", + ICMPv4CodeProtocol: "Protocol", + ICMPv4CodePort: "Port", + ICMPv4CodeFragmentationNeeded: "FragmentationNeeded", + ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed", + ICMPv4CodeNetUnknown: "NetUnknown", + ICMPv4CodeHostUnknown: "HostUnknown", + ICMPv4CodeSourceIsolated: "SourceIsolated", + ICMPv4CodeNetAdminProhibited: "NetAdminProhibited", + ICMPv4CodeHostAdminProhibited: "HostAdminProhibited", + ICMPv4CodeNetTOS: "NetTOS", + ICMPv4CodeHostTOS: "HostTOS", + ICMPv4CodeCommAdminProhibited: "CommAdminProhibited", + ICMPv4CodeHostPrecedence: "HostPrecedence", + ICMPv4CodePrecedenceCutoff: "PrecedenceCutoff", + }, + }, + ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{ + "TimeExceeded", &map[uint8]string{ + ICMPv4CodeTTLExceeded: "TTLExceeded", + ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded", + }, + }, + ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{ + "ParameterProblem", &map[uint8]string{ + ICMPv4CodePointerIndicatesError: "PointerIndicatesError", + ICMPv4CodeMissingOption: "MissingOption", + ICMPv4CodeBadLength: "BadLength", + }, + }, + ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{ + "SourceQuench", nil, + }, + ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{ + "Redirect", &map[uint8]string{ + ICMPv4CodeNet: "Net", + ICMPv4CodeHost: "Host", + ICMPv4CodeTOSNet: "TOS+Net", + ICMPv4CodeTOSHost: "TOS+Host", + }, + }, + ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{ + "EchoRequest", nil, + }, + ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{ + "EchoReply", nil, + }, + ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{ + "TimestampRequest", nil, + }, + ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{ + "TimestampReply", nil, + }, + ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{ + "InfoRequest", nil, + }, + ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{ + "InfoReply", nil, + }, + ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{ + "RouterSolicitation", nil, + }, + ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{ + "RouterAdvertisement", nil, + }, + ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{ + "AddressMaskRequest", nil, + }, + ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{ + "AddressMaskReply", nil, + }, + } +) + +type ICMPv4TypeCode uint16 + +// Type returns the ICMPv4 type field. +func (a ICMPv4TypeCode) Type() uint8 { + return uint8(a >> 8) +} + +// Code returns the ICMPv4 code field. +func (a ICMPv4TypeCode) Code() uint8 { + return uint8(a) +} + +func (a ICMPv4TypeCode) String() string { + t, c := a.Type(), a.Code() + strInfo, ok := icmpv4TypeCodeInfo[t] + if !ok { + // Unknown ICMPv4 type field + return fmt.Sprintf("%d(%d)", t, c) + } + typeStr := strInfo.typeStr + if strInfo.codeStr == nil && c == 0 { + // The ICMPv4 type does not make use of the code field + return fmt.Sprintf("%s", strInfo.typeStr) + } + if strInfo.codeStr == nil && c != 0 { + // The ICMPv4 type does not make use of the code field, but it is present anyway + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + codeStr, ok := (*strInfo.codeStr)[c] + if !ok { + // We don't know this ICMPv4 code; print the numerical value + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + return fmt.Sprintf("%s(%s)", typeStr, codeStr) +} + +func (a ICMPv4TypeCode) GoString() string { + t := reflect.TypeOf(a) + return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code()) +} + +// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer. +func (a ICMPv4TypeCode) SerializeTo(bytes []byte) { + binary.BigEndian.PutUint16(bytes, uint16(a)) +} + +// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode +// gopacket type from the ICMPv4 type and code values. +func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode { + return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code})) +} + +// ICMPv4 is the layer for IPv4 ICMP packet data. +type ICMPv4 struct { + BaseLayer + TypeCode ICMPv4TypeCode + Checksum uint16 + Id uint16 + Seq uint16 +} + +// LayerType returns LayerTypeICMPv4. +func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 } + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet") + } + i.TypeCode = CreateICMPv4TypeCode(data[0], data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.Id = binary.BigEndian.Uint16(data[4:6]) + i.Seq = binary.BigEndian.Uint16(data[6:8]) + i.BaseLayer = BaseLayer{data[:8], data[8:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + i.TypeCode.SerializeTo(bytes) + binary.BigEndian.PutUint16(bytes[4:], i.Id) + binary.BigEndian.PutUint16(bytes[6:], i.Seq) + if opts.ComputeChecksums { + bytes[2] = 0 + bytes[3] = 0 + i.Checksum = tcpipChecksum(b.Bytes(), 0) + } + binary.BigEndian.PutUint16(bytes[2:], i.Checksum) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv4) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv4 +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv4) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv4{} + return decodingLayerDecoder(i, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/icmp6.go b/vendor/github.com/google/gopacket/layers/icmp6.go new file mode 100644 index 00000000..03a32131 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/icmp6.go @@ -0,0 +1,231 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "reflect" + + "github.com/google/gopacket" +) + +const ( + // The following are from RFC 4443 + ICMPv6TypeDestinationUnreachable = 1 + ICMPv6TypePacketTooBig = 2 + ICMPv6TypeTimeExceeded = 3 + ICMPv6TypeParameterProblem = 4 + ICMPv6TypeEchoRequest = 128 + ICMPv6TypeEchoReply = 129 + // The following are from RFC 4861 + ICMPv6TypeRouterSolicitation = 133 + ICMPv6TypeRouterAdvertisement = 134 + ICMPv6TypeNeighborSolicitation = 135 + ICMPv6TypeNeighborAdvertisement = 136 + ICMPv6TypeRedirect = 137 +) + +const ( + // DestinationUnreachable + ICMPv6CodeNoRouteToDst = 0 + ICMPv6CodeAdminProhibited = 1 + ICMPv6CodeBeyondScopeOfSrc = 2 + ICMPv6CodeAddressUnreachable = 3 + ICMPv6CodePortUnreachable = 4 + ICMPv6CodeSrcAddressFailedPolicy = 5 + ICMPv6CodeRejectRouteToDst = 6 + + // TimeExceeded + ICMPv6CodeHopLimitExceeded = 0 + ICMPv6CodeFragmentReassemblyTimeExceeded = 1 + + // ParameterProblem + ICMPv6CodeErroneousHeaderField = 0 + ICMPv6CodeUnrecognizedNextHeader = 1 + ICMPv6CodeUnrecognizedIPv6Option = 2 +) + +type icmpv6TypeCodeInfoStruct struct { + typeStr string + codeStr *map[uint8]string +} + +var ( + icmpv6TypeCodeInfo = map[uint8]icmpv6TypeCodeInfoStruct{ + ICMPv6TypeDestinationUnreachable: icmpv6TypeCodeInfoStruct{ + "DestinationUnreachable", &map[uint8]string{ + ICMPv6CodeNoRouteToDst: "NoRouteToDst", + ICMPv6CodeAdminProhibited: "AdminProhibited", + ICMPv6CodeBeyondScopeOfSrc: "BeyondScopeOfSrc", + ICMPv6CodeAddressUnreachable: "AddressUnreachable", + ICMPv6CodePortUnreachable: "PortUnreachable", + ICMPv6CodeSrcAddressFailedPolicy: "SrcAddressFailedPolicy", + ICMPv6CodeRejectRouteToDst: "RejectRouteToDst", + }, + }, + ICMPv6TypePacketTooBig: icmpv6TypeCodeInfoStruct{ + "PacketTooBig", nil, + }, + ICMPv6TypeTimeExceeded: icmpv6TypeCodeInfoStruct{ + "TimeExceeded", &map[uint8]string{ + ICMPv6CodeHopLimitExceeded: "HopLimitExceeded", + ICMPv6CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded", + }, + }, + ICMPv6TypeParameterProblem: icmpv6TypeCodeInfoStruct{ + "ParameterProblem", &map[uint8]string{ + ICMPv6CodeErroneousHeaderField: "ErroneousHeaderField", + ICMPv6CodeUnrecognizedNextHeader: "UnrecognizedNextHeader", + ICMPv6CodeUnrecognizedIPv6Option: "UnrecognizedIPv6Option", + }, + }, + ICMPv6TypeEchoRequest: icmpv6TypeCodeInfoStruct{ + "EchoRequest", nil, + }, + ICMPv6TypeEchoReply: icmpv6TypeCodeInfoStruct{ + "EchoReply", nil, + }, + ICMPv6TypeRouterSolicitation: icmpv6TypeCodeInfoStruct{ + "RouterSolicitation", nil, + }, + ICMPv6TypeRouterAdvertisement: icmpv6TypeCodeInfoStruct{ + "RouterAdvertisement", nil, + }, + ICMPv6TypeNeighborSolicitation: icmpv6TypeCodeInfoStruct{ + "NeighborSolicitation", nil, + }, + ICMPv6TypeNeighborAdvertisement: icmpv6TypeCodeInfoStruct{ + "NeighborAdvertisement", nil, + }, + ICMPv6TypeRedirect: icmpv6TypeCodeInfoStruct{ + "Redirect", nil, + }, + } +) + +type ICMPv6TypeCode uint16 + +// Type returns the ICMPv6 type field. +func (a ICMPv6TypeCode) Type() uint8 { + return uint8(a >> 8) +} + +// Code returns the ICMPv6 code field. +func (a ICMPv6TypeCode) Code() uint8 { + return uint8(a) +} + +func (a ICMPv6TypeCode) String() string { + t, c := a.Type(), a.Code() + strInfo, ok := icmpv6TypeCodeInfo[t] + if !ok { + // Unknown ICMPv6 type field + return fmt.Sprintf("%d(%d)", t, c) + } + typeStr := strInfo.typeStr + if strInfo.codeStr == nil && c == 0 { + // The ICMPv6 type does not make use of the code field + return fmt.Sprintf("%s", strInfo.typeStr) + } + if strInfo.codeStr == nil && c != 0 { + // The ICMPv6 type does not make use of the code field, but it is present anyway + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + codeStr, ok := (*strInfo.codeStr)[c] + if !ok { + // We don't know this ICMPv6 code; print the numerical value + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + return fmt.Sprintf("%s(%s)", typeStr, codeStr) +} + +func (a ICMPv6TypeCode) GoString() string { + t := reflect.TypeOf(a) + return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code()) +} + +// SerializeTo writes the ICMPv6TypeCode value to the 'bytes' buffer. +func (a ICMPv6TypeCode) SerializeTo(bytes []byte) { + binary.BigEndian.PutUint16(bytes, uint16(a)) +} + +// CreateICMPv6TypeCode is a convenience function to create an ICMPv6TypeCode +// gopacket type from the ICMPv6 type and code values. +func CreateICMPv6TypeCode(typ uint8, code uint8) ICMPv6TypeCode { + return ICMPv6TypeCode(binary.BigEndian.Uint16([]byte{typ, code})) +} + +// ICMPv6 is the layer for IPv6 ICMP packet data +type ICMPv6 struct { + BaseLayer + TypeCode ICMPv6TypeCode + Checksum uint16 + TypeBytes []byte + tcpipchecksum +} + +// LayerType returns LayerTypeICMPv6. +func (i *ICMPv6) LayerType() gopacket.LayerType { return LayerTypeICMPv6 } + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return errors.New("ICMP layer less then 8 bytes for ICMPv6 packet") + } + i.TypeCode = CreateICMPv6TypeCode(data[0], data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.TypeBytes = data[4:8] + i.BaseLayer = BaseLayer{data[:8], data[8:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if i.TypeBytes == nil { + i.TypeBytes = lotsOfZeros[:4] + } else if len(i.TypeBytes) != 4 { + return fmt.Errorf("invalid type bytes for ICMPv6 packet: %v", i.TypeBytes) + } + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + i.TypeCode.SerializeTo(bytes) + copy(bytes[4:8], i.TypeBytes) + if opts.ComputeChecksums { + bytes[2] = 0 + bytes[3] = 0 + csum, err := i.computeChecksum(b.Bytes(), IPProtocolICMPv6) + if err != nil { + return err + } + i.Checksum = csum + } + binary.BigEndian.PutUint16(bytes[2:], i.Checksum) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6 +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6{} + return decodingLayerDecoder(i, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/icmp6_test.go b/vendor/github.com/google/gopacket/layers/icmp6_test.go new file mode 100644 index 00000000..4a6d248f --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/icmp6_test.go @@ -0,0 +1,84 @@ +// Copyright 2012, Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" + "net" + "reflect" + "testing" +) + +// testPacketICMPv6 is the packet: +// 10:48:30.088384 IP6 2620:0:1005:0:26be:5ff:fe27:b17 > fe80::21f:caff:feb3:7640: ICMP6, neighbor advertisement, tgt is 2620:0:1005:0:26be:5ff:fe27:b17, length 24 +// 0x0000: 001f cab3 7640 24be 0527 0b17 86dd 6000 ....v@$..'....`. +// 0x0010: 0000 0018 3aff 2620 0000 1005 0000 26be ....:.&.......&. +// 0x0020: 05ff fe27 0b17 fe80 0000 0000 0000 021f ...'............ +// 0x0030: caff feb3 7640 8800 1ed6 4000 0000 2620 ....v@....@...&. +// 0x0040: 0000 1005 0000 26be 05ff fe27 0b17 ......&....'.. +var testPacketICMPv6 = []byte{ + 0x00, 0x1f, 0xca, 0xb3, 0x76, 0x40, 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x86, 0xdd, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3a, 0xff, 0x26, 0x20, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x26, 0xbe, + 0x05, 0xff, 0xfe, 0x27, 0x0b, 0x17, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1f, + 0xca, 0xff, 0xfe, 0xb3, 0x76, 0x40, 0x88, 0x00, 0x1e, 0xd6, 0x40, 0x00, 0x00, 0x00, 0x26, 0x20, + 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x26, 0xbe, 0x05, 0xff, 0xfe, 0x27, 0x0b, 0x17, +} + +func TestPacketICMPv6(t *testing.T) { + p := gopacket.NewPacket(testPacketICMPv6, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv6, LayerTypeICMPv6, gopacket.LayerTypePayload}, t) + if got, ok := p.Layer(LayerTypeIPv6).(*IPv6); ok { + want := &IPv6{ + BaseLayer: BaseLayer{ + Contents: []byte{0x60, 0x0, 0x0, 0x0, 0x0, 0x18, + 0x3a, 0xff, 0x26, 0x20, 0x0, 0x0, 0x10, 0x5, 0x0, 0x0, 0x26, 0xbe, 0x5, + 0xff, 0xfe, 0x27, 0xb, 0x17, 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2, 0x1f, 0xca, 0xff, 0xfe, 0xb3, 0x76, 0x40}, + Payload: []byte{0x88, 0x0, 0x1e, 0xd6, 0x40, 0x0, 0x0, 0x0, 0x26, 0x20, + 0x0, 0x0, 0x10, 0x5, 0x0, 0x0, 0x26, 0xbe, 0x5, 0xff, 0xfe, 0x27, 0xb, + 0x17}, + }, + Version: 6, + TrafficClass: 0, + FlowLabel: 0, + Length: 24, + NextHeader: IPProtocolICMPv6, + HopLimit: 255, + SrcIP: net.IP{0x26, 0x20, 0x0, 0x0, 0x10, 0x5, 0x0, 0x0, 0x26, 0xbe, 0x5, 0xff, 0xfe, 0x27, 0xb, 0x17}, + DstIP: net.IP{0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1f, 0xca, 0xff, 0xfe, 0xb3, 0x76, 0x40}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("IPv6 packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } else { + t.Error("No IPv6 layer type found in packet") + } + if got, ok := p.Layer(LayerTypeICMPv6).(*ICMPv6); ok { + want := &ICMPv6{ + BaseLayer: BaseLayer{ + Contents: []byte{0x88, 0x0, 0x1e, 0xd6, 0x40, 0x0, 0x0, 0x0}, + Payload: []byte{0x26, 0x20, 0x0, 0x0, 0x10, + 0x5, 0x0, 0x0, 0x26, 0xbe, 0x5, 0xff, 0xfe, 0x27, 0xb, 0x17}, + }, + TypeCode: 0x8800, + Checksum: 0x1ed6, + TypeBytes: []byte{0x40, 0x0, 0x0, 0x0}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("ICMPv6 packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + if got.TypeCode.String() != "NeighborAdvertisement" { + t.Errorf("ICMPv6 type code, got %q want 'NeighborAdvertisement'", got.TypeCode.String()) + } + } else { + t.Error("No ICMPv6 layer type found in packet") + } +} diff --git a/vendor/github.com/google/gopacket/layers/igmp.go b/vendor/github.com/google/gopacket/layers/igmp.go new file mode 100644 index 00000000..d0084153 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/igmp.go @@ -0,0 +1,355 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "net" + "time" + + "github.com/google/gopacket" +) + +type IGMPType uint8 + +const ( + IGMPMembershipQuery IGMPType = 0x11 // General or group specific query + IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report + IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report + IGMPLeaveGroup IGMPType = 0x17 // Leave Group + IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report +) + +// String conversions for IGMP message types +func (i IGMPType) String() string { + switch i { + case IGMPMembershipQuery: + return "IGMP Membership Query" + case IGMPMembershipReportV1: + return "IGMPv1 Membership Report" + case IGMPMembershipReportV2: + return "IGMPv2 Membership Report" + case IGMPMembershipReportV3: + return "IGMPv3 Membership Report" + case IGMPLeaveGroup: + return "Leave Group" + default: + return "" + } +} + +type IGMPv3GroupRecordType uint8 + +const ( + IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x + IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x + IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x + IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x + IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x + IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x +) + +func (i IGMPv3GroupRecordType) String() string { + switch i { + case IGMPIsIn: + return "MODE_IS_INCLUDE" + case IGMPIsEx: + return "MODE_IS_EXCLUDE" + case IGMPToIn: + return "CHANGE_TO_INCLUDE_MODE" + case IGMPToEx: + return "CHANGE_TO_EXCLUDE_MODE" + case IGMPAllow: + return "ALLOW_NEW_SOURCES" + case IGMPBlock: + return "BLOCK_OLD_SOURCES" + default: + return "" + } +} + +// IGMP represents an IGMPv3 message. +type IGMP struct { + BaseLayer + Type IGMPType + MaxResponseTime time.Duration + Checksum uint16 + GroupAddress net.IP + SupressRouterProcessing bool + RobustnessValue uint8 + IntervalTime time.Duration + SourceAddresses []net.IP + NumberOfGroupRecords uint16 + NumberOfSources uint16 + GroupRecords []IGMPv3GroupRecord + Version uint8 // IGMP protocol version +} + +// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet. +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type | Max Resp Time | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type IGMPv1or2 struct { + BaseLayer + Type IGMPType // IGMP message type + MaxResponseTime time.Duration // meaningful only in Membership Query messages + Checksum uint16 // 16-bit checksum of entire ip payload + GroupAddress net.IP // either 0 or an IP multicast address + Version uint8 +} + +// decodeResponse dissects IGMPv1 or IGMPv2 packet. +func (i *IGMPv1or2) decodeResponse(data []byte) error { + if len(data) < 8 { + return errors.New("IGMP packet too small") + } + + i.MaxResponseTime = igmpTimeDecode(data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.GroupAddress = net.IP(data[4:8]) + + return nil +} + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x22 | Reserved | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Reserved | Number of Group Records (M) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Group Record [1] . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Group Record [2] . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Group Record [M] . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Record Type | Aux Data Len | Number of Sources (N) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Multicast Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Source Address [1] | +// +- -+ +// | Source Address [2] | +// +- -+ +// | Source Address [N] | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Auxiliary Data . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message. +type IGMPv3GroupRecord struct { + Type IGMPv3GroupRecordType + AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec. + NumberOfSources uint16 + MulticastAddress net.IP + SourceAddresses []net.IP + AuxData uint32 // NOT USED +} + +func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error { + if len(data) < 8 { + return errors.New("IGMPv3 Membership Report too small #1") + } + + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8]) + + recordOffset := 8 + for j := 0; j < int(i.NumberOfGroupRecords); j++ { + if len(data) < recordOffset+8 { + return errors.New("IGMPv3 Membership Report too small #2") + } + + var gr IGMPv3GroupRecord + gr.Type = IGMPv3GroupRecordType(data[recordOffset]) + gr.AuxDataLen = data[recordOffset+1] + gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4]) + gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8]) + + if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 { + return errors.New("IGMPv3 Membership Report too small #3") + } + + // append source address records. + for i := 0; i < int(gr.NumberOfSources); i++ { + sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4]) + gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr) + } + + i.GroupRecords = append(i.GroupRecords, gr) + recordOffset += 8 + 4*int(gr.NumberOfSources) + } + return nil +} + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x11 | Max Resp Code | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Resv |S| QRV | QQIC | Number of Sources (N) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Source Address [1] | +// +- -+ +// | Source Address [2] | +// +- . -+ +// | Source Address [N] | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11 +func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error { + if len(data) < 12 { + return errors.New("IGMPv3 Membership Query too small #1") + } + + i.MaxResponseTime = igmpTimeDecode(data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.SupressRouterProcessing = data[8]&0x8 != 0 + i.GroupAddress = net.IP(data[4:8]) + i.RobustnessValue = data[8] & 0x7 + i.IntervalTime = igmpTimeDecode(data[9]) + i.NumberOfSources = binary.BigEndian.Uint16(data[10:12]) + + if len(data) < 12+int(i.NumberOfSources)*4 { + return errors.New("IGMPv3 Membership Query too small #2") + } + + for j := 0; j < int(i.NumberOfSources); j++ { + i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4])) + } + + return nil +} + +// igmpTimeDecode decodes the duration created by the given byte, using the +// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1. +func igmpTimeDecode(t uint8) time.Duration { + if t&0x80 == 0 { + return time.Millisecond * 100 * time.Duration(t) + } + mant := (t & 0x70) >> 4 + exp := t & 0x0F + return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3)) +} + +// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats. +func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP } +func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP } + +func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + return errors.New("IGMP Packet too small") + } + + i.Type = IGMPType(data[0]) + i.MaxResponseTime = igmpTimeDecode(data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.GroupAddress = net.IP(data[4:8]) + + return nil +} + +func (i *IGMPv1or2) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +func (i *IGMPv1or2) CanDecode() gopacket.LayerClass { + return LayerTypeIGMP +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 1 { + return errors.New("IGMP packet is too small") + } + + // common IGMP header values between versions 1..3 of IGMP specification.. + i.Type = IGMPType(data[0]) + + switch i.Type { + case IGMPMembershipQuery: + i.decodeIGMPv3MembershipQuery(data) + case IGMPMembershipReportV3: + i.decodeIGMPv3MembershipReport(data) + default: + return errors.New("unsupported IGMP type") + } + + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *IGMP) CanDecode() gopacket.LayerClass { + return LayerTypeIGMP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *IGMP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the +// IGMP type are performed against byte[0], logic then iniitalizes and +// passes the appropriate struct (IGMP or IGMPv1or2) to +// decodingLayerDecoder. +func decodeIGMP(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 1 { + return errors.New("IGMP packet is too small") + } + + // byte 0 contains IGMP message type. + switch IGMPType(data[0]) { + case IGMPMembershipQuery: + // IGMPv3 Membership Query payload is >= 12 + if len(data) >= 12 { + i := &IGMP{Version: 3} + return decodingLayerDecoder(i, data, p) + } else if len(data) == 8 { + i := &IGMPv1or2{} + if data[1] == 0x00 { + i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0 + } else { + i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0 + } + + return decodingLayerDecoder(i, data, p) + } + case IGMPMembershipReportV3: + i := &IGMP{Version: 3} + return decodingLayerDecoder(i, data, p) + case IGMPMembershipReportV1: + i := &IGMPv1or2{Version: 1} + return decodingLayerDecoder(i, data, p) + case IGMPLeaveGroup, IGMPMembershipReportV2: + // leave group and Query Report v2 used in IGMPv2 only. + i := &IGMPv1or2{Version: 2} + return decodingLayerDecoder(i, data, p) + default: + } + + return errors.New("Unable to determine IGMP type.") +} diff --git a/vendor/github.com/google/gopacket/layers/igmp_test.go b/vendor/github.com/google/gopacket/layers/igmp_test.go new file mode 100644 index 00000000..923356da --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/igmp_test.go @@ -0,0 +1,171 @@ +// Copyright 2016, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "testing" + + "github.com/google/gopacket" +) + +// igmpv1MembershipReportPacket is the packet: +// 02:45:36.033916 IP 10.60.0.132 > 224.0.1.60: igmp v1 report 224.0.1.60 +// 0x0000: 0100 5e00 013c 0030 c1bf 5755 0800 4500 ..^..<.0..WU..E. +// 0x0010: 001c 6a7f 0000 0102 6365 0a3c 0084 e000 ..j.....ce.<.... +// 0x0020: 013c 1200 0cc3 e000 013c 0000 0000 0000 .<.......<...... +// 0x0030: ffff ffff ffff 0452 0000 0000 .......R.... +var igmpv1MembershipReportPacket = []byte{ + 0x01, 0x00, 0x5e, 0x00, 0x01, 0x3c, 0x00, 0x30, 0xc1, 0xbf, 0x57, 0x55, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0x6a, 0x7f, 0x00, 0x00, 0x01, 0x02, 0x63, 0x65, 0x0a, 0x3c, 0x00, 0x84, 0xe0, 0x00, + 0x01, 0x3c, 0x12, 0x00, 0x0c, 0xc3, 0xe0, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, +} + +func TestIGMPv1MembershipReportPacket(t *testing.T) { + p := gopacket.NewPacket(igmpv1MembershipReportPacket, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIGMP}, t) + + igmp := p.Layer(LayerTypeIGMP).(*IGMPv1or2) + if igmp.Type != IGMPMembershipReportV1 { + t.Fatal("Invalid IGMP type") + } +} + +func BenchmarkDecodeigmpv1MembershipReportPacket(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(igmpv1MembershipReportPacket, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// igmpv2MembershipQueryPacket is the packet: +// 02:45:28.071636 IP 10.60.0.189 > 224.0.0.1: igmp query v2 +// 0x0000: 0100 5e00 0001 0001 636f c800 0800 45c0 ..^.....co....E. +// 0x0010: 001c 0153 0000 0102 ccd3 0a3c 00bd e000 ...S.......<.... +// 0x0020: 0001 1164 ee9b 0000 0000 0000 0000 0000 ...d............ +// 0x0030: 0000 0000 0000 0000 0000 0000 ............ +var igmpv2MembershipQueryPacket = []byte{ + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x01, 0x63, 0x6f, 0xc8, 0x00, 0x08, 0x00, 0x45, 0xc0, + 0x00, 0x1c, 0x01, 0x53, 0x00, 0x00, 0x01, 0x02, 0xcc, 0xd3, 0x0a, 0x3c, 0x00, 0xbd, 0xe0, 0x00, + 0x00, 0x01, 0x11, 0x64, 0xee, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestIGMPv2MembershipQuery(t *testing.T) { + p := gopacket.NewPacket(igmpv2MembershipQueryPacket, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIGMP}, t) + + igmp := p.Layer(LayerTypeIGMP).(*IGMPv1or2) + if igmp.Type != IGMPMembershipQuery { + t.Fatal("Invalid IGMP type") + } +} +func BenchmarkDecodeigmpv2MembershipQueryPacket(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(igmpv2MembershipQueryPacket, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// igmpv2MembershipReportPacket is the packet: +// 02:47:32.417288 IP 10.60.5.103 > 239.255.255.253: igmp v2 report 239.255.255.253 +// 0x0000: 0100 5e7f fffd 0015 58dc d9f6 0800 4600 ..^.....X.....F. +// 0x0010: 0020 79f0 0000 0102 ab47 0a3c 0567 efff ..y......G.<.g.. +// 0x0020: fffd 9404 0000 1600 fa01 efff fffd 0000 ................ +// 0x0030: 0000 0000 0000 0000 0000 0000 ............ +var igmpv2MembershipReportPacket = []byte{ + 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfd, 0x00, 0x15, 0x58, 0xdc, 0xd9, 0xf6, 0x08, 0x00, 0x46, 0x00, + 0x00, 0x20, 0x79, 0xf0, 0x00, 0x00, 0x01, 0x02, 0xab, 0x47, 0x0a, 0x3c, 0x05, 0x67, 0xef, 0xff, + 0xff, 0xfd, 0x94, 0x04, 0x00, 0x00, 0x16, 0x00, 0xfa, 0x01, 0xef, 0xff, 0xff, 0xfd, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestIGMPv2MembershipReport(t *testing.T) { + p := gopacket.NewPacket(igmpv2MembershipReportPacket, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIGMP}, t) + + igmp := p.Layer(LayerTypeIGMP).(*IGMPv1or2) + if igmp.Type != IGMPMembershipReportV2 { + t.Fatal("Invalid IGMP type") + } +} +func BenchmarkDecodeigmpv2MembershipReportPacket(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(igmpv2MembershipReportPacket, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// igmp3v3MembershipQueryPacket is the packet: +// 10:07:30.488511 IP 192.168.1.254 > 224.0.0.1: igmp query v3 [max resp time 2.4s] +// 0x0000: 0100 5e00 0001 0026 446c 1eda 0800 46c0 ..^....&Dl....F. +// 0x0010: 0024 17f1 4000 0102 297b c0a8 01fe e000 .$..@...){...... +// 0x0020: 0001 9404 0000 1118 ecd3 0000 0000 0214 ................ +// 0x0030: 0000 0000 0000 0000 0000 0000 ............ +var igmp3v3MembershipQueryPacket = []byte{ + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x26, 0x44, 0x6c, 0x1e, 0xda, 0x08, 0x00, 0x46, 0xc0, + 0x00, 0x24, 0x17, 0xf1, 0x40, 0x00, 0x01, 0x02, 0x29, 0x7b, 0xc0, 0xa8, 0x01, 0xfe, 0xe0, 0x00, + 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x18, 0xec, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestIGMPv3MembershipQuery(t *testing.T) { + p := gopacket.NewPacket(igmp3v3MembershipQueryPacket, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIGMP}, t) + + igmp := p.Layer(LayerTypeIGMP).(*IGMP) + if igmp.Type != IGMPMembershipQuery { + t.Fatal("Invalid IGMP type") + } +} + +func BenchmarkDecodeigmp3v3MembershipQueryPacket(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(igmp3v3MembershipQueryPacket, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// igmpv3MembershipReport2Records is the packet: +// 10:07:29.756202 IP 192.168.1.66 > 224.0.0.22: igmp v3 report, 2 group record(s) +// 0x0000: 0100 5e00 0016 0025 2e51 c381 0800 4658 ..^....%.Q....FX +// 0x0010: 0030 013c 0000 0102 8133 c0a8 0142 e000 .0.<.....3...B.. +// 0x0020: 0016 9404 0000 2200 f33c 0000 0002 0200 ......"..<...... +// 0x0030: 0000 efc3 0702 0200 0000 efff fffa .............. +var igmpv3MembershipReport2Records = []byte{ + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x16, 0x00, 0x25, 0x2e, 0x51, 0xc3, 0x81, 0x08, 0x00, 0x46, 0x58, + 0x00, 0x30, 0x01, 0x3c, 0x00, 0x00, 0x01, 0x02, 0x81, 0x33, 0xc0, 0xa8, 0x01, 0x42, 0xe0, 0x00, + 0x00, 0x16, 0x94, 0x04, 0x00, 0x00, 0x22, 0x00, 0xf3, 0x3c, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, + 0x00, 0x00, 0xef, 0xc3, 0x07, 0x02, 0x02, 0x00, 0x00, 0x00, 0xef, 0xff, 0xff, 0xfa, +} + +func TestIGMPv3MembershipReport2Records(t *testing.T) { + p := gopacket.NewPacket(igmpv3MembershipReport2Records, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIGMP}, t) + + igmp := p.Layer(LayerTypeIGMP).(*IGMP) + if igmp.Type != IGMPMembershipReportV3 { + t.Fatal("Invalid IGMP type") + } +} + +func BenchmarkDecodeigmpv3MembershipReport2Records(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(igmpv3MembershipReport2Records, LinkTypeEthernet, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/ip4.go b/vendor/github.com/google/gopacket/layers/ip4.go new file mode 100644 index 00000000..3f31b274 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ip4.go @@ -0,0 +1,311 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + "strings" + + "github.com/google/gopacket" +) + +type IPv4Flag uint8 + +const ( + IPv4EvilBit IPv4Flag = 1 << 2 // http://tools.ietf.org/html/rfc3514 ;) + IPv4DontFragment IPv4Flag = 1 << 1 + IPv4MoreFragments IPv4Flag = 1 << 0 +) + +func (f IPv4Flag) String() string { + var s []string + if f&IPv4EvilBit != 0 { + s = append(s, "Evil") + } + if f&IPv4DontFragment != 0 { + s = append(s, "DF") + } + if f&IPv4MoreFragments != 0 { + s = append(s, "MF") + } + return strings.Join(s, "|") +} + +// IPv4 is the header of an IP packet. +type IPv4 struct { + BaseLayer + Version uint8 + IHL uint8 + TOS uint8 + Length uint16 + Id uint16 + Flags IPv4Flag + FragOffset uint16 + TTL uint8 + Protocol IPProtocol + Checksum uint16 + SrcIP net.IP + DstIP net.IP + Options []IPv4Option + Padding []byte +} + +// LayerType returns LayerTypeIPv4 +func (i *IPv4) LayerType() gopacket.LayerType { return LayerTypeIPv4 } +func (i *IPv4) NetworkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointIPv4, i.SrcIP, i.DstIP) +} + +type IPv4Option struct { + OptionType uint8 + OptionLength uint8 + OptionData []byte +} + +func (i IPv4Option) String() string { + return fmt.Sprintf("IPv4Option(%v:%v)", i.OptionType, i.OptionData) +} + +// for the current ipv4 options, return the number of bytes (including +// padding that the options used) +func (ip *IPv4) getIPv4OptionSize() uint8 { + optionSize := uint8(0) + for _, opt := range ip.Options { + switch opt.OptionType { + case 0: + // this is the end of option lists + optionSize++ + case 1: + // this is the padding + optionSize++ + default: + optionSize += opt.OptionLength + + } + } + // make sure the options are aligned to 32 bit boundary + if (optionSize % 4) != 0 { + optionSize += 4 - (optionSize % 4) + } + return optionSize +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + optionLength := ip.getIPv4OptionSize() + bytes, err := b.PrependBytes(20 + int(optionLength)) + if err != nil { + return err + } + if opts.FixLengths { + ip.IHL = 5 + (optionLength / 4) + ip.Length = uint16(len(b.Bytes())) + } + bytes[0] = (ip.Version << 4) | ip.IHL + bytes[1] = ip.TOS + binary.BigEndian.PutUint16(bytes[2:], ip.Length) + binary.BigEndian.PutUint16(bytes[4:], ip.Id) + binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags()) + bytes[8] = ip.TTL + bytes[9] = byte(ip.Protocol) + if err := ip.AddressTo4(); err != nil { + return err + } + copy(bytes[12:16], ip.SrcIP) + copy(bytes[16:20], ip.DstIP) + + curLocation := 20 + // Now, we will encode the options + for _, opt := range ip.Options { + switch opt.OptionType { + case 0: + // this is the end of option lists + bytes[curLocation] = 0 + curLocation++ + case 1: + // this is the padding + bytes[curLocation] = 1 + curLocation++ + default: + bytes[curLocation] = opt.OptionType + bytes[curLocation+1] = opt.OptionLength + + // sanity checking to protect us from buffer overrun + if len(opt.OptionData) > int(opt.OptionLength-2) { + return errors.New("option length is smaller than length of option data") + } + copy(bytes[curLocation+2:curLocation+int(opt.OptionLength)], opt.OptionData) + curLocation += int(opt.OptionLength) + } + } + + if opts.ComputeChecksums { + ip.Checksum = checksum(bytes) + } + binary.BigEndian.PutUint16(bytes[10:], ip.Checksum) + return nil +} + +func checksum(bytes []byte) uint16 { + // Clear checksum bytes + bytes[10] = 0 + bytes[11] = 0 + + // Compute checksum + var csum uint32 + for i := 0; i < len(bytes); i += 2 { + csum += uint32(bytes[i]) << 8 + csum += uint32(bytes[i+1]) + } + for { + // Break when sum is less or equals to 0xFFFF + if csum <= 65535 { + break + } + // Add carry to the sum + csum = (csum >> 16) + uint32(uint16(csum)) + } + // Flip all the bits + return ^uint16(csum) +} + +func (ip *IPv4) flagsfrags() (ff uint16) { + ff |= uint16(ip.Flags) << 13 + ff |= ip.FragOffset + return +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + flagsfrags := binary.BigEndian.Uint16(data[6:8]) + + ip.Version = uint8(data[0]) >> 4 + ip.IHL = uint8(data[0]) & 0x0F + ip.TOS = data[1] + ip.Length = binary.BigEndian.Uint16(data[2:4]) + ip.Id = binary.BigEndian.Uint16(data[4:6]) + ip.Flags = IPv4Flag(flagsfrags >> 13) + ip.FragOffset = flagsfrags & 0x1FFF + ip.TTL = data[8] + ip.Protocol = IPProtocol(data[9]) + ip.Checksum = binary.BigEndian.Uint16(data[10:12]) + ip.SrcIP = data[12:16] + ip.DstIP = data[16:20] + ip.Options = ip.Options[:0] + // Set up an initial guess for contents/payload... we'll reset these soon. + ip.BaseLayer = BaseLayer{Contents: data} + + // This code is added for the following enviroment: + // * Windows 10 with TSO option activated. ( tested on Hyper-V, RealTek ethernet driver ) + if ip.Length == 0 { + // If using TSO(TCP Segmentation Offload), length is zero. + // The actual packet length is the length of data. + ip.Length = uint16(len(data)) + } + + if ip.Length < 20 { + return fmt.Errorf("Invalid (too small) IP length (%d < 20)", ip.Length) + } else if ip.IHL < 5 { + return fmt.Errorf("Invalid (too small) IP header length (%d < 5)", ip.IHL) + } else if int(ip.IHL*4) > int(ip.Length) { + return fmt.Errorf("Invalid IP header length > IP length (%d > %d)", ip.IHL, ip.Length) + } + if cmp := len(data) - int(ip.Length); cmp > 0 { + data = data[:ip.Length] + } else if cmp < 0 { + df.SetTruncated() + if int(ip.IHL)*4 > len(data) { + return errors.New("Not all IP header bytes available") + } + } + ip.Contents = data[:ip.IHL*4] + ip.Payload = data[ip.IHL*4:] + // From here on, data contains the header options. + data = data[20 : ip.IHL*4] + // Pull out IP options + for len(data) > 0 { + if ip.Options == nil { + // Pre-allocate to avoid growing the slice too much. + ip.Options = make([]IPv4Option, 0, 4) + } + opt := IPv4Option{OptionType: data[0]} + switch opt.OptionType { + case 0: // End of options + opt.OptionLength = 1 + ip.Options = append(ip.Options, opt) + ip.Padding = data[1:] + break + case 1: // 1 byte padding + opt.OptionLength = 1 + default: + opt.OptionLength = data[1] + opt.OptionData = data[2:opt.OptionLength] + } + if len(data) >= int(opt.OptionLength) { + data = data[opt.OptionLength:] + } else { + return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength) + } + ip.Options = append(ip.Options, opt) + } + return nil +} + +func (i *IPv4) CanDecode() gopacket.LayerClass { + return LayerTypeIPv4 +} + +func (i *IPv4) NextLayerType() gopacket.LayerType { + if i.Flags&IPv4MoreFragments != 0 || i.FragOffset != 0 { + return gopacket.LayerTypeFragment + } + return i.Protocol.LayerType() +} + +func decodeIPv4(data []byte, p gopacket.PacketBuilder) error { + ip := &IPv4{} + err := ip.DecodeFromBytes(data, p) + p.AddLayer(ip) + p.SetNetworkLayer(ip) + if err != nil { + return err + } + return p.NextDecoder(ip.NextLayerType()) +} + +func checkIPv4Address(addr net.IP) (net.IP, error) { + if c := addr.To4(); c != nil { + return c, nil + } + if len(addr) == net.IPv6len { + return nil, errors.New("address is IPv6") + } + return nil, fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv4len) +} + +func (ip *IPv4) AddressTo4() error { + var src, dst net.IP + + if addr, err := checkIPv4Address(ip.SrcIP); err != nil { + return fmt.Errorf("Invalid source IPv4 address (%s)", err) + } else { + src = addr + } + if addr, err := checkIPv4Address(ip.DstIP); err != nil { + return fmt.Errorf("Invalid destination IPv4 address (%s)", err) + } else { + dst = addr + } + ip.SrcIP = src + ip.DstIP = dst + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/ip4_test.go b/vendor/github.com/google/gopacket/layers/ip4_test.go new file mode 100644 index 00000000..ec6b3512 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ip4_test.go @@ -0,0 +1,131 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This file tests some of the functionality provided in the ip4.go + +package layers + +import ( + "encoding/binary" + "encoding/hex" + "net" + "testing" + + "github.com/google/gopacket" +) + +// Test the function getIPv4OptionSize when the ipv4 has no options +func TestGetIPOptLengthNoOpt(t *testing.T) { + ip := IPv4{} + length := ip.getIPv4OptionSize() + if length != 0 { + t.Fatalf("Empty option list should have 0 length. Actual %d", length) + } +} + +// Test the function getIPv4OptionSize when the ipv4 has end of list option +func TestGetIPOptLengthEndOfList(t *testing.T) { + ip := IPv4{} + ip.Options = append(ip.Options, IPv4Option{OptionType: 0, OptionLength: 1}) + length := ip.getIPv4OptionSize() + if length != 4 { + t.Fatalf("After padding, the list should have 4 length. Actual %d", length) + } +} + +// Test the function getIPv4OptionSize when the ipv4 has padding and end of list option +func TestGetIPOptLengthPaddingEndOfList(t *testing.T) { + ip := IPv4{} + ip.Options = append(ip.Options, IPv4Option{OptionType: 1, OptionLength: 1}) + ip.Options = append(ip.Options, IPv4Option{OptionType: 0, OptionLength: 1}) + length := ip.getIPv4OptionSize() + if length != 4 { + t.Fatalf("After padding, the list should have 4 length. Actual %d", length) + } +} + +// Test the function getIPv4OptionSize when the ipv4 has some non-trivial option and end of list option +func TestGetIPOptLengthOptionEndOfList(t *testing.T) { + ip := IPv4{} + someByte := make([]byte, 8) + ip.Options = append(ip.Options, IPv4Option{OptionType: 2, OptionLength: 10, OptionData: someByte}) + ip.Options = append(ip.Options, IPv4Option{OptionType: 0, OptionLength: 1}) + length := ip.getIPv4OptionSize() + if length != 12 { + t.Fatalf("The list should have 12 length. Actual %d", length) + } +} + +// Tests that the Options slice is properly reset before parsing new data +func TestIPOptResetDuringDecoding(t *testing.T) { + ip := &IPv4{ + Options: []IPv4Option{{OptionType: 42, OptionLength: 4, OptionData: make([]byte, 2)}}, + } + + ipWithoutOptions := &IPv4{ + SrcIP: net.IPv4(192, 168, 1, 1), + DstIP: net.IPv4(192, 168, 1, 1), + Protocol: IPProtocolTCP, + } + + ipBytes, err := serialize(ipWithoutOptions) + + if err != nil { + t.Fatalf("Failed to serialize ip layer: %v", err) + } + + err = ip.DecodeFromBytes(ipBytes, gopacket.NilDecodeFeedback) + + if err != nil { + t.Fatalf("Failed to deserialize ip layer: %v", err) + } + + if len(ip.Options) > 0 { + t.Fatalf("Options slice has stale data from previous packet") + } + +} + +func serialize(ip *IPv4) ([]byte, error) { + buffer := gopacket.NewSerializeBuffer() + err := ip.SerializeTo(buffer, gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + }) + return buffer.Bytes(), err +} + +// Test the function checksum +func TestChecksum(t *testing.T) { + testData := []struct { + name string + header string + want string + }{{ + name: "sum has two carries", + header: "4540005800000000ff11ffff0aeb1d070aed8877", + want: "fffe", + }, { + name: "wikipedia case", + header: "45000073000040004011b861c0a80001c0a800c7", + want: "b861", + }} + + for _, test := range testData { + bytes, err := hex.DecodeString(test.header) + if err != nil { + t.Fatalf("Failed to Decode header: %v", err) + } + wantBytes, err := hex.DecodeString(test.want) + if err != nil { + t.Fatalf("Failed to decode want checksum: %v", err) + } + + if got, want := checksum(bytes), binary.BigEndian.Uint16(wantBytes); got != want { + t.Errorf("In test %q, got incorrect checksum: got(%x), want(%x)", test.name, got, want) + } + } +} diff --git a/vendor/github.com/google/gopacket/layers/ip6.go b/vendor/github.com/google/gopacket/layers/ip6.go new file mode 100644 index 00000000..b5befe9c --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ip6.go @@ -0,0 +1,650 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +const ( + IPv6HopByHopOptionJumbogram = 0xC2 // RFC 2675 +) + +const ( + ipv6MaxPayloadLength = 65535 +) + +// IPv6 is the layer for the IPv6 header. +type IPv6 struct { + // http://www.networksorcery.com/enp/protocol/ipv6.htm + BaseLayer + Version uint8 + TrafficClass uint8 + FlowLabel uint32 + Length uint16 + NextHeader IPProtocol + HopLimit uint8 + SrcIP net.IP + DstIP net.IP + HopByHop *IPv6HopByHop + // hbh will be pointed to by HopByHop if that layer exists. + hbh IPv6HopByHop +} + +// LayerType returns LayerTypeIPv6 +func (i *IPv6) LayerType() gopacket.LayerType { return LayerTypeIPv6 } + +func (i *IPv6) NetworkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointIPv6, i.SrcIP, i.DstIP) +} + +// Search for Jumbo Payload TLV in IPv6HopByHop and return (length, true) if found +func getIPv6HopByHopJumboLength(hopopts *IPv6HopByHop) (uint32, bool, error) { + var tlv *IPv6HopByHopOption + + for _, t := range hopopts.Options { + if t.OptionType == IPv6HopByHopOptionJumbogram { + tlv = t + break + } + } + if tlv == nil { + // Not found + return 0, false, nil + } + if len(tlv.OptionData) != 4 { + return 0, false, errors.New("Jumbo length TLV data must have length 4") + } + l := binary.BigEndian.Uint32(tlv.OptionData) + if l <= ipv6MaxPayloadLength { + return 0, false, fmt.Errorf("Jumbo length cannot be less than %d", ipv6MaxPayloadLength+1) + } + // Found + return l, true, nil +} + +// Adds zero-valued Jumbo TLV to IPv6 header if it does not exist +// (if necessary add hop-by-hop header) +func addIPv6JumboOption(ip6 *IPv6) { + var tlv *IPv6HopByHopOption + + if ip6.HopByHop == nil { + // Add IPv6 HopByHop + ip6.HopByHop = &IPv6HopByHop{} + ip6.HopByHop.NextHeader = ip6.NextHeader + ip6.HopByHop.HeaderLength = 0 + ip6.NextHeader = IPProtocolIPv6HopByHop + } + for _, t := range ip6.HopByHop.Options { + if t.OptionType == IPv6HopByHopOptionJumbogram { + tlv = t + break + } + } + if tlv == nil { + // Add Jumbo TLV + tlv = &IPv6HopByHopOption{} + ip6.HopByHop.Options = append(ip6.HopByHop.Options, tlv) + } + tlv.SetJumboLength(0) +} + +// Set jumbo length in serialized IPv6 payload (starting with HopByHop header) +func setIPv6PayloadJumboLength(hbh []byte) error { + pLen := len(hbh) + if pLen < 8 { + //HopByHop is minimum 8 bytes + return fmt.Errorf("Invalid IPv6 payload (length %d)", pLen) + } + hbhLen := int((hbh[1] + 1) * 8) + if hbhLen > pLen { + return fmt.Errorf("Invalid hop-by-hop length (length: %d, payload: %d", hbhLen, pLen) + } + offset := 2 //start with options + for offset < hbhLen { + opt := hbh[offset] + if opt == 0 { + //Pad1 + offset += 1 + continue + } + optLen := int(hbh[offset+1]) + if opt == IPv6HopByHopOptionJumbogram { + if optLen == 4 { + binary.BigEndian.PutUint32(hbh[offset+2:], uint32(pLen)) + return nil + } + return fmt.Errorf("Jumbo TLV too short (%d bytes)", optLen) + } + offset += 2 + optLen + } + return errors.New("Jumbo TLV not found") +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (ip6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var jumbo bool + var err error + + payload := b.Bytes() + pLen := len(payload) + if pLen > ipv6MaxPayloadLength { + jumbo = true + if opts.FixLengths { + // We need to set the length later because the hop-by-hop header may + // not exist or else need padding, so pLen may yet change + addIPv6JumboOption(ip6) + } else if ip6.HopByHop == nil { + return fmt.Errorf("Cannot fit payload length of %d into IPv6 packet", pLen) + } else { + _, ok, err := getIPv6HopByHopJumboLength(ip6.HopByHop) + if err != nil { + return err + } + if !ok { + return errors.New("Missing jumbo length hop-by-hop option") + } + } + } + if ip6.HopByHop != nil { + if ip6.NextHeader != IPProtocolIPv6HopByHop { + // Just fix it instead of throwing an error + ip6.NextHeader = IPProtocolIPv6HopByHop + } + err = ip6.HopByHop.SerializeTo(b, opts) + if err != nil { + return err + } + payload = b.Bytes() + pLen = len(payload) + if opts.FixLengths && jumbo { + err := setIPv6PayloadJumboLength(payload) + if err != nil { + return err + } + } + } + if !jumbo && pLen > ipv6MaxPayloadLength { + return errors.New("Cannot fit payload into IPv6 header") + } + bytes, err := b.PrependBytes(40) + if err != nil { + return err + } + bytes[0] = (ip6.Version << 4) | (ip6.TrafficClass >> 4) + bytes[1] = (ip6.TrafficClass << 4) | uint8(ip6.FlowLabel>>16) + binary.BigEndian.PutUint16(bytes[2:], uint16(ip6.FlowLabel)) + if opts.FixLengths { + if jumbo { + ip6.Length = 0 + } else { + ip6.Length = uint16(pLen) + } + } + binary.BigEndian.PutUint16(bytes[4:], ip6.Length) + bytes[6] = byte(ip6.NextHeader) + bytes[7] = byte(ip6.HopLimit) + if err := ip6.AddressTo16(); err != nil { + return err + } + copy(bytes[8:], ip6.SrcIP) + copy(bytes[24:], ip6.DstIP) + return nil +} + +func (ip6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + ip6.Version = uint8(data[0]) >> 4 + ip6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF) + ip6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF + ip6.Length = binary.BigEndian.Uint16(data[4:6]) + ip6.NextHeader = IPProtocol(data[6]) + ip6.HopLimit = data[7] + ip6.SrcIP = data[8:24] + ip6.DstIP = data[24:40] + ip6.HopByHop = nil + ip6.BaseLayer = BaseLayer{data[:40], data[40:]} + + // We treat a HopByHop IPv6 option as part of the IPv6 packet, since its + // options are crucial for understanding what's actually happening per packet. + if ip6.NextHeader == IPProtocolIPv6HopByHop { + err := ip6.hbh.DecodeFromBytes(ip6.Payload, df) + if err != nil { + return err + } + ip6.HopByHop = &ip6.hbh + pEnd, jumbo, err := getIPv6HopByHopJumboLength(ip6.HopByHop) + if err != nil { + return err + } + if jumbo && ip6.Length == 0 { + pEnd := int(pEnd) + if pEnd > len(ip6.Payload) { + df.SetTruncated() + pEnd = len(ip6.Payload) + } + ip6.Payload = ip6.Payload[:pEnd] + return nil + } else if jumbo && ip6.Length != 0 { + return errors.New("IPv6 has jumbo length and IPv6 length is not 0") + } else if !jumbo && ip6.Length == 0 { + return errors.New("IPv6 length 0, but HopByHop header does not have jumbogram option") + } + } + + if ip6.Length == 0 { + return fmt.Errorf("IPv6 length 0, but next header is %v, not HopByHop", ip6.NextHeader) + } else { + pEnd := int(ip6.Length) + if pEnd > len(ip6.Payload) { + df.SetTruncated() + pEnd = len(ip6.Payload) + } + ip6.Payload = ip6.Payload[:pEnd] + } + return nil +} + +func (i *IPv6) CanDecode() gopacket.LayerClass { + return LayerTypeIPv6 +} + +func (i *IPv6) NextLayerType() gopacket.LayerType { + if i.HopByHop != nil { + return i.HopByHop.NextHeader.LayerType() + } + return i.NextHeader.LayerType() +} + +func decodeIPv6(data []byte, p gopacket.PacketBuilder) error { + ip6 := &IPv6{} + err := ip6.DecodeFromBytes(data, p) + p.AddLayer(ip6) + p.SetNetworkLayer(ip6) + if ip6.HopByHop != nil { + p.AddLayer(ip6.HopByHop) + } + if err != nil { + return err + } + return p.NextDecoder(ip6.NextLayerType()) +} + +type ipv6HeaderTLVOption struct { + OptionType, OptionLength uint8 + ActualLength int + OptionData []byte + OptionAlignment [2]uint8 // Xn+Y = [2]uint8{X, Y} +} + +func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun bool) int { + if fixLengths { + h.OptionLength = uint8(len(h.OptionData)) + } + length := int(h.OptionLength) + 2 + if !dryrun { + data[0] = h.OptionType + data[1] = h.OptionLength + copy(data[2:], h.OptionData) + } + return length +} + +func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) { + h = &ipv6HeaderTLVOption{} + if data[0] == 0 { + h.ActualLength = 1 + return + } + h.OptionType = data[0] + h.OptionLength = data[1] + h.ActualLength = int(h.OptionLength) + 2 + h.OptionData = data[2:h.ActualLength] + return +} + +func serializeTLVOptionPadding(data []byte, padLength int) { + if padLength <= 0 { + return + } + if padLength == 1 { + data[0] = 0x0 + return + } + tlvLength := uint8(padLength) - 2 + data[0] = 0x1 + data[1] = tlvLength + if tlvLength != 0 { + for k := range data[2:] { + data[k+2] = 0x0 + } + } + return +} + +// If buf is 'nil' do a serialize dry run +func serializeIPv6HeaderTLVOptions(buf []byte, options []*ipv6HeaderTLVOption, fixLengths bool) int { + var l int + + dryrun := buf == nil + length := 2 + for _, opt := range options { + if fixLengths { + x := int(opt.OptionAlignment[0]) + y := int(opt.OptionAlignment[1]) + if x != 0 { + n := length / x + offset := x*n + y + if offset < length { + offset += x + } + if length != offset { + pad := offset - length + if !dryrun { + serializeTLVOptionPadding(buf[length-2:], pad) + } + length += pad + } + } + } + if dryrun { + l = opt.serializeTo(nil, fixLengths, true) + } else { + l = opt.serializeTo(buf[length-2:], fixLengths, false) + } + length += l + } + if fixLengths { + pad := length % 8 + if pad != 0 { + if !dryrun { + serializeTLVOptionPadding(buf[length-2:], pad) + } + length += pad + } + } + return length - 2 +} + +type ipv6ExtensionBase struct { + BaseLayer + NextHeader IPProtocol + HeaderLength uint8 + ActualLength int +} + +func decodeIPv6ExtensionBase(data []byte) (i ipv6ExtensionBase) { + i.NextHeader = IPProtocol(data[0]) + i.HeaderLength = data[1] + i.ActualLength = int(i.HeaderLength)*8 + 8 + i.Contents = data[:i.ActualLength] + i.Payload = data[i.ActualLength:] + return +} + +// IPv6ExtensionSkipper is a DecodingLayer which decodes and ignores v6 +// extensions. You can use it with a DecodingLayerParser to handle IPv6 stacks +// which may or may not have extensions. +type IPv6ExtensionSkipper struct { + NextHeader IPProtocol + BaseLayer +} + +func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + extension := decodeIPv6ExtensionBase(data) + i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]} + i.NextHeader = extension.NextHeader + return nil +} + +func (i *IPv6ExtensionSkipper) CanDecode() gopacket.LayerClass { + return LayerClassIPv6Extension +} + +func (i *IPv6ExtensionSkipper) NextLayerType() gopacket.LayerType { + return i.NextHeader.LayerType() +} + +// IPv6HopByHopOption is a TLV option present in an IPv6 hop-by-hop extension. +type IPv6HopByHopOption ipv6HeaderTLVOption + +// IPv6HopByHop is the IPv6 hop-by-hop extension. +type IPv6HopByHop struct { + ipv6ExtensionBase + Options []*IPv6HopByHopOption +} + +// LayerType returns LayerTypeIPv6HopByHop. +func (i *IPv6HopByHop) LayerType() gopacket.LayerType { return LayerTypeIPv6HopByHop } + +func (i *IPv6HopByHop) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var bytes []byte + var err error + + o := make([]*ipv6HeaderTLVOption, 0, len(i.Options)) + for _, v := range i.Options { + o = append(o, (*ipv6HeaderTLVOption)(v)) + } + + l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths) + bytes, err = b.PrependBytes(l) + if err != nil { + return err + } + serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths) + + length := len(bytes) + 2 + if length%8 != 0 { + return errors.New("IPv6HopByHop actual length must be multiple of 8") + } + bytes, err = b.PrependBytes(2) + if err != nil { + return err + } + bytes[0] = uint8(i.NextHeader) + if opts.FixLengths { + i.HeaderLength = uint8((length / 8) - 1) + } + bytes[1] = uint8(i.HeaderLength) + return nil +} + +func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data) + offset := 2 + for offset < i.ActualLength { + opt := decodeIPv6HeaderTLVOption(data[offset:]) + i.Options = append(i.Options, (*IPv6HopByHopOption)(opt)) + offset += opt.ActualLength + } + return nil +} + +func decodeIPv6HopByHop(data []byte, p gopacket.PacketBuilder) error { + i := &IPv6HopByHop{} + err := i.DecodeFromBytes(data, p) + p.AddLayer(i) + if err != nil { + return err + } + return p.NextDecoder(i.NextHeader) +} + +func (o *IPv6HopByHopOption) SetJumboLength(len uint32) { + o.OptionType = IPv6HopByHopOptionJumbogram + o.OptionLength = 4 + o.ActualLength = 6 + if o.OptionData == nil { + o.OptionData = make([]byte, 4) + } + binary.BigEndian.PutUint32(o.OptionData, len) + o.OptionAlignment = [2]uint8{4, 2} +} + +// IPv6Routing is the IPv6 routing extension. +type IPv6Routing struct { + ipv6ExtensionBase + RoutingType uint8 + SegmentsLeft uint8 + // This segment is supposed to be zero according to RFC2460, the second set of + // 4 bytes in the extension. + Reserved []byte + // SourceRoutingIPs is the set of IPv6 addresses requested for source routing, + // set only if RoutingType == 0. + SourceRoutingIPs []net.IP +} + +// LayerType returns LayerTypeIPv6Routing. +func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing } + +func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error { + i := &IPv6Routing{ + ipv6ExtensionBase: decodeIPv6ExtensionBase(data), + RoutingType: data[2], + SegmentsLeft: data[3], + Reserved: data[4:8], + } + switch i.RoutingType { + case 0: // Source routing + if (i.ActualLength-8)%16 != 0 { + return fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength) + } + for d := i.Contents[8:]; len(d) >= 16; d = d[16:] { + i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16])) + } + default: + return fmt.Errorf("Unknown IPv6 routing header type %d", i.RoutingType) + } + p.AddLayer(i) + return p.NextDecoder(i.NextHeader) +} + +// IPv6Fragment is the IPv6 fragment header, used for packet +// fragmentation/defragmentation. +type IPv6Fragment struct { + BaseLayer + NextHeader IPProtocol + // Reserved1 is bits [8-16), from least to most significant, 0-indexed + Reserved1 uint8 + FragmentOffset uint16 + // Reserved2 is bits [29-31), from least to most significant, 0-indexed + Reserved2 uint8 + MoreFragments bool + Identification uint32 +} + +// LayerType returns LayerTypeIPv6Fragment. +func (i *IPv6Fragment) LayerType() gopacket.LayerType { return LayerTypeIPv6Fragment } + +func decodeIPv6Fragment(data []byte, p gopacket.PacketBuilder) error { + i := &IPv6Fragment{ + BaseLayer: BaseLayer{data[:8], data[8:]}, + NextHeader: IPProtocol(data[0]), + Reserved1: data[1], + FragmentOffset: binary.BigEndian.Uint16(data[2:4]) >> 3, + Reserved2: data[3] & 0x6 >> 1, + MoreFragments: data[3]&0x1 != 0, + Identification: binary.BigEndian.Uint32(data[4:8]), + } + p.AddLayer(i) + return p.NextDecoder(gopacket.DecodeFragment) +} + +// IPv6DestinationOption is a TLV option present in an IPv6 destination options extension. +type IPv6DestinationOption ipv6HeaderTLVOption + +// IPv6Destination is the IPv6 destination options header. +type IPv6Destination struct { + ipv6ExtensionBase + Options []*IPv6DestinationOption +} + +// LayerType returns LayerTypeIPv6Destination. +func (i *IPv6Destination) LayerType() gopacket.LayerType { return LayerTypeIPv6Destination } + +func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data) + offset := 2 + for offset < i.ActualLength { + opt := decodeIPv6HeaderTLVOption(data[offset:]) + i.Options = append(i.Options, (*IPv6DestinationOption)(opt)) + offset += opt.ActualLength + } + return nil +} + +func decodeIPv6Destination(data []byte, p gopacket.PacketBuilder) error { + i := &IPv6Destination{} + err := i.DecodeFromBytes(data, p) + p.AddLayer(i) + if err != nil { + return err + } + return p.NextDecoder(i.NextHeader) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *IPv6Destination) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var bytes []byte + var err error + + o := make([]*ipv6HeaderTLVOption, 0, len(i.Options)) + for _, v := range i.Options { + o = append(o, (*ipv6HeaderTLVOption)(v)) + } + + l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths) + bytes, err = b.PrependBytes(l) + if err != nil { + return err + } + serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths) + + length := len(bytes) + 2 + if length%8 != 0 { + return errors.New("IPv6Destination actual length must be multiple of 8") + } + bytes, err = b.PrependBytes(2) + if err != nil { + return err + } + bytes[0] = uint8(i.NextHeader) + if opts.FixLengths { + i.HeaderLength = uint8((length / 8) - 1) + } + bytes[1] = uint8(i.HeaderLength) + return nil +} + +func checkIPv6Address(addr net.IP) error { + if len(addr) == net.IPv6len { + return nil + } + if len(addr) == net.IPv4len { + return errors.New("address is IPv4") + } + return fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv6len) +} + +func (ip *IPv6) AddressTo16() error { + if err := checkIPv6Address(ip.SrcIP); err != nil { + return fmt.Errorf("Invalid source IPv6 address (%s)", err) + } + if err := checkIPv6Address(ip.DstIP); err != nil { + return fmt.Errorf("Invalid destination IPv6 address (%s)", err) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/ip6_test.go b/vendor/github.com/google/gopacket/layers/ip6_test.go new file mode 100644 index 00000000..ab22f178 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ip6_test.go @@ -0,0 +1,430 @@ +// Copyright 2014, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "github.com/google/gopacket" + "net" + "reflect" + "testing" +) + +func TestSerializeIPv6HeaderTLVOptions(t *testing.T) { + //RFC 2460 Appendix B + /* + Example 3 + + A Hop-by-Hop or Destination Options header containing both options X + and Y from Examples 1 and 2 would have one of the two following + formats, depending on which option appeared first: + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len=3 | Option Type=X |Opt Data Len=12| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4-octet field | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + 8-octet field + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PadN Option=1 |Opt Data Len=1 | 0 | Option Type=Y | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Opt Data Len=7 | 1-octet field | 2-octet field | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4-octet field | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PadN Option=1 |Opt Data Len=2 | 0 | 0 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + opt1 := &ipv6HeaderTLVOption{} + opt1.OptionType = 0x1e + opt1.OptionData = []byte{0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb} + opt1.OptionAlignment = [2]uint8{8, 2} + + opt2 := &ipv6HeaderTLVOption{} + opt2.OptionType = 0x3e + opt2.OptionData = []byte{0x11, 0x22, 0x22, 0x44, 0x44, 0x44, 0x44} + opt2.OptionAlignment = [2]uint8{4, 3} + + l := serializeIPv6HeaderTLVOptions(nil, []*ipv6HeaderTLVOption{opt1, opt2}, true) + b := make([]byte, l) + serializeIPv6HeaderTLVOptions(b, []*ipv6HeaderTLVOption{opt1, opt2}, true) + got := b + want := []byte{0x1e, 0x0c, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x01, 0x01, 0x00, 0x3e, 0x07, 0x11, 0x22, 0x22, 0x44, 0x44, 0x44, 0x44, 0x01, 0x02, 0x00, 0x00} + + if !bytes.Equal(got, want) { + t.Errorf("IPv6HeaderTLVOption serialize (X,Y) failed:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } + + /* + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len=3 | Pad1 Option=0 | Option Type=Y | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Opt Data Len=7 | 1-octet field | 2-octet field | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4-octet field | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PadN Option=1 |Opt Data Len=4 | 0 | 0 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0 | 0 | Option Type=X |Opt Data Len=12| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 4-octet field | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + 8-octet field + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + l = serializeIPv6HeaderTLVOptions(nil, []*ipv6HeaderTLVOption{opt2, opt1}, true) + b = make([]byte, l) + serializeIPv6HeaderTLVOptions(b, []*ipv6HeaderTLVOption{opt2, opt1}, true) + got = b + want = []byte{0x00, 0x3e, 0x07, 0x11, 0x22, 0x22, 0x44, 0x44, 0x44, 0x44, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb} + + if !bytes.Equal(got, want) { + t.Errorf("IPv6HeaderTLVOption serialize (Y,X) failed:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } +} + +var testPacketIPv6HopByHop0 = []byte{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, +} + +func TestPacketIPv6HopByHop0Serialize(t *testing.T) { + var serialize []gopacket.SerializableLayer = make([]gopacket.SerializableLayer, 0, 2) + var err error + + ip6 := &IPv6{} + ip6.Version = 6 + ip6.NextHeader = IPProtocolIPv6HopByHop + ip6.HopLimit = 64 + ip6.SrcIP = net.ParseIP("2001:db8::1") + ip6.DstIP = net.ParseIP("2001:db8::2") + serialize = append(serialize, ip6) + + tlv := &IPv6HopByHopOption{} + tlv.OptionType = 0x01 //PadN + tlv.OptionData = []byte{0x00, 0x00, 0x00, 0x00} + hop := &IPv6HopByHop{} + hop.Options = append(hop.Options, tlv) + hop.NextHeader = IPProtocolNoNextHeader + ip6.HopByHop = hop + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err = gopacket.SerializeLayers(buf, opts, serialize...) + if err != nil { + t.Fatal(err) + } + + got := buf.Bytes() + want := testPacketIPv6HopByHop0 + if !bytes.Equal(got, want) { + t.Errorf("IPv6HopByHop serialize failed:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } +} + +func TestPacketIPv6HopByHop0Decode(t *testing.T) { + ip6 := &IPv6{ + BaseLayer: BaseLayer{ + Contents: []byte{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }, + Payload: []byte{0x3b, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00}, + }, + Version: 6, + TrafficClass: 0, + FlowLabel: 0, + Length: 8, + NextHeader: IPProtocolIPv6HopByHop, + HopLimit: 64, + SrcIP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + DstIP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + } + + hop := &ip6.hbh + hop.ipv6ExtensionBase = ipv6ExtensionBase{ + BaseLayer: BaseLayer{ + Contents: []byte{0x3b, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{}, + }, + NextHeader: IPProtocolNoNextHeader, + HeaderLength: uint8(0), + ActualLength: 8, + } + opt := &IPv6HopByHopOption{ + OptionType: uint8(0x01), + OptionLength: uint8(0x04), + ActualLength: 6, + OptionData: []byte{0x00, 0x00, 0x00, 0x00}, + } + hop.Options = append(hop.Options, opt) + ip6.HopByHop = hop + + p := gopacket.NewPacket(testPacketIPv6HopByHop0, LinkTypeRaw, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeIPv6, LayerTypeIPv6HopByHop}, t) + if got, ok := p.Layer(LayerTypeIPv6).(*IPv6); ok { + want := ip6 + want.HopByHop = got.HopByHop // avoid comparing pointers + if !reflect.DeepEqual(got, want) { + t.Errorf("IPv6 packet processing failed:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } + } else { + t.Error("No IPv6 layer type found in packet") + } + if got, ok := p.Layer(LayerTypeIPv6HopByHop).(*IPv6HopByHop); ok { + want := hop + if !reflect.DeepEqual(got, want) { + t.Errorf("IPv6HopByHop packet processing failed:\ngot\n%#v\n\nwant:\n%#v\n\n", got, want) + } + } else { + t.Error("No IPv6HopByHop layer type found in packet") + } +} + +// testPacketIPv6Destination0 is the packet: +// 12:40:14.429409595 IP6 2001:db8::1 > 2001:db8::2: DSTOPT no next header +// 0x0000: 6000 0000 0008 3c40 2001 0db8 0000 0000 `.....<@........ +// 0x0010: 0000 0000 0000 0001 2001 0db8 0000 0000 ................ +// 0x0020: 0000 0000 0000 0002 3b00 0104 0000 0000 ........;....... +var testPacketIPv6Destination0 = []byte{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3c, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3b, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, +} + +func TestPacketIPv6Destination0Serialize(t *testing.T) { + var serialize []gopacket.SerializableLayer = make([]gopacket.SerializableLayer, 0, 2) + var err error + + ip6 := &IPv6{} + ip6.Version = 6 + ip6.NextHeader = IPProtocolIPv6Destination + ip6.HopLimit = 64 + ip6.SrcIP = net.ParseIP("2001:db8::1") + ip6.DstIP = net.ParseIP("2001:db8::2") + serialize = append(serialize, ip6) + + tlv := &IPv6DestinationOption{} + tlv.OptionType = 0x01 //PadN + tlv.OptionData = []byte{0x00, 0x00, 0x00, 0x00} + dst := &IPv6Destination{} + dst.Options = append(dst.Options, tlv) + dst.NextHeader = IPProtocolNoNextHeader + serialize = append(serialize, dst) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err = gopacket.SerializeLayers(buf, opts, serialize...) + if err != nil { + t.Fatal(err) + } + + got := buf.Bytes() + want := testPacketIPv6Destination0 + if !bytes.Equal(got, want) { + t.Errorf("IPv6Destination serialize failed:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } +} + +func TestPacketIPv6Destination0Decode(t *testing.T) { + ip6 := &IPv6{ + BaseLayer: BaseLayer{ + Contents: []byte{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3c, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }, + Payload: []byte{0x3b, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00}, + }, + Version: 6, + TrafficClass: 0, + FlowLabel: 0, + Length: 8, + NextHeader: IPProtocolIPv6Destination, + HopLimit: 64, + SrcIP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + DstIP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + } + + dst := &IPv6Destination{} + dst.BaseLayer = BaseLayer{ + Contents: []byte{0x3b, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00}, + Payload: []byte{}, + } + dst.NextHeader = IPProtocolNoNextHeader + dst.HeaderLength = uint8(0) + dst.ActualLength = 8 + opt := &IPv6DestinationOption{ + OptionType: uint8(0x01), + OptionLength: uint8(0x04), + ActualLength: 6, + OptionData: []byte{0x00, 0x00, 0x00, 0x00}, + } + dst.Options = append(dst.Options, opt) + + p := gopacket.NewPacket(testPacketIPv6Destination0, LinkTypeRaw, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeIPv6, LayerTypeIPv6Destination}, t) + if got, ok := p.Layer(LayerTypeIPv6).(*IPv6); ok { + want := ip6 + if !reflect.DeepEqual(got, want) { + t.Errorf("IPv6 packet processing failed:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } + } else { + t.Error("No IPv6 layer type found in packet") + } + if got, ok := p.Layer(LayerTypeIPv6Destination).(*IPv6Destination); ok { + want := dst + if !reflect.DeepEqual(got, want) { + t.Errorf("IPv6Destination packet processing failed:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } + } else { + t.Error("No IPv6Destination layer type found in packet") + } +} + +var testPacketIPv6JumbogramHeader = []byte{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3b, 0x00, 0xc2, 0x04, 0x00, 0x01, 0x00, 0x08, +} + +func TestIPv6JumbogramSerialize(t *testing.T) { + var serialize []gopacket.SerializableLayer = make([]gopacket.SerializableLayer, 0, 2) + var err error + + ip6 := &IPv6{} + ip6.Version = 6 + ip6.NextHeader = IPProtocolNoNextHeader + ip6.HopLimit = 64 + ip6.SrcIP = net.ParseIP("2001:db8::1") + ip6.DstIP = net.ParseIP("2001:db8::2") + serialize = append(serialize, ip6) + + payload := make([]byte, ipv6MaxPayloadLength+1) + for i := range payload { + payload[i] = 0xfe + } + serialize = append(serialize, gopacket.Payload(payload)) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err = gopacket.SerializeLayers(buf, opts, serialize...) + if err != nil { + t.Fatal(err) + } + + got := buf.Bytes() + w := new(bytes.Buffer) + w.Write(testPacketIPv6JumbogramHeader) + w.Write(payload) + want := w.Bytes() + + if !bytes.Equal(got, want) { + t.Errorf("IPv6 Jumbogram serialize failed:\ngot:\n%v\n\nwant:\n%v\n\n", + gopacket.LongBytesGoString(got), gopacket.LongBytesGoString(want)) + } +} + +func TestIPv6JumbogramDecode(t *testing.T) { + payload := make([]byte, ipv6MaxPayloadLength+1) + for i := range payload { + payload[i] = 0xfe + } + + ip6 := &IPv6{ + BaseLayer: BaseLayer{ + Contents: []byte{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }, + }, + Version: 6, + TrafficClass: 0, + FlowLabel: 0, + Length: 0, + NextHeader: IPProtocolIPv6HopByHop, + HopLimit: 64, + SrcIP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + DstIP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + } + buf := new(bytes.Buffer) + buf.Write([]byte{0x3b, 0x00, 0xc2, 0x04, 0x00, 0x01, 0x00, 0x08}) + buf.Write(payload) + ip6.Payload = buf.Bytes() + + hop := &ip6.hbh + hop.Contents = []byte{0x3b, 0x00, 0xc2, 0x04, 0x00, 0x01, 0x00, 0x08} + hop.Payload = payload + hop.NextHeader = IPProtocolNoNextHeader + hop.HeaderLength = uint8(0) + hop.ActualLength = 8 + opt := &IPv6HopByHopOption{} + opt.OptionType = uint8(0xc2) + opt.OptionLength = uint8(0x04) + opt.ActualLength = 6 + opt.OptionData = []byte{0x00, 0x01, 0x00, 0x08} + hop.Options = append(hop.Options, opt) + ip6.HopByHop = hop + + pkt := new(bytes.Buffer) + pkt.Write(testPacketIPv6JumbogramHeader) + pkt.Write(payload) + + p := gopacket.NewPacket(pkt.Bytes(), LinkTypeRaw, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeIPv6, LayerTypeIPv6HopByHop, gopacket.LayerTypePayload}, t) + + if got, ok := p.Layer(LayerTypeIPv6).(*IPv6); ok { + want := ip6 + want.HopByHop = got.HopByHop // Hack, avoid comparing pointers + if !reflect.DeepEqual(got, want) { + t.Errorf("IPv6 packet processing failed:\ngot:\n%v\n\nwant:\n%v\n\n", + gopacket.LayerGoString(got), gopacket.LayerGoString(want)) + } + } else { + t.Error("No IPv6 layer type found in packet") + } + + if got, ok := p.Layer(LayerTypeIPv6HopByHop).(*IPv6HopByHop); ok { + want := hop + if !reflect.DeepEqual(got, want) { + t.Errorf("IPv6HopByHop packet processing failed:\ngot:\n%v\n\nwant:\n%v\n\n", + gopacket.LayerGoString(got), gopacket.LayerGoString(want)) + } + } else { + t.Error("No IPv6HopByHop layer type found in packet") + } + + if got, ok := p.Layer(gopacket.LayerTypePayload).(*gopacket.Payload); ok { + want := (*gopacket.Payload)(&payload) + if !reflect.DeepEqual(got, want) { + t.Errorf("Payload packet processing failed:\ngot:\n%v\n\nwant:\n%v\n\n", + gopacket.LayerGoString(got), gopacket.LayerGoString(want)) + } + } else { + t.Error("No Payload layer type found in packet") + } +} diff --git a/vendor/github.com/google/gopacket/layers/ipsec.go b/vendor/github.com/google/gopacket/layers/ipsec.go new file mode 100644 index 00000000..19163fa3 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ipsec.go @@ -0,0 +1,68 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// IPSecAH is the authentication header for IPv4/6 defined in +// http://tools.ietf.org/html/rfc2402 +type IPSecAH struct { + // While the auth header can be used for both IPv4 and v6, its format is that of + // an IPv6 extension (NextHeader, PayloadLength, etc...), so we use ipv6ExtensionBase + // to build it. + ipv6ExtensionBase + Reserved uint16 + SPI, Seq uint32 + AuthenticationData []byte +} + +// LayerType returns LayerTypeIPSecAH. +func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH } + +func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error { + i := &IPSecAH{ + ipv6ExtensionBase: ipv6ExtensionBase{ + NextHeader: IPProtocol(data[0]), + HeaderLength: data[1], + }, + Reserved: binary.BigEndian.Uint16(data[2:4]), + SPI: binary.BigEndian.Uint32(data[4:8]), + Seq: binary.BigEndian.Uint32(data[8:12]), + } + i.ActualLength = (int(i.HeaderLength) + 2) * 4 + i.AuthenticationData = data[12:i.ActualLength] + i.Contents = data[:i.ActualLength] + i.Payload = data[i.ActualLength:] + p.AddLayer(i) + return p.NextDecoder(i.NextHeader) +} + +// IPSecESP is the encapsulating security payload defined in +// http://tools.ietf.org/html/rfc2406 +type IPSecESP struct { + BaseLayer + SPI, Seq uint32 + // Encrypted contains the encrypted set of bytes sent in an ESP + Encrypted []byte +} + +// LayerType returns LayerTypeIPSecESP. +func (i *IPSecESP) LayerType() gopacket.LayerType { return LayerTypeIPSecESP } + +func decodeIPSecESP(data []byte, p gopacket.PacketBuilder) error { + i := &IPSecESP{ + BaseLayer: BaseLayer{data, nil}, + SPI: binary.BigEndian.Uint32(data[:4]), + Seq: binary.BigEndian.Uint32(data[4:8]), + Encrypted: data[8:], + } + p.AddLayer(i) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/ipsec_test.go b/vendor/github.com/google/gopacket/layers/ipsec_test.go new file mode 100644 index 00000000..466646d7 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ipsec_test.go @@ -0,0 +1,154 @@ +// Copyright 2012, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" + "reflect" + "testing" +) + +// testPacketIPSecAHTransport is the packet: +// 20:45:10.325850 IP 192.168.1.1 > 192.168.1.2: AH(spi=0x00000101,seq=0x1): ICMP echo request, id 1560, seq 1, length 64 +// 0x0000: 7ec0 ffc6 48f1 1a0e 3c4e 3b3a 0800 4500 ~...H... 192.168.1.2: AH(spi=0x00000101,seq=0x1): IP 172.16.1.1 > 172.16.2.1: ICMP echo request, id 31322, seq 1, length 64 (ipip-proto-4) +// 0x0000: 7220 4d91 63c9 566c ed2d 73cd 0800 4500 r.M.c.Vl.-s...E. +// 0x0010: 0080 0000 4000 4033 b6f7 c0a8 0101 c0a8 ....@.@3........ +// 0x0020: 0102 0404 0000 0000 0101 0000 0001 cca4 ................ +// 0x0030: 01da 9eb4 fb75 10fe 5a59 4500 0054 a96f .....u..ZYE..T.o +// 0x0040: 4000 4001 3617 ac10 0101 ac10 0201 0800 @.@.6........... +// 0x0050: d75f 7a5a 0001 0741 3355 0000 0000 a9db ._zZ...A3U...... +// 0x0060: 0300 0000 0000 1011 1213 1415 1617 1819 ................ +// 0x0070: 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 .......!"#$%&'() +// 0x0080: 2a2b 2c2d 2e2f 3031 3233 3435 3637 *+,-./01234567 +var testPacketIPSecAHTunnel = []byte{ + 0x72, 0x20, 0x4d, 0x91, 0x63, 0xc9, 0x56, 0x6c, 0xed, 0x2d, 0x73, 0xcd, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x40, 0x33, 0xb6, 0xf7, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, + 0x01, 0x02, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0xcc, 0xa4, + 0x01, 0xda, 0x9e, 0xb4, 0xfb, 0x75, 0x10, 0xfe, 0x5a, 0x59, 0x45, 0x00, 0x00, 0x54, 0xa9, 0x6f, + 0x40, 0x00, 0x40, 0x01, 0x36, 0x17, 0xac, 0x10, 0x01, 0x01, 0xac, 0x10, 0x02, 0x01, 0x08, 0x00, + 0xd7, 0x5f, 0x7a, 0x5a, 0x00, 0x01, 0x07, 0x41, 0x33, 0x55, 0x00, 0x00, 0x00, 0x00, 0xa9, 0xdb, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +} + +func TestPacketIPSecAHTunnel(t *testing.T) { + p := gopacket.NewPacket(testPacketIPSecAHTunnel, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIPSecAH, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + if got, ok := p.Layer(LayerTypeIPSecAH).(*IPSecAH); ok { + want := &IPSecAH{ + Reserved: 0x0, + SPI: 0x101, + Seq: 1, + AuthenticationData: []byte{0xcc, 0xa4, 0x01, 0xda, 0x9e, 0xb4, 0xfb, 0x75, 0x10, 0xfe, 0x5a, 0x59}, + } + want.BaseLayer = BaseLayer{testPacketIPSecAHTunnel[34:58], testPacketIPSecAHTunnel[58:]} + want.NextHeader = IPProtocolIPv4 + want.HeaderLength = 0x4 + want.ActualLength = 0x18 + if !reflect.DeepEqual(want, got) { + t.Errorf("IPSecAH layer mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } +} + +func BenchmarkDecodePacketIPSecAHTunnel(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketIPSecAHTunnel, LinkTypeEthernet, gopacket.NoCopy) + } +} + +// testPacketIPSecESP is the packet: +// 04:30:37.629376 IP 190.0.0.1 > 190.0.0.2: ESP(spi=0x0000006e,seq=0x13), length 116 +// 0x0000: 0000 0000 0012 0011 434a d70a 0800 4500 ........CJ....E. +// 0x0010: 0088 0000 4000 4032 be40 be00 0001 be00 ....@.@2.@...... +// 0x0020: 0002 0000 006e 0000 0013 82f4 1077 0418 .....n.......w.. +// 0x0030: e8ce dc45 1bac 22bb daaf 2ad2 c2e8 315b ...E.."...*...1[ +// 0x0040: ce9a 39da 2aae cf43 3716 70ab 7e7c 4676 ..9.*..C7.p.~|Fv +// 0x0050: c3fc d109 c990 274d f81c 6534 9a40 a0ef ......'M..e4.@.. +// 0x0060: 46b1 7da5 05af dda8 d0ba 6e23 d1ee 1f10 F.}.......n#.... +// 0x0070: 730c 7371 03b1 445c 2f70 852f 8475 12fb s.sq..D\/p./.u.. +// 0x0080: b057 a19b a617 bae7 09ca 8836 942f 3334 .W.........6./34 +// 0x0090: 312b 96d2 a4e3 1+.... +var testPacketIPSecESP = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x11, 0x43, 0x4a, 0xd7, 0x0a, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x88, 0x00, 0x00, 0x40, 0x00, 0x40, 0x32, 0xbe, 0x40, 0xbe, 0x00, 0x00, 0x01, 0xbe, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x13, 0x82, 0xf4, 0x10, 0x77, 0x04, 0x18, + 0xe8, 0xce, 0xdc, 0x45, 0x1b, 0xac, 0x22, 0xbb, 0xda, 0xaf, 0x2a, 0xd2, 0xc2, 0xe8, 0x31, 0x5b, + 0xce, 0x9a, 0x39, 0xda, 0x2a, 0xae, 0xcf, 0x43, 0x37, 0x16, 0x70, 0xab, 0x7e, 0x7c, 0x46, 0x76, + 0xc3, 0xfc, 0xd1, 0x09, 0xc9, 0x90, 0x27, 0x4d, 0xf8, 0x1c, 0x65, 0x34, 0x9a, 0x40, 0xa0, 0xef, + 0x46, 0xb1, 0x7d, 0xa5, 0x05, 0xaf, 0xdd, 0xa8, 0xd0, 0xba, 0x6e, 0x23, 0xd1, 0xee, 0x1f, 0x10, + 0x73, 0x0c, 0x73, 0x71, 0x03, 0xb1, 0x44, 0x5c, 0x2f, 0x70, 0x85, 0x2f, 0x84, 0x75, 0x12, 0xfb, + 0xb0, 0x57, 0xa1, 0x9b, 0xa6, 0x17, 0xba, 0xe7, 0x09, 0xca, 0x88, 0x36, 0x94, 0x2f, 0x33, 0x34, + 0x31, 0x2b, 0x96, 0xd2, 0xa4, 0xe3, +} + +func TestPacketIPSecESP(t *testing.T) { + p := gopacket.NewPacket(testPacketIPSecESP, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIPSecESP}, t) +} + +func BenchmarkDecodePacketIPSecESP(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketIPSecESP, LinkTypeEthernet, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/layertypes.go b/vendor/github.com/google/gopacket/layers/layertypes.go new file mode 100644 index 00000000..bc3b9d59 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/layertypes.go @@ -0,0 +1,175 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" +) + +var ( + LayerTypeARP = gopacket.RegisterLayerType(10, gopacket.LayerTypeMetadata{Name: "ARP", Decoder: gopacket.DecodeFunc(decodeARP)}) + LayerTypeCiscoDiscovery = gopacket.RegisterLayerType(11, gopacket.LayerTypeMetadata{Name: "CiscoDiscovery", Decoder: gopacket.DecodeFunc(decodeCiscoDiscovery)}) + LayerTypeEthernetCTP = gopacket.RegisterLayerType(12, gopacket.LayerTypeMetadata{Name: "EthernetCTP", Decoder: gopacket.DecodeFunc(decodeEthernetCTP)}) + LayerTypeEthernetCTPForwardData = gopacket.RegisterLayerType(13, gopacket.LayerTypeMetadata{Name: "EthernetCTPForwardData", Decoder: nil}) + LayerTypeEthernetCTPReply = gopacket.RegisterLayerType(14, gopacket.LayerTypeMetadata{Name: "EthernetCTPReply", Decoder: nil}) + LayerTypeDot1Q = gopacket.RegisterLayerType(15, gopacket.LayerTypeMetadata{Name: "Dot1Q", Decoder: gopacket.DecodeFunc(decodeDot1Q)}) + LayerTypeEtherIP = gopacket.RegisterLayerType(16, gopacket.LayerTypeMetadata{Name: "EtherIP", Decoder: gopacket.DecodeFunc(decodeEtherIP)}) + LayerTypeEthernet = gopacket.RegisterLayerType(17, gopacket.LayerTypeMetadata{Name: "Ethernet", Decoder: gopacket.DecodeFunc(decodeEthernet)}) + LayerTypeGRE = gopacket.RegisterLayerType(18, gopacket.LayerTypeMetadata{Name: "GRE", Decoder: gopacket.DecodeFunc(decodeGRE)}) + LayerTypeICMPv4 = gopacket.RegisterLayerType(19, gopacket.LayerTypeMetadata{Name: "ICMPv4", Decoder: gopacket.DecodeFunc(decodeICMPv4)}) + LayerTypeIPv4 = gopacket.RegisterLayerType(20, gopacket.LayerTypeMetadata{Name: "IPv4", Decoder: gopacket.DecodeFunc(decodeIPv4)}) + LayerTypeIPv6 = gopacket.RegisterLayerType(21, gopacket.LayerTypeMetadata{Name: "IPv6", Decoder: gopacket.DecodeFunc(decodeIPv6)}) + LayerTypeLLC = gopacket.RegisterLayerType(22, gopacket.LayerTypeMetadata{Name: "LLC", Decoder: gopacket.DecodeFunc(decodeLLC)}) + LayerTypeSNAP = gopacket.RegisterLayerType(23, gopacket.LayerTypeMetadata{Name: "SNAP", Decoder: gopacket.DecodeFunc(decodeSNAP)}) + LayerTypeMPLS = gopacket.RegisterLayerType(24, gopacket.LayerTypeMetadata{Name: "MPLS", Decoder: gopacket.DecodeFunc(decodeMPLS)}) + LayerTypePPP = gopacket.RegisterLayerType(25, gopacket.LayerTypeMetadata{Name: "PPP", Decoder: gopacket.DecodeFunc(decodePPP)}) + LayerTypePPPoE = gopacket.RegisterLayerType(26, gopacket.LayerTypeMetadata{Name: "PPPoE", Decoder: gopacket.DecodeFunc(decodePPPoE)}) + LayerTypeRUDP = gopacket.RegisterLayerType(27, gopacket.LayerTypeMetadata{Name: "RUDP", Decoder: gopacket.DecodeFunc(decodeRUDP)}) + LayerTypeSCTP = gopacket.RegisterLayerType(28, gopacket.LayerTypeMetadata{Name: "SCTP", Decoder: gopacket.DecodeFunc(decodeSCTP)}) + LayerTypeSCTPUnknownChunkType = gopacket.RegisterLayerType(29, gopacket.LayerTypeMetadata{Name: "SCTPUnknownChunkType", Decoder: nil}) + LayerTypeSCTPData = gopacket.RegisterLayerType(30, gopacket.LayerTypeMetadata{Name: "SCTPData", Decoder: nil}) + LayerTypeSCTPInit = gopacket.RegisterLayerType(31, gopacket.LayerTypeMetadata{Name: "SCTPInit", Decoder: nil}) + LayerTypeSCTPSack = gopacket.RegisterLayerType(32, gopacket.LayerTypeMetadata{Name: "SCTPSack", Decoder: nil}) + LayerTypeSCTPHeartbeat = gopacket.RegisterLayerType(33, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeat", Decoder: nil}) + LayerTypeSCTPError = gopacket.RegisterLayerType(34, gopacket.LayerTypeMetadata{Name: "SCTPError", Decoder: nil}) + LayerTypeSCTPShutdown = gopacket.RegisterLayerType(35, gopacket.LayerTypeMetadata{Name: "SCTPShutdown", Decoder: nil}) + LayerTypeSCTPShutdownAck = gopacket.RegisterLayerType(36, gopacket.LayerTypeMetadata{Name: "SCTPShutdownAck", Decoder: nil}) + LayerTypeSCTPCookieEcho = gopacket.RegisterLayerType(37, gopacket.LayerTypeMetadata{Name: "SCTPCookieEcho", Decoder: nil}) + LayerTypeSCTPEmptyLayer = gopacket.RegisterLayerType(38, gopacket.LayerTypeMetadata{Name: "SCTPEmptyLayer", Decoder: nil}) + LayerTypeSCTPInitAck = gopacket.RegisterLayerType(39, gopacket.LayerTypeMetadata{Name: "SCTPInitAck", Decoder: nil}) + LayerTypeSCTPHeartbeatAck = gopacket.RegisterLayerType(40, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeatAck", Decoder: nil}) + LayerTypeSCTPAbort = gopacket.RegisterLayerType(41, gopacket.LayerTypeMetadata{Name: "SCTPAbort", Decoder: nil}) + LayerTypeSCTPShutdownComplete = gopacket.RegisterLayerType(42, gopacket.LayerTypeMetadata{Name: "SCTPShutdownComplete", Decoder: nil}) + LayerTypeSCTPCookieAck = gopacket.RegisterLayerType(43, gopacket.LayerTypeMetadata{Name: "SCTPCookieAck", Decoder: nil}) + LayerTypeTCP = gopacket.RegisterLayerType(44, gopacket.LayerTypeMetadata{Name: "TCP", Decoder: gopacket.DecodeFunc(decodeTCP)}) + LayerTypeUDP = gopacket.RegisterLayerType(45, gopacket.LayerTypeMetadata{Name: "UDP", Decoder: gopacket.DecodeFunc(decodeUDP)}) + LayerTypeIPv6HopByHop = gopacket.RegisterLayerType(46, gopacket.LayerTypeMetadata{Name: "IPv6HopByHop", Decoder: gopacket.DecodeFunc(decodeIPv6HopByHop)}) + LayerTypeIPv6Routing = gopacket.RegisterLayerType(47, gopacket.LayerTypeMetadata{Name: "IPv6Routing", Decoder: gopacket.DecodeFunc(decodeIPv6Routing)}) + LayerTypeIPv6Fragment = gopacket.RegisterLayerType(48, gopacket.LayerTypeMetadata{Name: "IPv6Fragment", Decoder: gopacket.DecodeFunc(decodeIPv6Fragment)}) + LayerTypeIPv6Destination = gopacket.RegisterLayerType(49, gopacket.LayerTypeMetadata{Name: "IPv6Destination", Decoder: gopacket.DecodeFunc(decodeIPv6Destination)}) + LayerTypeIPSecAH = gopacket.RegisterLayerType(50, gopacket.LayerTypeMetadata{Name: "IPSecAH", Decoder: gopacket.DecodeFunc(decodeIPSecAH)}) + LayerTypeIPSecESP = gopacket.RegisterLayerType(51, gopacket.LayerTypeMetadata{Name: "IPSecESP", Decoder: gopacket.DecodeFunc(decodeIPSecESP)}) + LayerTypeUDPLite = gopacket.RegisterLayerType(52, gopacket.LayerTypeMetadata{Name: "UDPLite", Decoder: gopacket.DecodeFunc(decodeUDPLite)}) + LayerTypeFDDI = gopacket.RegisterLayerType(53, gopacket.LayerTypeMetadata{Name: "FDDI", Decoder: gopacket.DecodeFunc(decodeFDDI)}) + LayerTypeLoopback = gopacket.RegisterLayerType(54, gopacket.LayerTypeMetadata{Name: "Loopback", Decoder: gopacket.DecodeFunc(decodeLoopback)}) + LayerTypeEAP = gopacket.RegisterLayerType(55, gopacket.LayerTypeMetadata{Name: "EAP", Decoder: gopacket.DecodeFunc(decodeEAP)}) + LayerTypeEAPOL = gopacket.RegisterLayerType(56, gopacket.LayerTypeMetadata{Name: "EAPOL", Decoder: gopacket.DecodeFunc(decodeEAPOL)}) + LayerTypeICMPv6 = gopacket.RegisterLayerType(57, gopacket.LayerTypeMetadata{Name: "ICMPv6", Decoder: gopacket.DecodeFunc(decodeICMPv6)}) + LayerTypeLinkLayerDiscovery = gopacket.RegisterLayerType(58, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscovery", Decoder: gopacket.DecodeFunc(decodeLinkLayerDiscovery)}) + LayerTypeCiscoDiscoveryInfo = gopacket.RegisterLayerType(59, gopacket.LayerTypeMetadata{Name: "CiscoDiscoveryInfo", Decoder: gopacket.DecodeFunc(decodeCiscoDiscoveryInfo)}) + LayerTypeLinkLayerDiscoveryInfo = gopacket.RegisterLayerType(60, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscoveryInfo", Decoder: nil}) + LayerTypeNortelDiscovery = gopacket.RegisterLayerType(61, gopacket.LayerTypeMetadata{Name: "NortelDiscovery", Decoder: gopacket.DecodeFunc(decodeNortelDiscovery)}) + LayerTypeIGMP = gopacket.RegisterLayerType(62, gopacket.LayerTypeMetadata{Name: "IGMP", Decoder: gopacket.DecodeFunc(decodeIGMP)}) + LayerTypePFLog = gopacket.RegisterLayerType(63, gopacket.LayerTypeMetadata{Name: "PFLog", Decoder: gopacket.DecodeFunc(decodePFLog)}) + LayerTypeRadioTap = gopacket.RegisterLayerType(64, gopacket.LayerTypeMetadata{Name: "RadioTap", Decoder: gopacket.DecodeFunc(decodeRadioTap)}) + LayerTypeDot11 = gopacket.RegisterLayerType(65, gopacket.LayerTypeMetadata{Name: "Dot11", Decoder: gopacket.DecodeFunc(decodeDot11)}) + LayerTypeDot11Ctrl = gopacket.RegisterLayerType(66, gopacket.LayerTypeMetadata{Name: "Dot11Ctrl", Decoder: gopacket.DecodeFunc(decodeDot11Ctrl)}) + LayerTypeDot11Data = gopacket.RegisterLayerType(67, gopacket.LayerTypeMetadata{Name: "Dot11Data", Decoder: gopacket.DecodeFunc(decodeDot11Data)}) + LayerTypeDot11DataCFAck = gopacket.RegisterLayerType(68, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)}) + LayerTypeDot11DataCFPoll = gopacket.RegisterLayerType(69, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)}) + LayerTypeDot11DataCFAckPoll = gopacket.RegisterLayerType(70, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)}) + LayerTypeDot11DataNull = gopacket.RegisterLayerType(71, gopacket.LayerTypeMetadata{Name: "Dot11DataNull", Decoder: gopacket.DecodeFunc(decodeDot11DataNull)}) + LayerTypeDot11DataCFAckNoData = gopacket.RegisterLayerType(72, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)}) + LayerTypeDot11DataCFPollNoData = gopacket.RegisterLayerType(73, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)}) + LayerTypeDot11DataCFAckPollNoData = gopacket.RegisterLayerType(74, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)}) + LayerTypeDot11DataQOSData = gopacket.RegisterLayerType(75, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSData", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSData)}) + LayerTypeDot11DataQOSDataCFAck = gopacket.RegisterLayerType(76, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck)}) + LayerTypeDot11DataQOSDataCFPoll = gopacket.RegisterLayerType(77, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll)}) + LayerTypeDot11DataQOSDataCFAckPoll = gopacket.RegisterLayerType(78, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll)}) + LayerTypeDot11DataQOSNull = gopacket.RegisterLayerType(79, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSNull", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSNull)}) + LayerTypeDot11DataQOSCFPollNoData = gopacket.RegisterLayerType(80, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData)}) + LayerTypeDot11DataQOSCFAckPollNoData = gopacket.RegisterLayerType(81, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData)}) + LayerTypeDot11InformationElement = gopacket.RegisterLayerType(82, gopacket.LayerTypeMetadata{Name: "Dot11InformationElement", Decoder: gopacket.DecodeFunc(decodeDot11InformationElement)}) + LayerTypeDot11CtrlCTS = gopacket.RegisterLayerType(83, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCTS)}) + LayerTypeDot11CtrlRTS = gopacket.RegisterLayerType(84, gopacket.LayerTypeMetadata{Name: "Dot11CtrlRTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlRTS)}) + LayerTypeDot11CtrlBlockAckReq = gopacket.RegisterLayerType(85, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAckReq", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq)}) + LayerTypeDot11CtrlBlockAck = gopacket.RegisterLayerType(86, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAck)}) + LayerTypeDot11CtrlPowersavePoll = gopacket.RegisterLayerType(87, gopacket.LayerTypeMetadata{Name: "Dot11CtrlPowersavePoll", Decoder: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll)}) + LayerTypeDot11CtrlAck = gopacket.RegisterLayerType(88, gopacket.LayerTypeMetadata{Name: "Dot11CtrlAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlAck)}) + LayerTypeDot11CtrlCFEnd = gopacket.RegisterLayerType(89, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEnd", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEnd)}) + LayerTypeDot11CtrlCFEndAck = gopacket.RegisterLayerType(90, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEndAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck)}) + LayerTypeDot11MgmtAssociationReq = gopacket.RegisterLayerType(91, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq)}) + LayerTypeDot11MgmtAssociationResp = gopacket.RegisterLayerType(92, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp)}) + LayerTypeDot11MgmtReassociationReq = gopacket.RegisterLayerType(93, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq)}) + LayerTypeDot11MgmtReassociationResp = gopacket.RegisterLayerType(94, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp)}) + LayerTypeDot11MgmtProbeReq = gopacket.RegisterLayerType(95, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeReq)}) + LayerTypeDot11MgmtProbeResp = gopacket.RegisterLayerType(96, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeResp)}) + LayerTypeDot11MgmtMeasurementPilot = gopacket.RegisterLayerType(97, gopacket.LayerTypeMetadata{Name: "Dot11MgmtMeasurementPilot", Decoder: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot)}) + LayerTypeDot11MgmtBeacon = gopacket.RegisterLayerType(98, gopacket.LayerTypeMetadata{Name: "Dot11MgmtBeacon", Decoder: gopacket.DecodeFunc(decodeDot11MgmtBeacon)}) + LayerTypeDot11MgmtATIM = gopacket.RegisterLayerType(99, gopacket.LayerTypeMetadata{Name: "Dot11MgmtATIM", Decoder: gopacket.DecodeFunc(decodeDot11MgmtATIM)}) + LayerTypeDot11MgmtDisassociation = gopacket.RegisterLayerType(100, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDisassociation", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDisassociation)}) + LayerTypeDot11MgmtAuthentication = gopacket.RegisterLayerType(101, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAuthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAuthentication)}) + LayerTypeDot11MgmtDeauthentication = gopacket.RegisterLayerType(102, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDeauthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication)}) + LayerTypeDot11MgmtAction = gopacket.RegisterLayerType(103, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAction", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAction)}) + LayerTypeDot11MgmtActionNoAck = gopacket.RegisterLayerType(104, gopacket.LayerTypeMetadata{Name: "Dot11MgmtActionNoAck", Decoder: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck)}) + LayerTypeDot11MgmtArubaWLAN = gopacket.RegisterLayerType(105, gopacket.LayerTypeMetadata{Name: "Dot11MgmtArubaWLAN", Decoder: gopacket.DecodeFunc(decodeDot11MgmtArubaWLAN)}) + LayerTypeDot11WEP = gopacket.RegisterLayerType(106, gopacket.LayerTypeMetadata{Name: "Dot11WEP", Decoder: gopacket.DecodeFunc(decodeDot11WEP)}) + LayerTypeDNS = gopacket.RegisterLayerType(107, gopacket.LayerTypeMetadata{Name: "DNS", Decoder: gopacket.DecodeFunc(decodeDNS)}) + LayerTypeUSB = gopacket.RegisterLayerType(108, gopacket.LayerTypeMetadata{Name: "USB", Decoder: gopacket.DecodeFunc(decodeUSB)}) + LayerTypeUSBRequestBlockSetup = gopacket.RegisterLayerType(109, gopacket.LayerTypeMetadata{Name: "USBRequestBlockSetup", Decoder: gopacket.DecodeFunc(decodeUSBRequestBlockSetup)}) + LayerTypeUSBControl = gopacket.RegisterLayerType(110, gopacket.LayerTypeMetadata{Name: "USBControl", Decoder: gopacket.DecodeFunc(decodeUSBControl)}) + LayerTypeUSBInterrupt = gopacket.RegisterLayerType(111, gopacket.LayerTypeMetadata{Name: "USBInterrupt", Decoder: gopacket.DecodeFunc(decodeUSBInterrupt)}) + LayerTypeUSBBulk = gopacket.RegisterLayerType(112, gopacket.LayerTypeMetadata{Name: "USBBulk", Decoder: gopacket.DecodeFunc(decodeUSBBulk)}) + LayerTypeLinuxSLL = gopacket.RegisterLayerType(113, gopacket.LayerTypeMetadata{Name: "Linux SLL", Decoder: gopacket.DecodeFunc(decodeLinuxSLL)}) + LayerTypeSFlow = gopacket.RegisterLayerType(114, gopacket.LayerTypeMetadata{Name: "SFlow", Decoder: gopacket.DecodeFunc(decodeSFlow)}) + LayerTypePrismHeader = gopacket.RegisterLayerType(115, gopacket.LayerTypeMetadata{Name: "Prism monitor mode header", Decoder: gopacket.DecodeFunc(decodePrismHeader)}) + LayerTypeVXLAN = gopacket.RegisterLayerType(116, gopacket.LayerTypeMetadata{Name: "VXLAN", Decoder: gopacket.DecodeFunc(decodeVXLAN)}) + LayerTypeNTP = gopacket.RegisterLayerType(117, gopacket.LayerTypeMetadata{Name: "NTP", Decoder: gopacket.DecodeFunc(decodeNTP)}) + LayerTypeDHCPv4 = gopacket.RegisterLayerType(118, gopacket.LayerTypeMetadata{Name: "DHCPv4", Decoder: gopacket.DecodeFunc(decodeDHCPv4)}) + LayerTypeVRRP = gopacket.RegisterLayerType(119, gopacket.LayerTypeMetadata{Name: "VRRP", Decoder: gopacket.DecodeFunc(decodeVRRP)}) + LayerTypeGeneve = gopacket.RegisterLayerType(120, gopacket.LayerTypeMetadata{Name: "Geneve", Decoder: gopacket.DecodeFunc(decodeGeneve)}) + LayerTypeSTP = gopacket.RegisterLayerType(121, gopacket.LayerTypeMetadata{Name: "STP", Decoder: gopacket.DecodeFunc(decodeSTP)}) +) + +var ( + // LayerClassIPNetwork contains TCP/IP network layer types. + LayerClassIPNetwork = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeIPv4, + LayerTypeIPv6, + }) + // LayerClassIPTransport contains TCP/IP transport layer types. + LayerClassIPTransport = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeTCP, + LayerTypeUDP, + LayerTypeSCTP, + }) + // LayerClassIPControl contains TCP/IP control protocols. + LayerClassIPControl = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeICMPv4, + LayerTypeICMPv6, + }) + // LayerClassSCTPChunk contains SCTP chunk types (not the top-level SCTP + // layer). + LayerClassSCTPChunk = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeSCTPUnknownChunkType, + LayerTypeSCTPData, + LayerTypeSCTPInit, + LayerTypeSCTPSack, + LayerTypeSCTPHeartbeat, + LayerTypeSCTPError, + LayerTypeSCTPShutdown, + LayerTypeSCTPShutdownAck, + LayerTypeSCTPCookieEcho, + LayerTypeSCTPEmptyLayer, + LayerTypeSCTPInitAck, + LayerTypeSCTPHeartbeatAck, + LayerTypeSCTPAbort, + LayerTypeSCTPShutdownComplete, + LayerTypeSCTPCookieAck, + }) + // LayerClassIPv6Extension contains IPv6 extension headers. + LayerClassIPv6Extension = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeIPv6HopByHop, + LayerTypeIPv6Routing, + LayerTypeIPv6Fragment, + LayerTypeIPv6Destination, + }) + LayerClassIPSec = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeIPSecAH, + LayerTypeIPSecESP, + }) +) diff --git a/vendor/github.com/google/gopacket/layers/linux_sll.go b/vendor/github.com/google/gopacket/layers/linux_sll.go new file mode 100644 index 00000000..b1860536 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/linux_sll.go @@ -0,0 +1,96 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +type LinuxSLLPacketType uint16 + +const ( + LinuxSLLPacketTypeHost LinuxSLLPacketType = 0 // To us + LinuxSLLPacketTypeBroadcast LinuxSLLPacketType = 1 // To all + LinuxSLLPacketTypeMulticast LinuxSLLPacketType = 2 // To group + LinuxSLLPacketTypeOtherhost LinuxSLLPacketType = 3 // To someone else + LinuxSLLPacketTypeOutgoing LinuxSLLPacketType = 4 // Outgoing of any type + // These ones are invisible by user level + LinuxSLLPacketTypeLoopback LinuxSLLPacketType = 5 // MC/BRD frame looped back + LinuxSLLPacketTypeFastroute LinuxSLLPacketType = 6 // Fastrouted frame +) + +func (l LinuxSLLPacketType) String() string { + switch l { + case LinuxSLLPacketTypeHost: + return "host" + case LinuxSLLPacketTypeBroadcast: + return "broadcast" + case LinuxSLLPacketTypeMulticast: + return "multicast" + case LinuxSLLPacketTypeOtherhost: + return "otherhost" + case LinuxSLLPacketTypeOutgoing: + return "outgoing" + case LinuxSLLPacketTypeLoopback: + return "loopback" + case LinuxSLLPacketTypeFastroute: + return "fastroute" + } + return fmt.Sprintf("Unknown(%d)", int(l)) +} + +type LinuxSLL struct { + BaseLayer + PacketType LinuxSLLPacketType + AddrLen uint16 + Addr net.HardwareAddr + EthernetType EthernetType +} + +// LayerType returns LayerTypeLinuxSLL. +func (sll *LinuxSLL) LayerType() gopacket.LayerType { return LayerTypeLinuxSLL } + +func (sll *LinuxSLL) CanDecode() gopacket.LayerClass { + return LayerTypeLinuxSLL +} + +func (sll *LinuxSLL) LinkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointMAC, sll.Addr, nil) +} + +func (sll *LinuxSLL) NextLayerType() gopacket.LayerType { + return sll.EthernetType.LayerType() +} + +func (sll *LinuxSLL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 16 { + return errors.New("Linux SLL packet too small") + } + sll.PacketType = LinuxSLLPacketType(binary.BigEndian.Uint16(data[0:2])) + sll.AddrLen = binary.BigEndian.Uint16(data[4:6]) + + sll.Addr = net.HardwareAddr(data[6 : sll.AddrLen+6]) + sll.EthernetType = EthernetType(binary.BigEndian.Uint16(data[14:16])) + sll.BaseLayer = BaseLayer{data[:16], data[16:]} + + return nil +} + +func decodeLinuxSLL(data []byte, p gopacket.PacketBuilder) error { + sll := &LinuxSLL{} + if err := sll.DecodeFromBytes(data, p); err != nil { + return err + } + p.AddLayer(sll) + p.SetLinkLayer(sll) + return p.NextDecoder(sll.EthernetType) +} diff --git a/vendor/github.com/google/gopacket/layers/llc.go b/vendor/github.com/google/gopacket/layers/llc.go new file mode 100644 index 00000000..59453fee --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/llc.go @@ -0,0 +1,146 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +// LLC is the layer used for 802.2 Logical Link Control headers. +// See http://standards.ieee.org/getieee802/download/802.2-1998.pdf +type LLC struct { + BaseLayer + DSAP uint8 + IG bool // true means group, false means individual + SSAP uint8 + CR bool // true means response, false means command + Control uint16 +} + +// LayerType returns gopacket.LayerTypeLLC. +func (l *LLC) LayerType() gopacket.LayerType { return LayerTypeLLC } + +// SNAP is used inside LLC. See +// http://standards.ieee.org/getieee802/download/802-2001.pdf. +// From http://en.wikipedia.org/wiki/Subnetwork_Access_Protocol: +// "[T]he Subnetwork Access Protocol (SNAP) is a mechanism for multiplexing, +// on networks using IEEE 802.2 LLC, more protocols than can be distinguished +// by the 8-bit 802.2 Service Access Point (SAP) fields." +type SNAP struct { + BaseLayer + OrganizationalCode []byte + Type EthernetType +} + +// LayerType returns gopacket.LayerTypeSNAP. +func (s *SNAP) LayerType() gopacket.LayerType { return LayerTypeSNAP } + +func decodeLLC(data []byte, p gopacket.PacketBuilder) error { + l := &LLC{ + DSAP: data[0] & 0xFE, + IG: data[0]&0x1 != 0, + SSAP: data[1] & 0xFE, + CR: data[1]&0x1 != 0, + Control: uint16(data[2]), + } + if l.Control&0x1 == 0 || l.Control&0x3 == 0x1 { + l.Control = l.Control<<8 | uint16(data[3]) + l.Contents = data[:4] + l.Payload = data[4:] + } else { + l.Contents = data[:3] + l.Payload = data[3:] + } + p.AddLayer(l) + switch { + case l.DSAP == 0xAA && l.SSAP == 0xAA: + return p.NextDecoder(LayerTypeSNAP) + case l.DSAP == 0x42 && l.SSAP == 0x42: + return p.NextDecoder(LayerTypeSTP) + } + return p.NextDecoder(gopacket.DecodeUnknown) +} + +func decodeSNAP(data []byte, p gopacket.PacketBuilder) error { + s := &SNAP{ + OrganizationalCode: data[:3], + Type: EthernetType(binary.BigEndian.Uint16(data[3:5])), + BaseLayer: BaseLayer{data[:5], data[5:]}, + } + p.AddLayer(s) + // BUG(gconnell): When decoding SNAP, we treat the SNAP type as an Ethernet + // type. This may not actually be an ethernet type in all cases, + // depending on the organizational code. Right now, we don't check. + return p.NextDecoder(s.Type) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (l *LLC) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var ig_flag, cr_flag byte + var length int + + if l.Control&0xFF00 != 0 { + length = 4 + } else { + length = 3 + } + + if l.DSAP&0x1 != 0 { + return errors.New("DSAP value invalid, should not include IG flag bit") + } + + if l.SSAP&0x1 != 0 { + return errors.New("SSAP value invalid, should not include CR flag bit") + } + + if buf, err := b.PrependBytes(length); err != nil { + return err + } else { + ig_flag = 0 + if l.IG { + ig_flag = 0x1 + } + + cr_flag = 0 + if l.CR { + cr_flag = 0x1 + } + + buf[0] = l.DSAP + ig_flag + buf[1] = l.SSAP + cr_flag + + if length == 4 { + buf[2] = uint8(l.Control >> 8) + buf[3] = uint8(l.Control) + } else { + buf[2] = uint8(l.Control) + } + } + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (s *SNAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if buf, err := b.PrependBytes(5); err != nil { + return err + } else { + buf[0] = s.OrganizationalCode[0] + buf[1] = s.OrganizationalCode[1] + buf[2] = s.OrganizationalCode[2] + binary.BigEndian.PutUint16(buf[3:5], uint16(s.Type)) + } + + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/lldp.go b/vendor/github.com/google/gopacket/layers/lldp.go new file mode 100644 index 00000000..92ec7daf --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/lldp.go @@ -0,0 +1,1530 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// LLDPTLVType is the type of each TLV value in a LinkLayerDiscovery packet. +type LLDPTLVType byte + +const ( + LLDPTLVEnd LLDPTLVType = 0 + LLDPTLVChassisID LLDPTLVType = 1 + LLDPTLVPortID LLDPTLVType = 2 + LLDPTLVTTL LLDPTLVType = 3 + LLDPTLVPortDescription LLDPTLVType = 4 + LLDPTLVSysName LLDPTLVType = 5 + LLDPTLVSysDescription LLDPTLVType = 6 + LLDPTLVSysCapabilities LLDPTLVType = 7 + LLDPTLVMgmtAddress LLDPTLVType = 8 + LLDPTLVOrgSpecific LLDPTLVType = 127 +) + +// LinkLayerDiscoveryValue is a TLV value inside a LinkLayerDiscovery packet layer. +type LinkLayerDiscoveryValue struct { + Type LLDPTLVType + Length uint16 + Value []byte +} + +// LLDPChassisIDSubType specifies the value type for a single LLDPChassisID.ID +type LLDPChassisIDSubType byte + +// LLDP Chassis Types +const ( + LLDPChassisIDSubTypeReserved LLDPChassisIDSubType = 0 + LLDPChassisIDSubTypeChassisComp LLDPChassisIDSubType = 1 + LLDPChassisIDSubtypeIfaceAlias LLDPChassisIDSubType = 2 + LLDPChassisIDSubTypePortComp LLDPChassisIDSubType = 3 + LLDPChassisIDSubTypeMACAddr LLDPChassisIDSubType = 4 + LLDPChassisIDSubTypeNetworkAddr LLDPChassisIDSubType = 5 + LLDPChassisIDSubtypeIfaceName LLDPChassisIDSubType = 6 + LLDPChassisIDSubTypeLocal LLDPChassisIDSubType = 7 +) + +type LLDPChassisID struct { + Subtype LLDPChassisIDSubType + ID []byte +} + +// LLDPPortIDSubType specifies the value type for a single LLDPPortID.ID +type LLDPPortIDSubType byte + +// LLDP PortID types +const ( + LLDPPortIDSubtypeReserved LLDPPortIDSubType = 0 + LLDPPortIDSubtypeIfaceAlias LLDPPortIDSubType = 1 + LLDPPortIDSubtypePortComp LLDPPortIDSubType = 2 + LLDPPortIDSubtypeMACAddr LLDPPortIDSubType = 3 + LLDPPortIDSubtypeNetworkAddr LLDPPortIDSubType = 4 + LLDPPortIDSubtypeIfaceName LLDPPortIDSubType = 5 + LLDPPortIDSubtypeAgentCircuitID LLDPPortIDSubType = 6 + LLDPPortIDSubtypeLocal LLDPPortIDSubType = 7 +) + +type LLDPPortID struct { + Subtype LLDPPortIDSubType + ID []byte +} + +// LinkLayerDiscovery is a packet layer containing the LinkLayer Discovery Protocol. +// See http:http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf +// ChassisID, PortID and TTL are mandatory TLV's. Other values can be decoded +// with DecodeValues() +type LinkLayerDiscovery struct { + BaseLayer + ChassisID LLDPChassisID + PortID LLDPPortID + TTL uint16 + Values []LinkLayerDiscoveryValue +} + +type IEEEOUI uint32 + +// http://standards.ieee.org/develop/regauth/oui/oui.txt +const ( + IEEEOUI8021 IEEEOUI = 0x0080c2 + IEEEOUI8023 IEEEOUI = 0x00120f + IEEEOUI80211 IEEEOUI = 0x000fac + IEEEOUI8021Qbg IEEEOUI = 0x0013BF + IEEEOUICisco2 IEEEOUI = 0x000142 + IEEEOUIMedia IEEEOUI = 0x0012bb // TR-41 + IEEEOUIProfinet IEEEOUI = 0x000ecf + IEEEOUIDCBX IEEEOUI = 0x001b21 +) + +// LLDPOrgSpecificTLV is an Organisation-specific TLV +type LLDPOrgSpecificTLV struct { + OUI IEEEOUI + SubType uint8 + Info []byte +} + +// LLDPCapabilities Types +const ( + LLDPCapsOther uint16 = 1 << 0 + LLDPCapsRepeater uint16 = 1 << 1 + LLDPCapsBridge uint16 = 1 << 2 + LLDPCapsWLANAP uint16 = 1 << 3 + LLDPCapsRouter uint16 = 1 << 4 + LLDPCapsPhone uint16 = 1 << 5 + LLDPCapsDocSis uint16 = 1 << 6 + LLDPCapsStationOnly uint16 = 1 << 7 + LLDPCapsCVLAN uint16 = 1 << 8 + LLDPCapsSVLAN uint16 = 1 << 9 + LLDPCapsTmpr uint16 = 1 << 10 +) + +// LLDPCapabilities represents the capabilities of a device +type LLDPCapabilities struct { + Other bool + Repeater bool + Bridge bool + WLANAP bool + Router bool + Phone bool + DocSis bool + StationOnly bool + CVLAN bool + SVLAN bool + TMPR bool +} + +type LLDPSysCapabilities struct { + SystemCap LLDPCapabilities + EnabledCap LLDPCapabilities +} + +type IANAAddressFamily byte + +// LLDP Management Address Subtypes +// http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml +const ( + IANAAddressFamilyReserved IANAAddressFamily = 0 + IANAAddressFamilyIPV4 IANAAddressFamily = 1 + IANAAddressFamilyIPV6 IANAAddressFamily = 2 + IANAAddressFamilyNSAP IANAAddressFamily = 3 + IANAAddressFamilyHDLC IANAAddressFamily = 4 + IANAAddressFamilyBBN1822 IANAAddressFamily = 5 + IANAAddressFamily802 IANAAddressFamily = 6 + IANAAddressFamilyE163 IANAAddressFamily = 7 + IANAAddressFamilyE164 IANAAddressFamily = 8 + IANAAddressFamilyF69 IANAAddressFamily = 9 + IANAAddressFamilyX121 IANAAddressFamily = 10 + IANAAddressFamilyIPX IANAAddressFamily = 11 + IANAAddressFamilyAtalk IANAAddressFamily = 12 + IANAAddressFamilyDecnet IANAAddressFamily = 13 + IANAAddressFamilyBanyan IANAAddressFamily = 14 + IANAAddressFamilyE164NSAP IANAAddressFamily = 15 + IANAAddressFamilyDNS IANAAddressFamily = 16 + IANAAddressFamilyDistname IANAAddressFamily = 17 + IANAAddressFamilyASNumber IANAAddressFamily = 18 + IANAAddressFamilyXTPIPV4 IANAAddressFamily = 19 + IANAAddressFamilyXTPIPV6 IANAAddressFamily = 20 + IANAAddressFamilyXTP IANAAddressFamily = 21 + IANAAddressFamilyFcWWPN IANAAddressFamily = 22 + IANAAddressFamilyFcWWNN IANAAddressFamily = 23 + IANAAddressFamilyGWID IANAAddressFamily = 24 + IANAAddressFamilyL2VPN IANAAddressFamily = 25 +) + +type LLDPInterfaceSubtype byte + +// LLDP Interface Subtypes +const ( + LLDPInterfaceSubtypeUnknown LLDPInterfaceSubtype = 1 + LLDPInterfaceSubtypeifIndex LLDPInterfaceSubtype = 2 + LLDPInterfaceSubtypeSysPort LLDPInterfaceSubtype = 3 +) + +type LLDPMgmtAddress struct { + Subtype IANAAddressFamily + Address []byte + InterfaceSubtype LLDPInterfaceSubtype + InterfaceNumber uint32 + OID string +} + +// LinkLayerDiscoveryInfo represents the decoded details for a set of LinkLayerDiscoveryValues +// Organisation-specific TLV's can be decoded using the various Decode() methods +type LinkLayerDiscoveryInfo struct { + BaseLayer + PortDescription string + SysName string + SysDescription string + SysCapabilities LLDPSysCapabilities + MgmtAddress LLDPMgmtAddress + OrgTLVs []LLDPOrgSpecificTLV // Private TLVs + Unknown []LinkLayerDiscoveryValue // undecoded TLVs +} + +/// IEEE 802.1 TLV Subtypes +const ( + LLDP8021SubtypePortVLANID uint8 = 1 + LLDP8021SubtypeProtocolVLANID uint8 = 2 + LLDP8021SubtypeVLANName uint8 = 3 + LLDP8021SubtypeProtocolIdentity uint8 = 4 + LLDP8021SubtypeVDIUsageDigest uint8 = 5 + LLDP8021SubtypeManagementVID uint8 = 6 + LLDP8021SubtypeLinkAggregation uint8 = 7 +) + +// VLAN Port Protocol ID options +const ( + LLDPProtocolVLANIDCapability byte = 1 << 1 + LLDPProtocolVLANIDStatus byte = 1 << 2 +) + +type PortProtocolVLANID struct { + Supported bool + Enabled bool + ID uint16 +} + +type VLANName struct { + ID uint16 + Name string +} + +type ProtocolIdentity []byte + +// LACP options +const ( + LLDPAggregationCapability byte = 1 << 0 + LLDPAggregationStatus byte = 1 << 1 +) + +// IEEE 802 Link Aggregation parameters +type LLDPLinkAggregation struct { + Supported bool + Enabled bool + PortID uint32 +} + +// LLDPInfo8021 represents the information carried in 802.1 Org-specific TLVs +type LLDPInfo8021 struct { + PVID uint16 + PPVIDs []PortProtocolVLANID + VLANNames []VLANName + ProtocolIdentities []ProtocolIdentity + VIDUsageDigest uint32 + ManagementVID uint16 + LinkAggregation LLDPLinkAggregation +} + +// IEEE 802.3 TLV Subtypes +const ( + LLDP8023SubtypeMACPHY uint8 = 1 + LLDP8023SubtypeMDIPower uint8 = 2 + LLDP8023SubtypeLinkAggregation uint8 = 3 + LLDP8023SubtypeMTU uint8 = 4 +) + +// MACPHY options +const ( + LLDPMACPHYCapability byte = 1 << 0 + LLDPMACPHYStatus byte = 1 << 1 +) + +// From IANA-MAU-MIB (introduced by RFC 4836) - dot3MauType +const ( + LLDPMAUTypeUnknown uint16 = 0 + LLDPMAUTypeAUI uint16 = 1 + LLDPMAUType10Base5 uint16 = 2 + LLDPMAUTypeFOIRL uint16 = 3 + LLDPMAUType10Base2 uint16 = 4 + LLDPMAUType10BaseT uint16 = 5 + LLDPMAUType10BaseFP uint16 = 6 + LLDPMAUType10BaseFB uint16 = 7 + LLDPMAUType10BaseFL uint16 = 8 + LLDPMAUType10BROAD36 uint16 = 9 + LLDPMAUType10BaseT_HD uint16 = 10 + LLDPMAUType10BaseT_FD uint16 = 11 + LLDPMAUType10BaseFL_HD uint16 = 12 + LLDPMAUType10BaseFL_FD uint16 = 13 + LLDPMAUType100BaseT4 uint16 = 14 + LLDPMAUType100BaseTX_HD uint16 = 15 + LLDPMAUType100BaseTX_FD uint16 = 16 + LLDPMAUType100BaseFX_HD uint16 = 17 + LLDPMAUType100BaseFX_FD uint16 = 18 + LLDPMAUType100BaseT2_HD uint16 = 19 + LLDPMAUType100BaseT2_FD uint16 = 20 + LLDPMAUType1000BaseX_HD uint16 = 21 + LLDPMAUType1000BaseX_FD uint16 = 22 + LLDPMAUType1000BaseLX_HD uint16 = 23 + LLDPMAUType1000BaseLX_FD uint16 = 24 + LLDPMAUType1000BaseSX_HD uint16 = 25 + LLDPMAUType1000BaseSX_FD uint16 = 26 + LLDPMAUType1000BaseCX_HD uint16 = 27 + LLDPMAUType1000BaseCX_FD uint16 = 28 + LLDPMAUType1000BaseT_HD uint16 = 29 + LLDPMAUType1000BaseT_FD uint16 = 30 + LLDPMAUType10GBaseX uint16 = 31 + LLDPMAUType10GBaseLX4 uint16 = 32 + LLDPMAUType10GBaseR uint16 = 33 + LLDPMAUType10GBaseER uint16 = 34 + LLDPMAUType10GBaseLR uint16 = 35 + LLDPMAUType10GBaseSR uint16 = 36 + LLDPMAUType10GBaseW uint16 = 37 + LLDPMAUType10GBaseEW uint16 = 38 + LLDPMAUType10GBaseLW uint16 = 39 + LLDPMAUType10GBaseSW uint16 = 40 + LLDPMAUType10GBaseCX4 uint16 = 41 + LLDPMAUType2BaseTL uint16 = 42 + LLDPMAUType10PASS_TS uint16 = 43 + LLDPMAUType100BaseBX10D uint16 = 44 + LLDPMAUType100BaseBX10U uint16 = 45 + LLDPMAUType100BaseLX10 uint16 = 46 + LLDPMAUType1000BaseBX10D uint16 = 47 + LLDPMAUType1000BaseBX10U uint16 = 48 + LLDPMAUType1000BaseLX10 uint16 = 49 + LLDPMAUType1000BasePX10D uint16 = 50 + LLDPMAUType1000BasePX10U uint16 = 51 + LLDPMAUType1000BasePX20D uint16 = 52 + LLDPMAUType1000BasePX20U uint16 = 53 + LLDPMAUType10GBaseT uint16 = 54 + LLDPMAUType10GBaseLRM uint16 = 55 + LLDPMAUType1000BaseKX uint16 = 56 + LLDPMAUType10GBaseKX4 uint16 = 57 + LLDPMAUType10GBaseKR uint16 = 58 + LLDPMAUType10_1GBasePRX_D1 uint16 = 59 + LLDPMAUType10_1GBasePRX_D2 uint16 = 60 + LLDPMAUType10_1GBasePRX_D3 uint16 = 61 + LLDPMAUType10_1GBasePRX_U1 uint16 = 62 + LLDPMAUType10_1GBasePRX_U2 uint16 = 63 + LLDPMAUType10_1GBasePRX_U3 uint16 = 64 + LLDPMAUType10GBasePR_D1 uint16 = 65 + LLDPMAUType10GBasePR_D2 uint16 = 66 + LLDPMAUType10GBasePR_D3 uint16 = 67 + LLDPMAUType10GBasePR_U1 uint16 = 68 + LLDPMAUType10GBasePR_U3 uint16 = 69 +) + +// From RFC 3636 - ifMauAutoNegCapAdvertisedBits +const ( + LLDPMAUPMDOther uint16 = 1 << 15 + LLDPMAUPMD10BaseT uint16 = 1 << 14 + LLDPMAUPMD10BaseT_FD uint16 = 1 << 13 + LLDPMAUPMD100BaseT4 uint16 = 1 << 12 + LLDPMAUPMD100BaseTX uint16 = 1 << 11 + LLDPMAUPMD100BaseTX_FD uint16 = 1 << 10 + LLDPMAUPMD100BaseT2 uint16 = 1 << 9 + LLDPMAUPMD100BaseT2_FD uint16 = 1 << 8 + LLDPMAUPMDFDXPAUSE uint16 = 1 << 7 + LLDPMAUPMDFDXAPAUSE uint16 = 1 << 6 + LLDPMAUPMDFDXSPAUSE uint16 = 1 << 5 + LLDPMAUPMDFDXBPAUSE uint16 = 1 << 4 + LLDPMAUPMD1000BaseX uint16 = 1 << 3 + LLDPMAUPMD1000BaseX_FD uint16 = 1 << 2 + LLDPMAUPMD1000BaseT uint16 = 1 << 1 + LLDPMAUPMD1000BaseT_FD uint16 = 1 << 0 +) + +// Inverted ifMauAutoNegCapAdvertisedBits if required +// (Some manufacturers misinterpreted the spec - +// see https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1455) +const ( + LLDPMAUPMDOtherInv uint16 = 1 << 0 + LLDPMAUPMD10BaseTInv uint16 = 1 << 1 + LLDPMAUPMD10BaseT_FDInv uint16 = 1 << 2 + LLDPMAUPMD100BaseT4Inv uint16 = 1 << 3 + LLDPMAUPMD100BaseTXInv uint16 = 1 << 4 + LLDPMAUPMD100BaseTX_FDInv uint16 = 1 << 5 + LLDPMAUPMD100BaseT2Inv uint16 = 1 << 6 + LLDPMAUPMD100BaseT2_FDInv uint16 = 1 << 7 + LLDPMAUPMDFDXPAUSEInv uint16 = 1 << 8 + LLDPMAUPMDFDXAPAUSEInv uint16 = 1 << 9 + LLDPMAUPMDFDXSPAUSEInv uint16 = 1 << 10 + LLDPMAUPMDFDXBPAUSEInv uint16 = 1 << 11 + LLDPMAUPMD1000BaseXInv uint16 = 1 << 12 + LLDPMAUPMD1000BaseX_FDInv uint16 = 1 << 13 + LLDPMAUPMD1000BaseTInv uint16 = 1 << 14 + LLDPMAUPMD1000BaseT_FDInv uint16 = 1 << 15 +) + +type LLDPMACPHYConfigStatus struct { + AutoNegSupported bool + AutoNegEnabled bool + AutoNegCapability uint16 + MAUType uint16 +} + +// MDI Power options +const ( + LLDPMDIPowerPortClass byte = 1 << 0 + LLDPMDIPowerCapability byte = 1 << 1 + LLDPMDIPowerStatus byte = 1 << 2 + LLDPMDIPowerPairsAbility byte = 1 << 3 +) + +type LLDPPowerType byte + +type LLDPPowerSource byte + +type LLDPPowerPriority byte + +const ( + LLDPPowerPriorityUnknown LLDPPowerPriority = 0 + LLDPPowerPriorityMedium LLDPPowerPriority = 1 + LLDPPowerPriorityHigh LLDPPowerPriority = 2 + LLDPPowerPriorityLow LLDPPowerPriority = 3 +) + +type LLDPPowerViaMDI8023 struct { + PortClassPSE bool // false = PD + PSESupported bool + PSEEnabled bool + PSEPairsAbility bool + PSEPowerPair uint8 + PSEClass uint8 + Type LLDPPowerType + Source LLDPPowerSource + Priority LLDPPowerPriority + Requested uint16 // 1-510 Watts + Allocated uint16 // 1-510 Watts +} + +// LLDPInfo8023 represents the information carried in 802.3 Org-specific TLVs +type LLDPInfo8023 struct { + MACPHYConfigStatus LLDPMACPHYConfigStatus + PowerViaMDI LLDPPowerViaMDI8023 + LinkAggregation LLDPLinkAggregation + MTU uint16 +} + +// IEEE 802.1Qbg TLV Subtypes +const ( + LLDP8021QbgEVB uint8 = 0 + LLDP8021QbgCDCP uint8 = 1 + LLDP8021QbgVDP uint8 = 2 + LLDP8021QbgEVB22 uint8 = 13 +) + +// LLDPEVBCapabilities Types +const ( + LLDPEVBCapsSTD uint16 = 1 << 7 + LLDPEVBCapsRR uint16 = 1 << 6 + LLDPEVBCapsRTE uint16 = 1 << 2 + LLDPEVBCapsECP uint16 = 1 << 1 + LLDPEVBCapsVDP uint16 = 1 << 0 +) + +// LLDPEVBCapabilities represents the EVB capabilities of a device +type LLDPEVBCapabilities struct { + StandardBridging bool + ReflectiveRelay bool + RetransmissionTimerExponent bool + EdgeControlProtocol bool + VSIDiscoveryProtocol bool +} + +type LLDPEVBSettings struct { + Supported LLDPEVBCapabilities + Enabled LLDPEVBCapabilities + SupportedVSIs uint16 + ConfiguredVSIs uint16 + RTEExponent uint8 +} + +// LLDPInfo8021Qbg represents the information carried in 802.1Qbg Org-specific TLVs +type LLDPInfo8021Qbg struct { + EVBSettings LLDPEVBSettings +} + +type LLDPMediaSubtype uint8 + +// Media TLV Subtypes +const ( + LLDPMediaTypeCapabilities LLDPMediaSubtype = 1 + LLDPMediaTypeNetwork LLDPMediaSubtype = 2 + LLDPMediaTypeLocation LLDPMediaSubtype = 3 + LLDPMediaTypePower LLDPMediaSubtype = 4 + LLDPMediaTypeHardware LLDPMediaSubtype = 5 + LLDPMediaTypeFirmware LLDPMediaSubtype = 6 + LLDPMediaTypeSoftware LLDPMediaSubtype = 7 + LLDPMediaTypeSerial LLDPMediaSubtype = 8 + LLDPMediaTypeManufacturer LLDPMediaSubtype = 9 + LLDPMediaTypeModel LLDPMediaSubtype = 10 + LLDPMediaTypeAssetID LLDPMediaSubtype = 11 +) + +type LLDPMediaClass uint8 + +// Media Class Values +const ( + LLDPMediaClassUndefined LLDPMediaClass = 0 + LLDPMediaClassEndpointI LLDPMediaClass = 1 + LLDPMediaClassEndpointII LLDPMediaClass = 2 + LLDPMediaClassEndpointIII LLDPMediaClass = 3 + LLDPMediaClassNetwork LLDPMediaClass = 4 +) + +// LLDPMediaCapabilities Types +const ( + LLDPMediaCapsLLDP uint16 = 1 << 0 + LLDPMediaCapsNetwork uint16 = 1 << 1 + LLDPMediaCapsLocation uint16 = 1 << 2 + LLDPMediaCapsPowerPSE uint16 = 1 << 3 + LLDPMediaCapsPowerPD uint16 = 1 << 4 + LLDPMediaCapsInventory uint16 = 1 << 5 +) + +// LLDPMediaCapabilities represents the LLDP Media capabilities of a device +type LLDPMediaCapabilities struct { + Capabilities bool + NetworkPolicy bool + Location bool + PowerPSE bool + PowerPD bool + Inventory bool + Class LLDPMediaClass +} + +type LLDPApplicationType uint8 + +const ( + LLDPAppTypeReserved LLDPApplicationType = 0 + LLDPAppTypeVoice LLDPApplicationType = 1 + LLDPappTypeVoiceSignaling LLDPApplicationType = 2 + LLDPappTypeGuestVoice LLDPApplicationType = 3 + LLDPappTypeGuestVoiceSignaling LLDPApplicationType = 4 + LLDPappTypeSoftphoneVoice LLDPApplicationType = 5 + LLDPappTypeVideoConferencing LLDPApplicationType = 6 + LLDPappTypeStreamingVideo LLDPApplicationType = 7 + LLDPappTypeVideoSignaling LLDPApplicationType = 8 +) + +type LLDPNetworkPolicy struct { + ApplicationType LLDPApplicationType + Defined bool + Tagged bool + VLANId uint16 + L2Priority uint16 + DSCPValue uint8 +} + +type LLDPLocationFormat uint8 + +const ( + LLDPLocationFormatInvalid LLDPLocationFormat = 0 + LLDPLocationFormatCoordinate LLDPLocationFormat = 1 + LLDPLocationFormatAddress LLDPLocationFormat = 2 + LLDPLocationFormatECS LLDPLocationFormat = 3 +) + +type LLDPLocationAddressWhat uint8 + +const ( + LLDPLocationAddressWhatDHCP LLDPLocationAddressWhat = 0 + LLDPLocationAddressWhatNetwork LLDPLocationAddressWhat = 1 + LLDPLocationAddressWhatClient LLDPLocationAddressWhat = 2 +) + +type LLDPLocationAddressType uint8 + +const ( + LLDPLocationAddressTypeLanguage LLDPLocationAddressType = 0 + LLDPLocationAddressTypeNational LLDPLocationAddressType = 1 + LLDPLocationAddressTypeCounty LLDPLocationAddressType = 2 + LLDPLocationAddressTypeCity LLDPLocationAddressType = 3 + LLDPLocationAddressTypeCityDivision LLDPLocationAddressType = 4 + LLDPLocationAddressTypeNeighborhood LLDPLocationAddressType = 5 + LLDPLocationAddressTypeStreet LLDPLocationAddressType = 6 + LLDPLocationAddressTypeLeadingStreet LLDPLocationAddressType = 16 + LLDPLocationAddressTypeTrailingStreet LLDPLocationAddressType = 17 + LLDPLocationAddressTypeStreetSuffix LLDPLocationAddressType = 18 + LLDPLocationAddressTypeHouseNum LLDPLocationAddressType = 19 + LLDPLocationAddressTypeHouseSuffix LLDPLocationAddressType = 20 + LLDPLocationAddressTypeLandmark LLDPLocationAddressType = 21 + LLDPLocationAddressTypeAdditional LLDPLocationAddressType = 22 + LLDPLocationAddressTypeName LLDPLocationAddressType = 23 + LLDPLocationAddressTypePostal LLDPLocationAddressType = 24 + LLDPLocationAddressTypeBuilding LLDPLocationAddressType = 25 + LLDPLocationAddressTypeUnit LLDPLocationAddressType = 26 + LLDPLocationAddressTypeFloor LLDPLocationAddressType = 27 + LLDPLocationAddressTypeRoom LLDPLocationAddressType = 28 + LLDPLocationAddressTypePlace LLDPLocationAddressType = 29 + LLDPLocationAddressTypeScript LLDPLocationAddressType = 128 +) + +type LLDPLocationCoordinate struct { + LatitudeResolution uint8 + Latitude uint64 + LongitudeResolution uint8 + Longitude uint64 + AltitudeType uint8 + AltitudeResolution uint16 + Altitude uint32 + Datum uint8 +} + +type LLDPLocationAddressLine struct { + Type LLDPLocationAddressType + Value string +} + +type LLDPLocationAddress struct { + What LLDPLocationAddressWhat + CountryCode string + AddressLines []LLDPLocationAddressLine +} + +type LLDPLocationECS struct { + ELIN string +} + +// LLDP represents a physical location. +// Only one of the embedded types will contain values, depending on Format. +type LLDPLocation struct { + Format LLDPLocationFormat + Coordinate LLDPLocationCoordinate + Address LLDPLocationAddress + ECS LLDPLocationECS +} + +type LLDPPowerViaMDI struct { + Type LLDPPowerType + Source LLDPPowerSource + Priority LLDPPowerPriority + Value uint16 +} + +// LLDPInfoMedia represents the information carried in TR-41 Org-specific TLVs +type LLDPInfoMedia struct { + MediaCapabilities LLDPMediaCapabilities + NetworkPolicy LLDPNetworkPolicy + Location LLDPLocation + PowerViaMDI LLDPPowerViaMDI + HardwareRevision string + FirmwareRevision string + SoftwareRevision string + SerialNumber string + Manufacturer string + Model string + AssetID string +} + +type LLDPCisco2Subtype uint8 + +// Cisco2 TLV Subtypes +const ( + LLDPCisco2PowerViaMDI LLDPCisco2Subtype = 1 +) + +const ( + LLDPCiscoPSESupport uint8 = 1 << 0 + LLDPCiscoArchShared uint8 = 1 << 1 + LLDPCiscoPDSparePair uint8 = 1 << 2 + LLDPCiscoPSESparePair uint8 = 1 << 3 +) + +// LLDPInfoCisco2 represents the information carried in Cisco Org-specific TLVs +type LLDPInfoCisco2 struct { + PSEFourWirePoESupported bool + PDSparePairArchitectureShared bool + PDRequestSparePairPoEOn bool + PSESparePairPoEOn bool +} + +// Profinet Subtypes +type LLDPProfinetSubtype uint8 + +const ( + LLDPProfinetPNIODelay LLDPProfinetSubtype = 1 + LLDPProfinetPNIOPortStatus LLDPProfinetSubtype = 2 + LLDPProfinetPNIOMRPPortStatus LLDPProfinetSubtype = 4 + LLDPProfinetPNIOChassisMAC LLDPProfinetSubtype = 5 + LLDPProfinetPNIOPTCPStatus LLDPProfinetSubtype = 6 +) + +type LLDPPNIODelay struct { + RXLocal uint32 + RXRemote uint32 + TXLocal uint32 + TXRemote uint32 + CableLocal uint32 +} + +type LLDPPNIOPortStatus struct { + Class2 uint16 + Class3 uint16 +} + +type LLDPPNIOMRPPortStatus struct { + UUID []byte + Status uint16 +} + +type LLDPPNIOPTCPStatus struct { + MasterAddress []byte + SubdomainUUID []byte + IRDataUUID []byte + PeriodValid bool + PeriodLength uint32 + RedPeriodValid bool + RedPeriodBegin uint32 + OrangePeriodValid bool + OrangePeriodBegin uint32 + GreenPeriodValid bool + GreenPeriodBegin uint32 +} + +// LLDPInfoProfinet represents the information carried in Profinet Org-specific TLVs +type LLDPInfoProfinet struct { + PNIODelay LLDPPNIODelay + PNIOPortStatus LLDPPNIOPortStatus + PNIOMRPPortStatus LLDPPNIOMRPPortStatus + ChassisMAC []byte + PNIOPTCPStatus LLDPPNIOPTCPStatus +} + +// LayerType returns gopacket.LayerTypeLinkLayerDiscovery. +func (c *LinkLayerDiscovery) LayerType() gopacket.LayerType { + return LayerTypeLinkLayerDiscovery +} + +func decodeLinkLayerDiscovery(data []byte, p gopacket.PacketBuilder) error { + var vals []LinkLayerDiscoveryValue + vData := data[0:] + for len(vData) > 0 { + nbit := vData[0] & 0x01 + t := LLDPTLVType(vData[0] >> 1) + val := LinkLayerDiscoveryValue{Type: t, Length: uint16(nbit)<<8 + uint16(vData[1])} + if val.Length > 0 { + val.Value = vData[2 : val.Length+2] + } + vals = append(vals, val) + if t == LLDPTLVEnd { + break + } + if len(vData) < int(2+val.Length) { + return errors.New("Malformed LinkLayerDiscovery Header") + } + vData = vData[2+val.Length:] + } + if len(vals) < 4 { + return errors.New("Missing mandatory LinkLayerDiscovery TLV") + } + c := &LinkLayerDiscovery{} + gotEnd := false + for _, v := range vals { + switch v.Type { + case LLDPTLVEnd: + gotEnd = true + case LLDPTLVChassisID: + if len(v.Value) < 2 { + return errors.New("Malformed LinkLayerDiscovery ChassisID TLV") + } + c.ChassisID.Subtype = LLDPChassisIDSubType(v.Value[0]) + c.ChassisID.ID = v.Value[1:] + case LLDPTLVPortID: + if len(v.Value) < 2 { + return errors.New("Malformed LinkLayerDiscovery PortID TLV") + } + c.PortID.Subtype = LLDPPortIDSubType(v.Value[0]) + c.PortID.ID = v.Value[1:] + case LLDPTLVTTL: + if len(v.Value) < 2 { + return errors.New("Malformed LinkLayerDiscovery TTL TLV") + } + c.TTL = binary.BigEndian.Uint16(v.Value[0:2]) + default: + c.Values = append(c.Values, v) + } + } + if c.ChassisID.Subtype == 0 || c.PortID.Subtype == 0 || !gotEnd { + return errors.New("Missing mandatory LinkLayerDiscovery TLV") + } + c.Contents = data + p.AddLayer(c) + + info := &LinkLayerDiscoveryInfo{} + p.AddLayer(info) + for _, v := range c.Values { + switch v.Type { + case LLDPTLVPortDescription: + info.PortDescription = string(v.Value) + case LLDPTLVSysName: + info.SysName = string(v.Value) + case LLDPTLVSysDescription: + info.SysDescription = string(v.Value) + case LLDPTLVSysCapabilities: + if err := checkLLDPTLVLen(v, 4); err != nil { + return err + } + info.SysCapabilities.SystemCap = getCapabilities(binary.BigEndian.Uint16(v.Value[0:2])) + info.SysCapabilities.EnabledCap = getCapabilities(binary.BigEndian.Uint16(v.Value[2:4])) + case LLDPTLVMgmtAddress: + if err := checkLLDPTLVLen(v, 9); err != nil { + return err + } + mlen := v.Value[0] + if err := checkLLDPTLVLen(v, int(mlen+7)); err != nil { + return err + } + info.MgmtAddress.Subtype = IANAAddressFamily(v.Value[1]) + info.MgmtAddress.Address = v.Value[2 : mlen+1] + info.MgmtAddress.InterfaceSubtype = LLDPInterfaceSubtype(v.Value[mlen+1]) + info.MgmtAddress.InterfaceNumber = binary.BigEndian.Uint32(v.Value[mlen+2 : mlen+6]) + olen := v.Value[mlen+6] + if err := checkLLDPTLVLen(v, int(mlen+6+olen)); err != nil { + return err + } + info.MgmtAddress.OID = string(v.Value[mlen+9 : mlen+9+olen]) + case LLDPTLVOrgSpecific: + if err := checkLLDPTLVLen(v, 4); err != nil { + return err + } + info.OrgTLVs = append(info.OrgTLVs, LLDPOrgSpecificTLV{IEEEOUI(binary.BigEndian.Uint32(append([]byte{byte(0)}, v.Value[0:3]...))), uint8(v.Value[3]), v.Value[4:]}) + } + } + return nil +} + +func (l *LinkLayerDiscoveryInfo) Decode8021() (info LLDPInfo8021, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUI8021 { + continue + } + switch o.SubType { + case LLDP8021SubtypePortVLANID: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + info.PVID = binary.BigEndian.Uint16(o.Info[0:2]) + case LLDP8021SubtypeProtocolVLANID: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + sup := (o.Info[0]&LLDPProtocolVLANIDCapability > 0) + en := (o.Info[0]&LLDPProtocolVLANIDStatus > 0) + id := binary.BigEndian.Uint16(o.Info[1:3]) + info.PPVIDs = append(info.PPVIDs, PortProtocolVLANID{sup, en, id}) + case LLDP8021SubtypeVLANName: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + id := binary.BigEndian.Uint16(o.Info[0:2]) + info.VLANNames = append(info.VLANNames, VLANName{id, string(o.Info[3:])}) + case LLDP8021SubtypeProtocolIdentity: + if err = checkLLDPOrgSpecificLen(o, 1); err != nil { + return + } + l := int(o.Info[0]) + if l > 0 { + info.ProtocolIdentities = append(info.ProtocolIdentities, o.Info[1:1+l]) + } + case LLDP8021SubtypeVDIUsageDigest: + if err = checkLLDPOrgSpecificLen(o, 4); err != nil { + return + } + info.VIDUsageDigest = binary.BigEndian.Uint32(o.Info[0:4]) + case LLDP8021SubtypeManagementVID: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + info.ManagementVID = binary.BigEndian.Uint16(o.Info[0:2]) + case LLDP8021SubtypeLinkAggregation: + if err = checkLLDPOrgSpecificLen(o, 5); err != nil { + return + } + sup := (o.Info[0]&LLDPAggregationCapability > 0) + en := (o.Info[0]&LLDPAggregationStatus > 0) + info.LinkAggregation = LLDPLinkAggregation{sup, en, binary.BigEndian.Uint32(o.Info[1:5])} + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) Decode8023() (info LLDPInfo8023, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUI8023 { + continue + } + switch o.SubType { + case LLDP8023SubtypeMACPHY: + if err = checkLLDPOrgSpecificLen(o, 5); err != nil { + return + } + sup := (o.Info[0]&LLDPMACPHYCapability > 0) + en := (o.Info[0]&LLDPMACPHYStatus > 0) + ca := binary.BigEndian.Uint16(o.Info[1:3]) + mau := binary.BigEndian.Uint16(o.Info[3:5]) + info.MACPHYConfigStatus = LLDPMACPHYConfigStatus{sup, en, ca, mau} + case LLDP8023SubtypeMDIPower: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + info.PowerViaMDI.PortClassPSE = (o.Info[0]&LLDPMDIPowerPortClass > 0) + info.PowerViaMDI.PSESupported = (o.Info[0]&LLDPMDIPowerCapability > 0) + info.PowerViaMDI.PSEEnabled = (o.Info[0]&LLDPMDIPowerStatus > 0) + info.PowerViaMDI.PSEPairsAbility = (o.Info[0]&LLDPMDIPowerPairsAbility > 0) + info.PowerViaMDI.PSEPowerPair = uint8(o.Info[1]) + info.PowerViaMDI.PSEClass = uint8(o.Info[2]) + if len(o.Info) >= 7 { + info.PowerViaMDI.Type = LLDPPowerType((o.Info[3] & 0xc0) >> 6) + info.PowerViaMDI.Source = LLDPPowerSource((o.Info[3] & 0x30) >> 4) + if info.PowerViaMDI.Type == 1 || info.PowerViaMDI.Type == 3 { + info.PowerViaMDI.Source += 128 // For Stringify purposes + } + info.PowerViaMDI.Priority = LLDPPowerPriority(o.Info[3] & 0x0f) + info.PowerViaMDI.Requested = binary.BigEndian.Uint16(o.Info[4:6]) + info.PowerViaMDI.Allocated = binary.BigEndian.Uint16(o.Info[6:8]) + } + case LLDP8023SubtypeLinkAggregation: + if err = checkLLDPOrgSpecificLen(o, 5); err != nil { + return + } + sup := (o.Info[0]&LLDPAggregationCapability > 0) + en := (o.Info[0]&LLDPAggregationStatus > 0) + info.LinkAggregation = LLDPLinkAggregation{sup, en, binary.BigEndian.Uint32(o.Info[1:5])} + case LLDP8023SubtypeMTU: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + info.MTU = binary.BigEndian.Uint16(o.Info[0:2]) + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) Decode8021Qbg() (info LLDPInfo8021Qbg, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUI8021Qbg { + continue + } + switch o.SubType { + case LLDP8021QbgEVB: + if err = checkLLDPOrgSpecificLen(o, 9); err != nil { + return + } + info.EVBSettings.Supported = getEVBCapabilities(binary.BigEndian.Uint16(o.Info[0:2])) + info.EVBSettings.Enabled = getEVBCapabilities(binary.BigEndian.Uint16(o.Info[2:4])) + info.EVBSettings.SupportedVSIs = binary.BigEndian.Uint16(o.Info[4:6]) + info.EVBSettings.ConfiguredVSIs = binary.BigEndian.Uint16(o.Info[6:8]) + info.EVBSettings.RTEExponent = uint8(o.Info[8]) + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) DecodeMedia() (info LLDPInfoMedia, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUIMedia { + continue + } + switch LLDPMediaSubtype(o.SubType) { + case LLDPMediaTypeCapabilities: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + b := binary.BigEndian.Uint16(o.Info[0:2]) + info.MediaCapabilities.Capabilities = (b & LLDPMediaCapsLLDP) > 0 + info.MediaCapabilities.NetworkPolicy = (b & LLDPMediaCapsNetwork) > 0 + info.MediaCapabilities.Location = (b & LLDPMediaCapsLocation) > 0 + info.MediaCapabilities.PowerPSE = (b & LLDPMediaCapsPowerPSE) > 0 + info.MediaCapabilities.PowerPD = (b & LLDPMediaCapsPowerPD) > 0 + info.MediaCapabilities.Inventory = (b & LLDPMediaCapsInventory) > 0 + info.MediaCapabilities.Class = LLDPMediaClass(o.Info[2]) + case LLDPMediaTypeNetwork: + if err = checkLLDPOrgSpecificLen(o, 4); err != nil { + return + } + info.NetworkPolicy.ApplicationType = LLDPApplicationType(o.Info[0]) + b := binary.BigEndian.Uint16(o.Info[1:3]) + info.NetworkPolicy.Defined = (b & 0x8000) == 0 + info.NetworkPolicy.Tagged = (b & 0x4000) > 0 + info.NetworkPolicy.VLANId = (b & 0x1ffe) >> 1 + b = binary.BigEndian.Uint16(o.Info[2:4]) + info.NetworkPolicy.L2Priority = (b & 0x01c0) >> 6 + info.NetworkPolicy.DSCPValue = uint8(o.Info[3] & 0x3f) + case LLDPMediaTypeLocation: + if err = checkLLDPOrgSpecificLen(o, 1); err != nil { + return + } + info.Location.Format = LLDPLocationFormat(o.Info[0]) + o.Info = o.Info[1:] + switch info.Location.Format { + case LLDPLocationFormatCoordinate: + if err = checkLLDPOrgSpecificLen(o, 16); err != nil { + return + } + info.Location.Coordinate.LatitudeResolution = uint8(o.Info[0]&0xfc) >> 2 + b := binary.BigEndian.Uint64(o.Info[0:8]) + info.Location.Coordinate.Latitude = (b & 0x03ffffffff000000) >> 24 + info.Location.Coordinate.LongitudeResolution = uint8(o.Info[5]&0xfc) >> 2 + b = binary.BigEndian.Uint64(o.Info[5:13]) + info.Location.Coordinate.Longitude = (b & 0x03ffffffff000000) >> 24 + info.Location.Coordinate.AltitudeType = uint8((o.Info[10] & 0x30) >> 4) + b1 := binary.BigEndian.Uint16(o.Info[10:12]) + info.Location.Coordinate.AltitudeResolution = (b1 & 0xfc0) >> 6 + b2 := binary.BigEndian.Uint32(o.Info[11:15]) + info.Location.Coordinate.Altitude = b2 & 0x3fffffff + info.Location.Coordinate.Datum = uint8(o.Info[15]) + case LLDPLocationFormatAddress: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + //ll := uint8(o.Info[0]) + info.Location.Address.What = LLDPLocationAddressWhat(o.Info[1]) + info.Location.Address.CountryCode = string(o.Info[2:4]) + data := o.Info[4:] + for len(data) > 1 { + aType := LLDPLocationAddressType(data[0]) + aLen := int(data[1]) + if len(data) >= aLen+2 { + info.Location.Address.AddressLines = append(info.Location.Address.AddressLines, LLDPLocationAddressLine{aType, string(data[2 : aLen+2])}) + data = data[aLen+2:] + } else { + break + } + } + case LLDPLocationFormatECS: + info.Location.ECS.ELIN = string(o.Info) + } + case LLDPMediaTypePower: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + info.PowerViaMDI.Type = LLDPPowerType((o.Info[0] & 0xc0) >> 6) + info.PowerViaMDI.Source = LLDPPowerSource((o.Info[0] & 0x30) >> 4) + if info.PowerViaMDI.Type == 1 || info.PowerViaMDI.Type == 3 { + info.PowerViaMDI.Source += 128 // For Stringify purposes + } + info.PowerViaMDI.Priority = LLDPPowerPriority(o.Info[0] & 0x0f) + info.PowerViaMDI.Value = binary.BigEndian.Uint16(o.Info[1:3]) * 100 // 0 to 102.3 w, 0.1W increments + case LLDPMediaTypeHardware: + info.HardwareRevision = string(o.Info) + case LLDPMediaTypeFirmware: + info.FirmwareRevision = string(o.Info) + case LLDPMediaTypeSoftware: + info.SoftwareRevision = string(o.Info) + case LLDPMediaTypeSerial: + info.SerialNumber = string(o.Info) + case LLDPMediaTypeManufacturer: + info.Manufacturer = string(o.Info) + case LLDPMediaTypeModel: + info.Model = string(o.Info) + case LLDPMediaTypeAssetID: + info.AssetID = string(o.Info) + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) DecodeCisco2() (info LLDPInfoCisco2, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUICisco2 { + continue + } + switch LLDPCisco2Subtype(o.SubType) { + case LLDPCisco2PowerViaMDI: + if err = checkLLDPOrgSpecificLen(o, 1); err != nil { + return + } + info.PSEFourWirePoESupported = (o.Info[0] & LLDPCiscoPSESupport) > 0 + info.PDSparePairArchitectureShared = (o.Info[0] & LLDPCiscoArchShared) > 0 + info.PDRequestSparePairPoEOn = (o.Info[0] & LLDPCiscoPDSparePair) > 0 + info.PSESparePairPoEOn = (o.Info[0] & LLDPCiscoPSESparePair) > 0 + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) DecodeProfinet() (info LLDPInfoProfinet, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUIProfinet { + continue + } + switch LLDPProfinetSubtype(o.SubType) { + case LLDPProfinetPNIODelay: + if err = checkLLDPOrgSpecificLen(o, 20); err != nil { + return + } + info.PNIODelay.RXLocal = binary.BigEndian.Uint32(o.Info[0:4]) + info.PNIODelay.RXRemote = binary.BigEndian.Uint32(o.Info[4:8]) + info.PNIODelay.TXLocal = binary.BigEndian.Uint32(o.Info[8:12]) + info.PNIODelay.TXRemote = binary.BigEndian.Uint32(o.Info[12:16]) + info.PNIODelay.CableLocal = binary.BigEndian.Uint32(o.Info[16:20]) + case LLDPProfinetPNIOPortStatus: + if err = checkLLDPOrgSpecificLen(o, 4); err != nil { + return + } + info.PNIOPortStatus.Class2 = binary.BigEndian.Uint16(o.Info[0:2]) + info.PNIOPortStatus.Class3 = binary.BigEndian.Uint16(o.Info[2:4]) + case LLDPProfinetPNIOMRPPortStatus: + if err = checkLLDPOrgSpecificLen(o, 18); err != nil { + return + } + info.PNIOMRPPortStatus.UUID = o.Info[0:16] + info.PNIOMRPPortStatus.Status = binary.BigEndian.Uint16(o.Info[16:18]) + case LLDPProfinetPNIOChassisMAC: + if err = checkLLDPOrgSpecificLen(o, 6); err != nil { + return + } + info.ChassisMAC = o.Info[0:6] + case LLDPProfinetPNIOPTCPStatus: + if err = checkLLDPOrgSpecificLen(o, 54); err != nil { + return + } + info.PNIOPTCPStatus.MasterAddress = o.Info[0:6] + info.PNIOPTCPStatus.SubdomainUUID = o.Info[6:22] + info.PNIOPTCPStatus.IRDataUUID = o.Info[22:38] + b := binary.BigEndian.Uint32(o.Info[38:42]) + info.PNIOPTCPStatus.PeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.PeriodLength = b & 0x7fffffff + b = binary.BigEndian.Uint32(o.Info[42:46]) + info.PNIOPTCPStatus.RedPeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.RedPeriodBegin = b & 0x7fffffff + b = binary.BigEndian.Uint32(o.Info[46:50]) + info.PNIOPTCPStatus.OrangePeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.OrangePeriodBegin = b & 0x7fffffff + b = binary.BigEndian.Uint32(o.Info[50:54]) + info.PNIOPTCPStatus.GreenPeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.GreenPeriodBegin = b & 0x7fffffff + } + } + return +} + +// LayerType returns gopacket.LayerTypeLinkLayerDiscoveryInfo. +func (c *LinkLayerDiscoveryInfo) LayerType() gopacket.LayerType { + return LayerTypeLinkLayerDiscoveryInfo +} + +func getCapabilities(v uint16) (c LLDPCapabilities) { + c.Other = (v&LLDPCapsOther > 0) + c.Repeater = (v&LLDPCapsRepeater > 0) + c.Bridge = (v&LLDPCapsBridge > 0) + c.WLANAP = (v&LLDPCapsWLANAP > 0) + c.Router = (v&LLDPCapsRouter > 0) + c.Phone = (v&LLDPCapsPhone > 0) + c.DocSis = (v&LLDPCapsDocSis > 0) + c.StationOnly = (v&LLDPCapsStationOnly > 0) + c.CVLAN = (v&LLDPCapsCVLAN > 0) + c.SVLAN = (v&LLDPCapsSVLAN > 0) + c.TMPR = (v&LLDPCapsTmpr > 0) + return +} + +func getEVBCapabilities(v uint16) (c LLDPEVBCapabilities) { + c.StandardBridging = (v & LLDPEVBCapsSTD) > 0 + c.StandardBridging = (v & LLDPEVBCapsSTD) > 0 + c.ReflectiveRelay = (v & LLDPEVBCapsRR) > 0 + c.RetransmissionTimerExponent = (v & LLDPEVBCapsRTE) > 0 + c.EdgeControlProtocol = (v & LLDPEVBCapsECP) > 0 + c.VSIDiscoveryProtocol = (v & LLDPEVBCapsVDP) > 0 + return +} + +func (t LLDPTLVType) String() (s string) { + switch t { + case LLDPTLVEnd: + s = "TLV End" + case LLDPTLVChassisID: + s = "Chassis ID" + case LLDPTLVPortID: + s = "Port ID" + case LLDPTLVTTL: + s = "TTL" + case LLDPTLVPortDescription: + s = "Port Description" + case LLDPTLVSysName: + s = "System Name" + case LLDPTLVSysDescription: + s = "System Description" + case LLDPTLVSysCapabilities: + s = "System Capabilities" + case LLDPTLVMgmtAddress: + s = "Management Address" + case LLDPTLVOrgSpecific: + s = "Organisation Specific" + default: + s = "Unknown" + } + return +} + +func (t LLDPChassisIDSubType) String() (s string) { + switch t { + case LLDPChassisIDSubTypeReserved: + s = "Reserved" + case LLDPChassisIDSubTypeChassisComp: + s = "Chassis Component" + case LLDPChassisIDSubtypeIfaceAlias: + s = "Interface Alias" + case LLDPChassisIDSubTypePortComp: + s = "Port Component" + case LLDPChassisIDSubTypeMACAddr: + s = "MAC Address" + case LLDPChassisIDSubTypeNetworkAddr: + s = "Network Address" + case LLDPChassisIDSubtypeIfaceName: + s = "Interface Name" + case LLDPChassisIDSubTypeLocal: + s = "Local" + default: + s = "Unknown" + } + return +} + +func (t LLDPPortIDSubType) String() (s string) { + switch t { + case LLDPPortIDSubtypeReserved: + s = "Reserved" + case LLDPPortIDSubtypeIfaceAlias: + s = "Interface Alias" + case LLDPPortIDSubtypePortComp: + s = "Port Component" + case LLDPPortIDSubtypeMACAddr: + s = "MAC Address" + case LLDPPortIDSubtypeNetworkAddr: + s = "Network Address" + case LLDPPortIDSubtypeIfaceName: + s = "Interface Name" + case LLDPPortIDSubtypeAgentCircuitID: + s = "Agent Circuit ID" + case LLDPPortIDSubtypeLocal: + s = "Local" + default: + s = "Unknown" + } + return +} + +func (t IANAAddressFamily) String() (s string) { + switch t { + case IANAAddressFamilyReserved: + s = "Reserved" + case IANAAddressFamilyIPV4: + s = "IPv4" + case IANAAddressFamilyIPV6: + s = "IPv6" + case IANAAddressFamilyNSAP: + s = "NSAP" + case IANAAddressFamilyHDLC: + s = "HDLC" + case IANAAddressFamilyBBN1822: + s = "BBN 1822" + case IANAAddressFamily802: + s = "802 media plus Ethernet 'canonical format'" + case IANAAddressFamilyE163: + s = "E.163" + case IANAAddressFamilyE164: + s = "E.164 (SMDS, Frame Relay, ATM)" + case IANAAddressFamilyF69: + s = "F.69 (Telex)" + case IANAAddressFamilyX121: + s = "X.121, X.25, Frame Relay" + case IANAAddressFamilyIPX: + s = "IPX" + case IANAAddressFamilyAtalk: + s = "Appletalk" + case IANAAddressFamilyDecnet: + s = "Decnet IV" + case IANAAddressFamilyBanyan: + s = "Banyan Vines" + case IANAAddressFamilyE164NSAP: + s = "E.164 with NSAP format subaddress" + case IANAAddressFamilyDNS: + s = "DNS" + case IANAAddressFamilyDistname: + s = "Distinguished Name" + case IANAAddressFamilyASNumber: + s = "AS Number" + case IANAAddressFamilyXTPIPV4: + s = "XTP over IP version 4" + case IANAAddressFamilyXTPIPV6: + s = "XTP over IP version 6" + case IANAAddressFamilyXTP: + s = "XTP native mode XTP" + case IANAAddressFamilyFcWWPN: + s = "Fibre Channel World-Wide Port Name" + case IANAAddressFamilyFcWWNN: + s = "Fibre Channel World-Wide Node Name" + case IANAAddressFamilyGWID: + s = "GWID" + case IANAAddressFamilyL2VPN: + s = "AFI for Layer 2 VPN" + default: + s = "Unknown" + } + return +} + +func (t LLDPInterfaceSubtype) String() (s string) { + switch t { + case LLDPInterfaceSubtypeUnknown: + s = "Unknown" + case LLDPInterfaceSubtypeifIndex: + s = "IfIndex" + case LLDPInterfaceSubtypeSysPort: + s = "System Port Number" + default: + s = "Unknown" + } + return +} + +func (t LLDPPowerType) String() (s string) { + switch t { + case 0: + s = "Type 2 PSE Device" + case 1: + s = "Type 2 PD Device" + case 2: + s = "Type 1 PSE Device" + case 3: + s = "Type 1 PD Device" + default: + s = "Unknown" + } + return +} + +func (t LLDPPowerSource) String() (s string) { + switch t { + // PD Device + case 0: + s = "Unknown" + case 1: + s = "PSE" + case 2: + s = "Local" + case 3: + s = "PSE and Local" + // PSE Device (Actual value + 128) + case 128: + s = "Unknown" + case 129: + s = "Primary Power Source" + case 130: + s = "Backup Power Source" + default: + s = "Unknown" + } + return +} + +func (t LLDPPowerPriority) String() (s string) { + switch t { + case 0: + s = "Unknown" + case 1: + s = "Critical" + case 2: + s = "High" + case 3: + s = "Low" + default: + s = "Unknown" + } + return +} + +func (t LLDPMediaSubtype) String() (s string) { + switch t { + case LLDPMediaTypeCapabilities: + s = "Media Capabilities " + case LLDPMediaTypeNetwork: + s = "Network Policy" + case LLDPMediaTypeLocation: + s = "Location Identification" + case LLDPMediaTypePower: + s = "Extended Power-via-MDI" + case LLDPMediaTypeHardware: + s = "Hardware Revision" + case LLDPMediaTypeFirmware: + s = "Firmware Revision" + case LLDPMediaTypeSoftware: + s = "Software Revision" + case LLDPMediaTypeSerial: + s = "Serial Number" + case LLDPMediaTypeManufacturer: + s = "Manufacturer" + case LLDPMediaTypeModel: + s = "Model" + case LLDPMediaTypeAssetID: + s = "Asset ID" + default: + s = "Unknown" + } + return +} + +func (t LLDPMediaClass) String() (s string) { + switch t { + case LLDPMediaClassUndefined: + s = "Undefined" + case LLDPMediaClassEndpointI: + s = "Endpoint Class I" + case LLDPMediaClassEndpointII: + s = "Endpoint Class II" + case LLDPMediaClassEndpointIII: + s = "Endpoint Class III" + case LLDPMediaClassNetwork: + s = "Network connectivity " + default: + s = "Unknown" + } + return +} + +func (t LLDPApplicationType) String() (s string) { + switch t { + case LLDPAppTypeReserved: + s = "Reserved" + case LLDPAppTypeVoice: + s = "Voice" + case LLDPappTypeVoiceSignaling: + s = "Voice Signaling" + case LLDPappTypeGuestVoice: + s = "Guest Voice" + case LLDPappTypeGuestVoiceSignaling: + s = "Guest Voice Signaling" + case LLDPappTypeSoftphoneVoice: + s = "Softphone Voice" + case LLDPappTypeVideoConferencing: + s = "Video Conferencing" + case LLDPappTypeStreamingVideo: + s = "Streaming Video" + case LLDPappTypeVideoSignaling: + s = "Video Signaling" + default: + s = "Unknown" + } + return +} + +func (t LLDPLocationFormat) String() (s string) { + switch t { + case LLDPLocationFormatInvalid: + s = "Invalid" + case LLDPLocationFormatCoordinate: + s = "Coordinate-based LCI" + case LLDPLocationFormatAddress: + s = "Address-based LCO" + case LLDPLocationFormatECS: + s = "ECS ELIN" + default: + s = "Unknown" + } + return +} + +func (t LLDPLocationAddressType) String() (s string) { + switch t { + case LLDPLocationAddressTypeLanguage: + s = "Language" + case LLDPLocationAddressTypeNational: + s = "National subdivisions (province, state, etc)" + case LLDPLocationAddressTypeCounty: + s = "County, parish, district" + case LLDPLocationAddressTypeCity: + s = "City, township" + case LLDPLocationAddressTypeCityDivision: + s = "City division, borough, ward" + case LLDPLocationAddressTypeNeighborhood: + s = "Neighborhood, block" + case LLDPLocationAddressTypeStreet: + s = "Street" + case LLDPLocationAddressTypeLeadingStreet: + s = "Leading street direction" + case LLDPLocationAddressTypeTrailingStreet: + s = "Trailing street suffix" + case LLDPLocationAddressTypeStreetSuffix: + s = "Street suffix" + case LLDPLocationAddressTypeHouseNum: + s = "House number" + case LLDPLocationAddressTypeHouseSuffix: + s = "House number suffix" + case LLDPLocationAddressTypeLandmark: + s = "Landmark or vanity address" + case LLDPLocationAddressTypeAdditional: + s = "Additional location information" + case LLDPLocationAddressTypeName: + s = "Name" + case LLDPLocationAddressTypePostal: + s = "Postal/ZIP code" + case LLDPLocationAddressTypeBuilding: + s = "Building" + case LLDPLocationAddressTypeUnit: + s = "Unit" + case LLDPLocationAddressTypeFloor: + s = "Floor" + case LLDPLocationAddressTypeRoom: + s = "Room number" + case LLDPLocationAddressTypePlace: + s = "Place type" + case LLDPLocationAddressTypeScript: + s = "Script" + default: + s = "Unknown" + } + return +} + +func checkLLDPTLVLen(v LinkLayerDiscoveryValue, l int) (err error) { + if len(v.Value) < l { + err = fmt.Errorf("Invalid TLV %v length %d (wanted mimimum %v", v.Type, len(v.Value), l) + } + return +} + +func checkLLDPOrgSpecificLen(o LLDPOrgSpecificTLV, l int) (err error) { + if len(o.Info) < l { + err = fmt.Errorf("Invalid Org Specific TLV %v length %d (wanted minimum %v)", o.SubType, len(o.Info), l) + } + return +} diff --git a/vendor/github.com/google/gopacket/layers/loopback.go b/vendor/github.com/google/gopacket/layers/loopback.go new file mode 100644 index 00000000..839f7607 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/loopback.go @@ -0,0 +1,80 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// Loopback contains the header for loopback encapsulation. This header is +// used by both BSD and OpenBSD style loopback decoding (pcap's DLT_NULL +// and DLT_LOOP, respectively). +type Loopback struct { + BaseLayer + Family ProtocolFamily +} + +// LayerType returns LayerTypeLoopback. +func (l *Loopback) LayerType() gopacket.LayerType { return LayerTypeLoopback } + +// DecodeFromBytes decodes the given bytes into this layer. +func (l *Loopback) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + return errors.New("Loopback packet too small") + } + + // The protocol could be either big-endian or little-endian, we're + // not sure. But we're PRETTY sure that the value is less than + // 256, so we can check the first two bytes. + var prot uint32 + if data[0] == 0 && data[1] == 0 { + prot = binary.BigEndian.Uint32(data[:4]) + } else { + prot = binary.LittleEndian.Uint32(data[:4]) + } + if prot > 0xFF { + return fmt.Errorf("Invalid loopback protocol %q", data[:4]) + } + + l.Family = ProtocolFamily(prot) + l.BaseLayer = BaseLayer{data[:4], data[4:]} + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (l *Loopback) CanDecode() gopacket.LayerClass { + return LayerTypeLoopback +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (l *Loopback) NextLayerType() gopacket.LayerType { + return l.Family.LayerType() +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +func (l *Loopback) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + binary.LittleEndian.PutUint32(bytes, uint32(l.Family)) + return nil +} + +func decodeLoopback(data []byte, p gopacket.PacketBuilder) error { + l := Loopback{} + if err := l.DecodeFromBytes(data, gopacket.NilDecodeFeedback); err != nil { + return err + } + p.AddLayer(&l) + return p.NextDecoder(l.Family) +} diff --git a/vendor/github.com/google/gopacket/layers/mpls.go b/vendor/github.com/google/gopacket/layers/mpls.go new file mode 100644 index 00000000..83079a09 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/mpls.go @@ -0,0 +1,87 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "github.com/google/gopacket" +) + +// MPLS is the MPLS packet header. +type MPLS struct { + BaseLayer + Label uint32 + TrafficClass uint8 + StackBottom bool + TTL uint8 +} + +// LayerType returns gopacket.LayerTypeMPLS. +func (m *MPLS) LayerType() gopacket.LayerType { return LayerTypeMPLS } + +// ProtocolGuessingDecoder attempts to guess the protocol of the bytes it's +// given, then decode the packet accordingly. Its algorithm for guessing is: +// If the packet starts with byte 0x45-0x4F: IPv4 +// If the packet starts with byte 0x60-0x6F: IPv6 +// Otherwise: Error +// See draft-hsmit-isis-aal5mux-00.txt for more detail on this approach. +type ProtocolGuessingDecoder struct{} + +func (ProtocolGuessingDecoder) Decode(data []byte, p gopacket.PacketBuilder) error { + switch data[0] { + // 0x40 | header_len, where header_len is at least 5. + case 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f: + return decodeIPv4(data, p) + // IPv6 can start with any byte whose first 4 bits are 0x6. + case 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f: + return decodeIPv6(data, p) + } + return errors.New("Unable to guess protocol of packet data") +} + +// MPLSPayloadDecoder is the decoder used to data encapsulated by each MPLS +// layer. MPLS contains no type information, so we have to explicitly decide +// which decoder to use. This is initially set to ProtocolGuessingDecoder, our +// simple attempt at guessing protocols based on the first few bytes of data +// available to us. However, if you know that in your environment MPLS always +// encapsulates a specific protocol, you may reset this. +var MPLSPayloadDecoder gopacket.Decoder = ProtocolGuessingDecoder{} + +func decodeMPLS(data []byte, p gopacket.PacketBuilder) error { + decoded := binary.BigEndian.Uint32(data[:4]) + mpls := &MPLS{ + Label: decoded >> 12, + TrafficClass: uint8(decoded>>9) & 0x7, + StackBottom: decoded&0x100 != 0, + TTL: uint8(decoded), + BaseLayer: BaseLayer{data[:4], data[4:]}, + } + p.AddLayer(mpls) + if mpls.StackBottom { + return p.NextDecoder(MPLSPayloadDecoder) + } + return p.NextDecoder(gopacket.DecodeFunc(decodeMPLS)) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (m *MPLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + encoded := m.Label << 12 + encoded |= uint32(m.TrafficClass) << 9 + encoded |= uint32(m.TTL) + if m.StackBottom { + encoded |= 0x100 + } + binary.BigEndian.PutUint32(bytes, encoded) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/mpls_test.go b/vendor/github.com/google/gopacket/layers/mpls_test.go new file mode 100644 index 00000000..e0a2b95e --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/mpls_test.go @@ -0,0 +1,96 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "reflect" + "testing" + + "github.com/google/gopacket" +) + +// testPacketMPLS +// Ethernet II, Src: cc:15:14:64:00:00 (cc:15:14:64:00:00), Dst: cc:13:14:64:00:01 (cc:13:14:64:00:01) +// MultiProtocol Label Switching Header, Label: 17, Exp: 0, S: 0, TTL: 254 +// MultiProtocol Label Switching Header, Label: 19, Exp: 0, S: 1, TTL: 254 +// Internet Protocol Version 4, Src: 12.0.0.1, Dst: 2.2.2.2 +// Internet Control Message Protocol +// 0000 cc 13 14 64 00 01 cc 15 14 64 00 00 88 47 00 01 ...d.....d...G.. +// 0010 10 fe 00 01 31 fe 45 00 00 64 00 39 00 00 fe 01 ....1.E..d.9.... +// 0020 ac 5b 0c 00 00 01 02 02 02 02 08 00 3a 6b 00 0b .[..........:k.. +// 0030 00 02 00 00 00 00 00 3e 43 94 ab cd ab cd ab cd .......>C....... +// 0040 ab cd ab cd ab cd ab cd ab cd ab cd ab cd ab cd ................ +// 0050 ab cd ab cd ab cd ab cd ab cd ab cd ab cd ab cd ................ +// 0060 ab cd ab cd ab cd ab cd ab cd ab cd ab cd ab cd ................ +// 0070 ab cd ab cd ab cd ab cd ab cd .......... + +var testPacketMPLS = []byte{ + 0xcc, 0x13, 0x14, 0x64, 0x00, 0x01, 0xcc, 0x15, 0x14, 0x64, 0x00, 0x00, 0x88, 0x47, 0x00, 0x01, + 0x10, 0xfe, 0x00, 0x01, 0x31, 0xfe, 0x45, 0x00, 0x00, 0x64, 0x00, 0x39, 0x00, 0x00, 0xfe, 0x01, + 0xac, 0x5b, 0x0c, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x08, 0x00, 0x3a, 0x6b, 0x00, 0x0b, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x43, 0x94, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, +} + +func TestPacketMPLS(t *testing.T) { + p := gopacket.NewPacket(testPacketMPLS, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeMPLS, LayerTypeMPLS, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + if got, ok := p.Layers()[1].(*MPLS); ok { + want := &MPLS{ + BaseLayer: BaseLayer{ + Contents: []byte{0x00, 0x01, 0x10, 0xfe}, + Payload: []byte{0x00, 0x01, 0x31, 0xfe, 0x45, 0x00, 0x00, 0x64, 0x00, 0x39, 0x00, 0x00, 0xfe, 0x01, + 0xac, 0x5b, 0x0c, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x08, 0x00, 0x3a, 0x6b, 0x00, 0x0b, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x43, 0x94, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd}, + }, + Label: 17, + TrafficClass: 0, + StackBottom: false, + TTL: 254, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("MPLS layer 1 mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } + if got, ok := p.Layers()[2].(*MPLS); ok { + want := &MPLS{ + BaseLayer: BaseLayer{ + Contents: []byte{0x00, 0x01, 0x31, 0xfe}, + Payload: []byte{0x45, 0x00, 0x00, 0x64, 0x00, 0x39, 0x00, 0x00, 0xfe, 0x01, + 0xac, 0x5b, 0x0c, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x08, 0x00, 0x3a, 0x6b, 0x00, 0x0b, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x43, 0x94, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd}, + }, + Label: 19, + TrafficClass: 0, + StackBottom: true, + TTL: 254, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("MPLS layer 2 mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } +} + +func BenchmarkDecodePacketMPLS(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketMPLS, LinkTypeEthernet, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/ndp.go b/vendor/github.com/google/gopacket/layers/ndp.go new file mode 100644 index 00000000..f7ca1b26 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ndp.go @@ -0,0 +1,611 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Enum types courtesy of... +// http://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors/packet-ndp.c + +package layers + +import ( + "fmt" + "github.com/google/gopacket" + "net" +) + +type NDPChassisType uint8 + +// Nortel Chassis Types +const ( + NDPChassisother NDPChassisType = 1 + NDPChassis3000 NDPChassisType = 2 + NDPChassis3030 NDPChassisType = 3 + NDPChassis2310 NDPChassisType = 4 + NDPChassis2810 NDPChassisType = 5 + NDPChassis2912 NDPChassisType = 6 + NDPChassis2914 NDPChassisType = 7 + NDPChassis271x NDPChassisType = 8 + NDPChassis2813 NDPChassisType = 9 + NDPChassis2814 NDPChassisType = 10 + NDPChassis2915 NDPChassisType = 11 + NDPChassis5000 NDPChassisType = 12 + NDPChassis2813SA NDPChassisType = 13 + NDPChassis2814SA NDPChassisType = 14 + NDPChassis810M NDPChassisType = 15 + NDPChassisEthercell NDPChassisType = 16 + NDPChassis5005 NDPChassisType = 17 + NDPChassisAlcatelEWC NDPChassisType = 18 + NDPChassis2715SA NDPChassisType = 20 + NDPChassis2486 NDPChassisType = 21 + NDPChassis28000series NDPChassisType = 22 + NDPChassis23000series NDPChassisType = 23 + NDPChassis5DN00xseries NDPChassisType = 24 + NDPChassisBayStackEthernet NDPChassisType = 25 + NDPChassis23100series NDPChassisType = 26 + NDPChassis100BaseTHub NDPChassisType = 27 + NDPChassis3000FastEthernet NDPChassisType = 28 + NDPChassisOrionSwitch NDPChassisType = 29 + NDPChassisDDS NDPChassisType = 31 + NDPChassisCentillion6slot NDPChassisType = 32 + NDPChassisCentillion12slot NDPChassisType = 33 + NDPChassisCentillion1slot NDPChassisType = 34 + NDPChassisBayStack301 NDPChassisType = 35 + NDPChassisBayStackTokenRingHub NDPChassisType = 36 + NDPChassisFVCMultimediaSwitch NDPChassisType = 37 + NDPChassisSwitchNode NDPChassisType = 38 + NDPChassisBayStack302Switch NDPChassisType = 39 + NDPChassisBayStack350Switch NDPChassisType = 40 + NDPChassisBayStack150EthernetHub NDPChassisType = 41 + NDPChassisCentillion50NSwitch NDPChassisType = 42 + NDPChassisCentillion50TSwitch NDPChassisType = 43 + NDPChassisBayStack303304Switches NDPChassisType = 44 + NDPChassisBayStack200EthernetHub NDPChassisType = 45 + NDPChassisBayStack25010100EthernetHub NDPChassisType = 46 + NDPChassisBayStack450101001000Switches NDPChassisType = 48 + NDPChassisBayStack41010100Switches NDPChassisType = 49 + NDPChassisPassport1200L3Switch NDPChassisType = 50 + NDPChassisPassport1250L3Switch NDPChassisType = 51 + NDPChassisPassport1100L3Switch NDPChassisType = 52 + NDPChassisPassport1150L3Switch NDPChassisType = 53 + NDPChassisPassport1050L3Switch NDPChassisType = 54 + NDPChassisPassport1051L3Switch NDPChassisType = 55 + NDPChassisPassport8610L3Switch NDPChassisType = 56 + NDPChassisPassport8606L3Switch NDPChassisType = 57 + NDPChassisPassport8010 NDPChassisType = 58 + NDPChassisPassport8006 NDPChassisType = 59 + NDPChassisBayStack670wirelessaccesspoint NDPChassisType = 60 + NDPChassisPassport740 NDPChassisType = 61 + NDPChassisPassport750 NDPChassisType = 62 + NDPChassisPassport790 NDPChassisType = 63 + NDPChassisBusinessPolicySwitch200010100Switches NDPChassisType = 64 + NDPChassisPassport8110L2Switch NDPChassisType = 65 + NDPChassisPassport8106L2Switch NDPChassisType = 66 + NDPChassisBayStack3580GigSwitch NDPChassisType = 67 + NDPChassisBayStack10PowerSupplyUnit NDPChassisType = 68 + NDPChassisBayStack42010100Switch NDPChassisType = 69 + NDPChassisOPTeraMetro1200EthernetServiceModule NDPChassisType = 70 + NDPChassisOPTera8010co NDPChassisType = 71 + NDPChassisOPTera8610coL3Switch NDPChassisType = 72 + NDPChassisOPTera8110coL2Switch NDPChassisType = 73 + NDPChassisOPTera8003 NDPChassisType = 74 + NDPChassisOPTera8603L3Switch NDPChassisType = 75 + NDPChassisOPTera8103L2Switch NDPChassisType = 76 + NDPChassisBayStack380101001000Switch NDPChassisType = 77 + NDPChassisEthernetSwitch47048T NDPChassisType = 78 + NDPChassisOPTeraMetro1450EthernetServiceModule NDPChassisType = 79 + NDPChassisOPTeraMetro1400EthernetServiceModule NDPChassisType = 80 + NDPChassisAlteonSwitchFamily NDPChassisType = 81 + NDPChassisEthernetSwitch46024TPWR NDPChassisType = 82 + NDPChassisOPTeraMetro8010OPML2Switch NDPChassisType = 83 + NDPChassisOPTeraMetro8010coOPML2Switch NDPChassisType = 84 + NDPChassisOPTeraMetro8006OPML2Switch NDPChassisType = 85 + NDPChassisOPTeraMetro8003OPML2Switch NDPChassisType = 86 + NDPChassisAlteon180e NDPChassisType = 87 + NDPChassisAlteonAD3 NDPChassisType = 88 + NDPChassisAlteon184 NDPChassisType = 89 + NDPChassisAlteonAD4 NDPChassisType = 90 + NDPChassisPassport1424L3Switch NDPChassisType = 91 + NDPChassisPassport1648L3Switch NDPChassisType = 92 + NDPChassisPassport1612L3Switch NDPChassisType = 93 + NDPChassisPassport1624L3Switch NDPChassisType = 94 + NDPChassisBayStack38024FFiber1000Switch NDPChassisType = 95 + NDPChassisEthernetRoutingSwitch551024T NDPChassisType = 96 + NDPChassisEthernetRoutingSwitch551048T NDPChassisType = 97 + NDPChassisEthernetSwitch47024T NDPChassisType = 98 + NDPChassisNortelNetworksWirelessLANAccessPoint2220 NDPChassisType = 99 + NDPChassisPassportRBS2402L3Switch NDPChassisType = 100 + NDPChassisAlteonApplicationSwitch2424 NDPChassisType = 101 + NDPChassisAlteonApplicationSwitch2224 NDPChassisType = 102 + NDPChassisAlteonApplicationSwitch2208 NDPChassisType = 103 + NDPChassisAlteonApplicationSwitch2216 NDPChassisType = 104 + NDPChassisAlteonApplicationSwitch3408 NDPChassisType = 105 + NDPChassisAlteonApplicationSwitch3416 NDPChassisType = 106 + NDPChassisNortelNetworksWirelessLANSecuritySwitch2250 NDPChassisType = 107 + NDPChassisEthernetSwitch42548T NDPChassisType = 108 + NDPChassisEthernetSwitch42524T NDPChassisType = 109 + NDPChassisNortelNetworksWirelessLANAccessPoint2221 NDPChassisType = 110 + NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch NDPChassisType = 111 + NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch NDPChassisType = 112 + NDPChassisPassport830010slotchassis NDPChassisType = 113 + NDPChassisPassport83006slotchassis NDPChassisType = 114 + NDPChassisEthernetRoutingSwitch552024TPWR NDPChassisType = 115 + NDPChassisEthernetRoutingSwitch552048TPWR NDPChassisType = 116 + NDPChassisNortelNetworksVPNGateway3050 NDPChassisType = 117 + NDPChassisAlteonSSL31010100 NDPChassisType = 118 + NDPChassisAlteonSSL31010100Fiber NDPChassisType = 119 + NDPChassisAlteonSSL31010100FIPS NDPChassisType = 120 + NDPChassisAlteonSSL410101001000 NDPChassisType = 121 + NDPChassisAlteonSSL410101001000Fiber NDPChassisType = 122 + NDPChassisAlteonApplicationSwitch2424SSL NDPChassisType = 123 + NDPChassisEthernetSwitch32524T NDPChassisType = 124 + NDPChassisEthernetSwitch32524G NDPChassisType = 125 + NDPChassisNortelNetworksWirelessLANAccessPoint2225 NDPChassisType = 126 + NDPChassisNortelNetworksWirelessLANSecuritySwitch2270 NDPChassisType = 127 + NDPChassis24portEthernetSwitch47024TPWR NDPChassisType = 128 + NDPChassis48portEthernetSwitch47048TPWR NDPChassisType = 129 + NDPChassisEthernetRoutingSwitch553024TFD NDPChassisType = 130 + NDPChassisEthernetSwitch351024T NDPChassisType = 131 + NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch NDPChassisType = 132 + NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch NDPChassisType = 133 + NDPChassisNortelSecureAccessSwitch NDPChassisType = 134 + NDPChassisNortelNetworksVPNGateway3070 NDPChassisType = 135 + NDPChassisOPTeraMetro3500 NDPChassisType = 136 + NDPChassisSMBBES101024T NDPChassisType = 137 + NDPChassisSMBBES101048T NDPChassisType = 138 + NDPChassisSMBBES102024TPWR NDPChassisType = 139 + NDPChassisSMBBES102048TPWR NDPChassisType = 140 + NDPChassisSMBBES201024T NDPChassisType = 141 + NDPChassisSMBBES201048T NDPChassisType = 142 + NDPChassisSMBBES202024TPWR NDPChassisType = 143 + NDPChassisSMBBES202048TPWR NDPChassisType = 144 + NDPChassisSMBBES11024T NDPChassisType = 145 + NDPChassisSMBBES11048T NDPChassisType = 146 + NDPChassisSMBBES12024TPWR NDPChassisType = 147 + NDPChassisSMBBES12048TPWR NDPChassisType = 148 + NDPChassisSMBBES21024T NDPChassisType = 149 + NDPChassisSMBBES21048T NDPChassisType = 150 + NDPChassisSMBBES22024TPWR NDPChassisType = 151 + NDPChassisSMBBES22048TPWR NDPChassisType = 152 + NDPChassisOME6500 NDPChassisType = 153 + NDPChassisEthernetRoutingSwitch4548GT NDPChassisType = 154 + NDPChassisEthernetRoutingSwitch4548GTPWR NDPChassisType = 155 + NDPChassisEthernetRoutingSwitch4550T NDPChassisType = 156 + NDPChassisEthernetRoutingSwitch4550TPWR NDPChassisType = 157 + NDPChassisEthernetRoutingSwitch4526FX NDPChassisType = 158 + NDPChassisEthernetRoutingSwitch250026T NDPChassisType = 159 + NDPChassisEthernetRoutingSwitch250026TPWR NDPChassisType = 160 + NDPChassisEthernetRoutingSwitch250050T NDPChassisType = 161 + NDPChassisEthernetRoutingSwitch250050TPWR NDPChassisType = 162 +) + +type NDPBackplaneType uint8 + +// Nortel Backplane Types +const ( + NDPBackplaneOther NDPBackplaneType = 1 + NDPBackplaneEthernet NDPBackplaneType = 2 + NDPBackplaneEthernetTokenring NDPBackplaneType = 3 + NDPBackplaneEthernetFDDI NDPBackplaneType = 4 + NDPBackplaneEthernetTokenringFDDI NDPBackplaneType = 5 + NDPBackplaneEthernetTokenringRedundantPower NDPBackplaneType = 6 + NDPBackplaneEthernetTokenringFDDIRedundantPower NDPBackplaneType = 7 + NDPBackplaneTokenRing NDPBackplaneType = 8 + NDPBackplaneEthernetTokenringFastEthernet NDPBackplaneType = 9 + NDPBackplaneEthernetFastEthernet NDPBackplaneType = 10 + NDPBackplaneEthernetTokenringFastEthernetRedundantPower NDPBackplaneType = 11 + NDPBackplaneEthernetFastEthernetGigabitEthernet NDPBackplaneType = 12 +) + +type NDPState uint8 + +// Device State +const ( + NDPStateTopology NDPState = 1 + NDPStateHeartbeat NDPState = 2 + NDPStateNew NDPState = 3 +) + +// NortelDiscovery is a packet layer containing the Nortel Discovery Protocol. +type NortelDiscovery struct { + BaseLayer + IPAddress net.IP + SegmentID []byte + Chassis NDPChassisType + Backplane NDPBackplaneType + State NDPState + NumLinks uint8 +} + +// LayerType returns gopacket.LayerTypeNortelDiscovery. +func (c *NortelDiscovery) LayerType() gopacket.LayerType { + return LayerTypeNortelDiscovery +} + +func decodeNortelDiscovery(data []byte, p gopacket.PacketBuilder) error { + c := &NortelDiscovery{} + if len(data) < 11 { + return fmt.Errorf("Invalid NortelDiscovery packet length %d", len(data)) + } + c.IPAddress = data[0:4] + c.SegmentID = data[4:7] + c.Chassis = NDPChassisType(data[7]) + c.Backplane = NDPBackplaneType(data[8]) + c.State = NDPState(data[9]) + c.NumLinks = uint8(data[10]) + p.AddLayer(c) + return nil +} + +func (t NDPChassisType) String() (s string) { + switch t { + case NDPChassisother: + s = "other" + case NDPChassis3000: + s = "3000" + case NDPChassis3030: + s = "3030" + case NDPChassis2310: + s = "2310" + case NDPChassis2810: + s = "2810" + case NDPChassis2912: + s = "2912" + case NDPChassis2914: + s = "2914" + case NDPChassis271x: + s = "271x" + case NDPChassis2813: + s = "2813" + case NDPChassis2814: + s = "2814" + case NDPChassis2915: + s = "2915" + case NDPChassis5000: + s = "5000" + case NDPChassis2813SA: + s = "2813SA" + case NDPChassis2814SA: + s = "2814SA" + case NDPChassis810M: + s = "810M" + case NDPChassisEthercell: + s = "Ethercell" + case NDPChassis5005: + s = "5005" + case NDPChassisAlcatelEWC: + s = "Alcatel Ethernet workgroup conc." + case NDPChassis2715SA: + s = "2715SA" + case NDPChassis2486: + s = "2486" + case NDPChassis28000series: + s = "28000 series" + case NDPChassis23000series: + s = "23000 series" + case NDPChassis5DN00xseries: + s = "5DN00x series" + case NDPChassisBayStackEthernet: + s = "BayStack Ethernet" + case NDPChassis23100series: + s = "23100 series" + case NDPChassis100BaseTHub: + s = "100Base-T Hub" + case NDPChassis3000FastEthernet: + s = "3000 Fast Ethernet" + case NDPChassisOrionSwitch: + s = "Orion switch" + case NDPChassisDDS: + s = "DDS" + case NDPChassisCentillion6slot: + s = "Centillion (6 slot)" + case NDPChassisCentillion12slot: + s = "Centillion (12 slot)" + case NDPChassisCentillion1slot: + s = "Centillion (1 slot)" + case NDPChassisBayStack301: + s = "BayStack 301" + case NDPChassisBayStackTokenRingHub: + s = "BayStack TokenRing Hub" + case NDPChassisFVCMultimediaSwitch: + s = "FVC Multimedia Switch" + case NDPChassisSwitchNode: + s = "Switch Node" + case NDPChassisBayStack302Switch: + s = "BayStack 302 Switch" + case NDPChassisBayStack350Switch: + s = "BayStack 350 Switch" + case NDPChassisBayStack150EthernetHub: + s = "BayStack 150 Ethernet Hub" + case NDPChassisCentillion50NSwitch: + s = "Centillion 50N switch" + case NDPChassisCentillion50TSwitch: + s = "Centillion 50T switch" + case NDPChassisBayStack303304Switches: + s = "BayStack 303 and 304 Switches" + case NDPChassisBayStack200EthernetHub: + s = "BayStack 200 Ethernet Hub" + case NDPChassisBayStack25010100EthernetHub: + s = "BayStack 250 10/100 Ethernet Hub" + case NDPChassisBayStack450101001000Switches: + s = "BayStack 450 10/100/1000 Switches" + case NDPChassisBayStack41010100Switches: + s = "BayStack 410 10/100 Switches" + case NDPChassisPassport1200L3Switch: + s = "Passport 1200 L3 Switch" + case NDPChassisPassport1250L3Switch: + s = "Passport 1250 L3 Switch" + case NDPChassisPassport1100L3Switch: + s = "Passport 1100 L3 Switch" + case NDPChassisPassport1150L3Switch: + s = "Passport 1150 L3 Switch" + case NDPChassisPassport1050L3Switch: + s = "Passport 1050 L3 Switch" + case NDPChassisPassport1051L3Switch: + s = "Passport 1051 L3 Switch" + case NDPChassisPassport8610L3Switch: + s = "Passport 8610 L3 Switch" + case NDPChassisPassport8606L3Switch: + s = "Passport 8606 L3 Switch" + case NDPChassisPassport8010: + s = "Passport 8010" + case NDPChassisPassport8006: + s = "Passport 8006" + case NDPChassisBayStack670wirelessaccesspoint: + s = "BayStack 670 wireless access point" + case NDPChassisPassport740: + s = "Passport 740" + case NDPChassisPassport750: + s = "Passport 750" + case NDPChassisPassport790: + s = "Passport 790" + case NDPChassisBusinessPolicySwitch200010100Switches: + s = "Business Policy Switch 2000 10/100 Switches" + case NDPChassisPassport8110L2Switch: + s = "Passport 8110 L2 Switch" + case NDPChassisPassport8106L2Switch: + s = "Passport 8106 L2 Switch" + case NDPChassisBayStack3580GigSwitch: + s = "BayStack 3580 Gig Switch" + case NDPChassisBayStack10PowerSupplyUnit: + s = "BayStack 10 Power Supply Unit" + case NDPChassisBayStack42010100Switch: + s = "BayStack 420 10/100 Switch" + case NDPChassisOPTeraMetro1200EthernetServiceModule: + s = "OPTera Metro 1200 Ethernet Service Module" + case NDPChassisOPTera8010co: + s = "OPTera 8010co" + case NDPChassisOPTera8610coL3Switch: + s = "OPTera 8610co L3 switch" + case NDPChassisOPTera8110coL2Switch: + s = "OPTera 8110co L2 switch" + case NDPChassisOPTera8003: + s = "OPTera 8003" + case NDPChassisOPTera8603L3Switch: + s = "OPTera 8603 L3 switch" + case NDPChassisOPTera8103L2Switch: + s = "OPTera 8103 L2 switch" + case NDPChassisBayStack380101001000Switch: + s = "BayStack 380 10/100/1000 Switch" + case NDPChassisEthernetSwitch47048T: + s = "Ethernet Switch 470-48T" + case NDPChassisOPTeraMetro1450EthernetServiceModule: + s = "OPTera Metro 1450 Ethernet Service Module" + case NDPChassisOPTeraMetro1400EthernetServiceModule: + s = "OPTera Metro 1400 Ethernet Service Module" + case NDPChassisAlteonSwitchFamily: + s = "Alteon Switch Family" + case NDPChassisEthernetSwitch46024TPWR: + s = "Ethernet Switch 460-24T-PWR" + case NDPChassisOPTeraMetro8010OPML2Switch: + s = "OPTera Metro 8010 OPM L2 Switch" + case NDPChassisOPTeraMetro8010coOPML2Switch: + s = "OPTera Metro 8010co OPM L2 Switch" + case NDPChassisOPTeraMetro8006OPML2Switch: + s = "OPTera Metro 8006 OPM L2 Switch" + case NDPChassisOPTeraMetro8003OPML2Switch: + s = "OPTera Metro 8003 OPM L2 Switch" + case NDPChassisAlteon180e: + s = "Alteon 180e" + case NDPChassisAlteonAD3: + s = "Alteon AD3" + case NDPChassisAlteon184: + s = "Alteon 184" + case NDPChassisAlteonAD4: + s = "Alteon AD4" + case NDPChassisPassport1424L3Switch: + s = "Passport 1424 L3 switch" + case NDPChassisPassport1648L3Switch: + s = "Passport 1648 L3 switch" + case NDPChassisPassport1612L3Switch: + s = "Passport 1612 L3 switch" + case NDPChassisPassport1624L3Switch: + s = "Passport 1624 L3 switch" + case NDPChassisBayStack38024FFiber1000Switch: + s = "BayStack 380-24F Fiber 1000 Switch" + case NDPChassisEthernetRoutingSwitch551024T: + s = "Ethernet Routing Switch 5510-24T" + case NDPChassisEthernetRoutingSwitch551048T: + s = "Ethernet Routing Switch 5510-48T" + case NDPChassisEthernetSwitch47024T: + s = "Ethernet Switch 470-24T" + case NDPChassisNortelNetworksWirelessLANAccessPoint2220: + s = "Nortel Networks Wireless LAN Access Point 2220" + case NDPChassisPassportRBS2402L3Switch: + s = "Passport RBS 2402 L3 switch" + case NDPChassisAlteonApplicationSwitch2424: + s = "Alteon Application Switch 2424" + case NDPChassisAlteonApplicationSwitch2224: + s = "Alteon Application Switch 2224" + case NDPChassisAlteonApplicationSwitch2208: + s = "Alteon Application Switch 2208" + case NDPChassisAlteonApplicationSwitch2216: + s = "Alteon Application Switch 2216" + case NDPChassisAlteonApplicationSwitch3408: + s = "Alteon Application Switch 3408" + case NDPChassisAlteonApplicationSwitch3416: + s = "Alteon Application Switch 3416" + case NDPChassisNortelNetworksWirelessLANSecuritySwitch2250: + s = "Nortel Networks Wireless LAN SecuritySwitch 2250" + case NDPChassisEthernetSwitch42548T: + s = "Ethernet Switch 425-48T" + case NDPChassisEthernetSwitch42524T: + s = "Ethernet Switch 425-24T" + case NDPChassisNortelNetworksWirelessLANAccessPoint2221: + s = "Nortel Networks Wireless LAN Access Point 2221" + case NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch: + s = "Nortel Metro Ethernet Service Unit 24-T SPF switch" + case NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch: + s = " Nortel Metro Ethernet Service Unit 24-T LX DC switch" + case NDPChassisPassport830010slotchassis: + s = "Passport 8300 10-slot chassis" + case NDPChassisPassport83006slotchassis: + s = "Passport 8300 6-slot chassis" + case NDPChassisEthernetRoutingSwitch552024TPWR: + s = "Ethernet Routing Switch 5520-24T-PWR" + case NDPChassisEthernetRoutingSwitch552048TPWR: + s = "Ethernet Routing Switch 5520-48T-PWR" + case NDPChassisNortelNetworksVPNGateway3050: + s = "Nortel Networks VPN Gateway 3050" + case NDPChassisAlteonSSL31010100: + s = "Alteon SSL 310 10/100" + case NDPChassisAlteonSSL31010100Fiber: + s = "Alteon SSL 310 10/100 Fiber" + case NDPChassisAlteonSSL31010100FIPS: + s = "Alteon SSL 310 10/100 FIPS" + case NDPChassisAlteonSSL410101001000: + s = "Alteon SSL 410 10/100/1000" + case NDPChassisAlteonSSL410101001000Fiber: + s = "Alteon SSL 410 10/100/1000 Fiber" + case NDPChassisAlteonApplicationSwitch2424SSL: + s = "Alteon Application Switch 2424-SSL" + case NDPChassisEthernetSwitch32524T: + s = "Ethernet Switch 325-24T" + case NDPChassisEthernetSwitch32524G: + s = "Ethernet Switch 325-24G" + case NDPChassisNortelNetworksWirelessLANAccessPoint2225: + s = "Nortel Networks Wireless LAN Access Point 2225" + case NDPChassisNortelNetworksWirelessLANSecuritySwitch2270: + s = "Nortel Networks Wireless LAN SecuritySwitch 2270" + case NDPChassis24portEthernetSwitch47024TPWR: + s = "24-port Ethernet Switch 470-24T-PWR" + case NDPChassis48portEthernetSwitch47048TPWR: + s = "48-port Ethernet Switch 470-48T-PWR" + case NDPChassisEthernetRoutingSwitch553024TFD: + s = "Ethernet Routing Switch 5530-24TFD" + case NDPChassisEthernetSwitch351024T: + s = "Ethernet Switch 3510-24T" + case NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch: + s = "Nortel Metro Ethernet Service Unit 12G AC L3 switch" + case NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch: + s = "Nortel Metro Ethernet Service Unit 12G DC L3 switch" + case NDPChassisNortelSecureAccessSwitch: + s = "Nortel Secure Access Switch" + case NDPChassisNortelNetworksVPNGateway3070: + s = "Nortel Networks VPN Gateway 3070" + case NDPChassisOPTeraMetro3500: + s = "OPTera Metro 3500" + case NDPChassisSMBBES101024T: + s = "SMB BES 1010 24T" + case NDPChassisSMBBES101048T: + s = "SMB BES 1010 48T" + case NDPChassisSMBBES102024TPWR: + s = "SMB BES 1020 24T PWR" + case NDPChassisSMBBES102048TPWR: + s = "SMB BES 1020 48T PWR" + case NDPChassisSMBBES201024T: + s = "SMB BES 2010 24T" + case NDPChassisSMBBES201048T: + s = "SMB BES 2010 48T" + case NDPChassisSMBBES202024TPWR: + s = "SMB BES 2020 24T PWR" + case NDPChassisSMBBES202048TPWR: + s = "SMB BES 2020 48T PWR" + case NDPChassisSMBBES11024T: + s = "SMB BES 110 24T" + case NDPChassisSMBBES11048T: + s = "SMB BES 110 48T" + case NDPChassisSMBBES12024TPWR: + s = "SMB BES 120 24T PWR" + case NDPChassisSMBBES12048TPWR: + s = "SMB BES 120 48T PWR" + case NDPChassisSMBBES21024T: + s = "SMB BES 210 24T" + case NDPChassisSMBBES21048T: + s = "SMB BES 210 48T" + case NDPChassisSMBBES22024TPWR: + s = "SMB BES 220 24T PWR" + case NDPChassisSMBBES22048TPWR: + s = "SMB BES 220 48T PWR" + case NDPChassisOME6500: + s = "OME 6500" + case NDPChassisEthernetRoutingSwitch4548GT: + s = "Ethernet Routing Switch 4548GT" + case NDPChassisEthernetRoutingSwitch4548GTPWR: + s = "Ethernet Routing Switch 4548GT-PWR" + case NDPChassisEthernetRoutingSwitch4550T: + s = "Ethernet Routing Switch 4550T" + case NDPChassisEthernetRoutingSwitch4550TPWR: + s = "Ethernet Routing Switch 4550T-PWR" + case NDPChassisEthernetRoutingSwitch4526FX: + s = "Ethernet Routing Switch 4526FX" + case NDPChassisEthernetRoutingSwitch250026T: + s = "Ethernet Routing Switch 2500-26T" + case NDPChassisEthernetRoutingSwitch250026TPWR: + s = "Ethernet Routing Switch 2500-26T-PWR" + case NDPChassisEthernetRoutingSwitch250050T: + s = "Ethernet Routing Switch 2500-50T" + case NDPChassisEthernetRoutingSwitch250050TPWR: + s = "Ethernet Routing Switch 2500-50T-PWR" + default: + s = "Unknown" + } + return +} + +func (t NDPBackplaneType) String() (s string) { + switch t { + case NDPBackplaneOther: + s = "Other" + case NDPBackplaneEthernet: + s = "Ethernet" + case NDPBackplaneEthernetTokenring: + s = "Ethernet and Tokenring" + case NDPBackplaneEthernetFDDI: + s = "Ethernet and FDDI" + case NDPBackplaneEthernetTokenringFDDI: + s = "Ethernet, Tokenring and FDDI" + case NDPBackplaneEthernetTokenringRedundantPower: + s = "Ethernet and Tokenring with redundant power" + case NDPBackplaneEthernetTokenringFDDIRedundantPower: + s = "Ethernet, Tokenring, FDDI with redundant power" + case NDPBackplaneTokenRing: + s = "Token Ring" + case NDPBackplaneEthernetTokenringFastEthernet: + s = "Ethernet, Tokenring and Fast Ethernet" + case NDPBackplaneEthernetFastEthernet: + s = "Ethernet and Fast Ethernet" + case NDPBackplaneEthernetTokenringFastEthernetRedundantPower: + s = "Ethernet, Tokenring, Fast Ethernet with redundant power" + case NDPBackplaneEthernetFastEthernetGigabitEthernet: + s = "Ethernet, Fast Ethernet and Gigabit Ethernet" + default: + s = "Unknown" + } + return +} + +func (t NDPState) String() (s string) { + switch t { + case NDPStateTopology: + s = "Topology Change" + case NDPStateHeartbeat: + s = "Heartbeat" + case NDPStateNew: + s = "New" + default: + s = "Unknown" + } + return +} diff --git a/vendor/github.com/google/gopacket/layers/ntp.go b/vendor/github.com/google/gopacket/layers/ntp.go new file mode 100644 index 00000000..33c15b3b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ntp.go @@ -0,0 +1,416 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +// +//****************************************************************************** + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +//****************************************************************************** +// +// Network Time Protocol (NTP) Decoding Layer +// ------------------------------------------ +// This file provides a GoPacket decoding layer for NTP. +// +//****************************************************************************** +// +// About The Network Time Protocol (NTP) +// ------------------------------------- +// NTP is a protocol that enables computers on the internet to set their +// clocks to the correct time (or to a time that is acceptably close to the +// correct time). NTP runs on top of UDP. +// +// There have been a series of versions of the NTP protocol. The latest +// version is V4 and is specified in RFC 5905: +// http://www.ietf.org/rfc/rfc5905.txt +// +//****************************************************************************** +// +// References +// ---------- +// +// Wikipedia's NTP entry: +// https://en.wikipedia.org/wiki/Network_Time_Protocol +// This is the best place to get an overview of NTP. +// +// Network Time Protocol Home Website: +// http://www.ntp.org/ +// This appears to be the official website of NTP. +// +// List of current NTP Protocol RFCs: +// http://www.ntp.org/rfc.html +// +// RFC 958: "Network Time Protocol (NTP)" (1985) +// https://tools.ietf.org/html/rfc958 +// This is the original NTP specification. +// +// RFC 1305: "Network Time Protocol (Version 3) Specification, Implementation and Analysis" (1992) +// https://tools.ietf.org/html/rfc1305 +// The protocol was updated in 1992 yielding NTP V3. +// +// RFC 5905: "Network Time Protocol Version 4: Protocol and Algorithms Specification" (2010) +// https://www.ietf.org/rfc/rfc5905.txt +// The protocol was updated in 2010 yielding NTP V4. +// V4 is backwards compatible with all previous versions of NTP. +// +// RFC 5906: "Network Time Protocol Version 4: Autokey Specification" +// https://tools.ietf.org/html/rfc5906 +// This document addresses the security of the NTP protocol +// and is probably not relevant to this package. +// +// RFC 5907: "Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4)" +// https://tools.ietf.org/html/rfc5907 +// This document addresses the management of NTP servers and +// is probably not relevant to this package. +// +// RFC 5908: "Network Time Protocol (NTP) Server Option for DHCPv6" +// https://tools.ietf.org/html/rfc5908 +// This document addresses the use of NTP in DHCPv6 and is +// probably not relevant to this package. +// +// "Let's make a NTP Client in C" +// https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html +// This web page contains useful information about the details of NTP, +// including an NTP record struture in C, and C code. +// +// "NTP Packet Header (NTP Reference Implementation) (Computer Network Time Synchronization)" +// http://what-when-how.com/computer-network-time-synchronization/ +// ntp-packet-header-ntp-reference-implementation-computer-network-time-synchronization/ +// This web page contains useful information on the details of NTP. +// +// "Technical information - NTP Data Packet" +// https://www.meinbergglobal.com/english/info/ntp-packet.htm +// This page has a helpful diagram of an NTP V4 packet. +// +//****************************************************************************** +// +// Obsolete References +// ------------------- +// +// RFC 1119: "RFC-1119 "Network Time Protocol (Version 2) Specification and Implementation" (1989) +// https://tools.ietf.org/html/rfc1119 +// Version 2 was drafted in 1989. +// It is unclear whether V2 was ever implememented or whether the +// ideas ended up in V3 (which was implemented in 1992). +// +// RFC 1361: "Simple Network Time Protocol (SNTP)" +// https://tools.ietf.org/html/rfc1361 +// This document is obsoleted by RFC 1769 and is included only for completeness. +// +// RFC 1769: "Simple Network Time Protocol (SNTP)" +// https://tools.ietf.org/html/rfc1769 +// This document is obsoleted by RFC 2030 and RFC 4330 and is included only for completeness. +// +// RFC 2030: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI" +// https://tools.ietf.org/html/rfc2030 +// This document is obsoleted by RFC 4330 and is included only for completeness. +// +// RFC 4330: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI" +// https://tools.ietf.org/html/rfc4330 +// This document is obsoleted by RFC 5905 and is included only for completeness. +// +//****************************************************************************** +// +// Endian And Bit Numbering Issues +// ------------------------------- +// +// Endian and bit numbering issues can be confusing. Here is some +// clarification: +// +// ENDIAN: Values are sent big endian. +// https://en.wikipedia.org/wiki/Endianness +// +// BIT NUMBERING: Bits are numbered 0 upwards from the most significant +// bit to the least significant bit. This means that if there is a 32-bit +// value, the most significant bit is called bit 0 and the least +// significant bit is called bit 31. +// +// See RFC 791 Appendix B for more discussion. +// +//****************************************************************************** +// +// NTP V3 and V4 Packet Format +// --------------------------- +// NTP packets are UDP packets whose payload contains an NTP record. +// +// The NTP RFC defines the format of the NTP record. +// +// There have been four versions of the protocol: +// +// V1 in 1985 +// V2 in 1989 +// V3 in 1992 +// V4 in 2010 +// +// It is clear that V1 and V2 are obsolete, and there is no need to +// cater for these formats. +// +// V3 and V4 essentially use the same format, with V4 adding some optional +// fields on the end. So this package supports the V3 and V4 formats. +// +// The current version of NTP (NTP V4)'s RFC (V4 - RFC 5905) contains +// the following diagram for the NTP record format: + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |LI | VN |Mode | Stratum | Poll | Precision | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Root Delay | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Root Dispersion | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Reference ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Reference Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Origin Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Receive Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Transmit Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Extension Field 1 (variable) . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Extension Field 2 (variable) . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Key Identifier | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// | dgst (128) | +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// From http://www.ietf.org/rfc/rfc5905.txt +// +// The fields "Extension Field 1 (variable)" and later are optional fields, +// and so we can set a minimum NTP record size of 48 bytes. +// +const ntpMinimumRecordSizeInBytes int = 48 + +//****************************************************************************** + +// NTP Type +// -------- +// Type NTP implements the DecodingLayer interface. Each NTP object +// represents in a structured form the NTP record present as the UDP +// payload in an NTP UDP packet. +// + +type NTPLeapIndicator uint8 +type NTPVersion uint8 +type NTPMode uint8 +type NTPStratum uint8 +type NTPLog2Seconds int8 +type NTPFixed16Seconds uint32 +type NTPReferenceID uint32 +type NTPTimestamp uint64 + +type NTP struct { + BaseLayer // Stores the packet bytes and payload bytes. + + LeapIndicator NTPLeapIndicator // [0,3]. Indicates whether leap second(s) is to be added. + Version NTPVersion // [0,7]. Version of the NTP protocol. + Mode NTPMode // [0,7]. Mode. + Stratum NTPStratum // [0,255]. Stratum of time server in the server tree. + Poll NTPLog2Seconds // [-128,127]. The maximum interval between successive messages, in log2 seconds. + Precision NTPLog2Seconds // [-128,127]. The precision of the system clock, in log2 seconds. + RootDelay NTPFixed16Seconds // [0,2^32-1]. Total round trip delay to the reference clock in seconds times 2^16. + RootDispersion NTPFixed16Seconds // [0,2^32-1]. Total dispersion to the reference clock, in seconds times 2^16. + ReferenceID NTPReferenceID // ID code of reference clock [0,2^32-1]. + ReferenceTimestamp NTPTimestamp // Most recent timestamp from the reference clock. + OriginTimestamp NTPTimestamp // Local time when request was sent from local host. + ReceiveTimestamp NTPTimestamp // Local time (on server) that request arrived at server host. + TransmitTimestamp NTPTimestamp // Local time (on server) that request departed server host. + + // FIX: This package should analyse the extension fields and represent the extension fields too. + ExtensionBytes []byte // Just put extensions in a byte slice. +} + +//****************************************************************************** + +// LayerType returns the layer type of the NTP object, which is LayerTypeNTP. +func (d *NTP) LayerType() gopacket.LayerType { + return LayerTypeNTP +} + +//****************************************************************************** + +// decodeNTP analyses a byte slice and attempts to decode it as an NTP +// record of a UDP packet. +// +// If it succeeds, it loads p with information about the packet and returns nil. +// If it fails, it returns an error (non nil). +// +// This function is employed in layertypes.go to register the NTP layer. +func decodeNTP(data []byte, p gopacket.PacketBuilder) error { + + // Attempt to decode the byte slice. + d := &NTP{} + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + + // If the decoding worked, add the layer to the packet and set it + // as the application layer too, if there isn't already one. + p.AddLayer(d) + p.SetApplicationLayer(d) + + return nil +} + +//****************************************************************************** + +// DecodeFromBytes analyses a byte slice and attempts to decode it as an NTP +// record of a UDP packet. +// +// Upon succeeds, it loads the NTP object with information about the packet +// and returns nil. +// Upon failure, it returns an error (non nil). +func (d *NTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + + // If the data block is too short to be a NTP record, then return an error. + if len(data) < ntpMinimumRecordSizeInBytes { + df.SetTruncated() + return errors.New("NTP packet too short") + } + + // RFC 5905 does not appear to define a maximum NTP record length. + // The protocol allows "extension fields" to be included in the record, + // and states about these fields:" + // + // "While the minimum field length containing required fields is + // four words (16 octets), a maximum field length remains to be + // established." + // + // For this reason, the packet length is not checked here for being too long. + + // NTP type embeds type BaseLayer which contains two fields: + // Contents is supposed to contain the bytes of the data at this level. + // Payload is supposed to contain the payload of this level. + // Here we set the baselayer to be the bytes of the NTP record. + d.BaseLayer = BaseLayer{Contents: data[:len(data)]} + + // Extract the fields from the block of bytes. + // To make sense of this, refer to the packet diagram + // above and the section on endian conventions. + + // The first few fields are all packed into the first 32 bits. Unpack them. + f := data[0] + d.LeapIndicator = NTPLeapIndicator((f & 0xC0) >> 6) + d.Version = NTPVersion((f & 0x38) >> 3) + d.Mode = NTPMode(f & 0x07) + d.Stratum = NTPStratum(data[1]) + d.Poll = NTPLog2Seconds(data[2]) + d.Precision = NTPLog2Seconds(data[3]) + + // The remaining fields can just be copied in big endian order. + d.RootDelay = NTPFixed16Seconds(binary.BigEndian.Uint32(data[4:8])) + d.RootDispersion = NTPFixed16Seconds(binary.BigEndian.Uint32(data[8:12])) + d.ReferenceID = NTPReferenceID(binary.BigEndian.Uint32(data[12:16])) + d.ReferenceTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[16:24])) + d.OriginTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[24:32])) + d.ReceiveTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[32:40])) + d.TransmitTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[40:48])) + + // This layer does not attempt to analyse the extension bytes. + // But if there are any, we'd like the user to know. So we just + // place them all in an ExtensionBytes field. + d.ExtensionBytes = data[48:] + + // Return no error. + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *NTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + data, err := b.PrependBytes(ntpMinimumRecordSizeInBytes) + if err != nil { + return err + } + + // Pack the first few fields into the first 32 bits. + h := uint8(0) + h |= (uint8(d.LeapIndicator) << 6) & 0xC0 + h |= (uint8(d.Version) << 3) & 0x38 + h |= (uint8(d.Mode)) & 0x07 + data[0] = byte(h) + data[1] = byte(d.Stratum) + data[2] = byte(d.Poll) + data[3] = byte(d.Precision) + + // The remaining fields can just be copied in big endian order. + binary.BigEndian.PutUint32(data[4:8], uint32(d.RootDelay)) + binary.BigEndian.PutUint32(data[8:12], uint32(d.RootDispersion)) + binary.BigEndian.PutUint32(data[12:16], uint32(d.ReferenceID)) + binary.BigEndian.PutUint64(data[16:24], uint64(d.ReferenceTimestamp)) + binary.BigEndian.PutUint64(data[24:32], uint64(d.OriginTimestamp)) + binary.BigEndian.PutUint64(data[32:40], uint64(d.ReceiveTimestamp)) + binary.BigEndian.PutUint64(data[40:48], uint64(d.TransmitTimestamp)) + + ex, err := b.AppendBytes(len(d.ExtensionBytes)) + if err != nil { + return err + } + copy(ex, d.ExtensionBytes) + + return nil +} + +//****************************************************************************** + +// CanDecode returns a set of layers that NTP objects can decode. +// As NTP objects can only decide the NTP layer, we can return just that layer. +// Apparently a single layer type implements LayerClass. +func (d *NTP) CanDecode() gopacket.LayerClass { + return LayerTypeNTP +} + +//****************************************************************************** + +// NextLayerType specifies the next layer that GoPacket should attempt to +// analyse after this (NTP) layer. As NTP packets do not contain any payload +// bytes, there are no further layers to analyse. +func (d *NTP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +//****************************************************************************** + +// NTP packets do not carry any data payload, so the empty byte slice is retured. +// In Go, a nil slice is functionally identical to an empty slice, so we +// return nil to avoid a heap allocation. +func (d *NTP) Payload() []byte { + return nil +} + +//****************************************************************************** +//* End Of NTP File * +//****************************************************************************** diff --git a/vendor/github.com/google/gopacket/layers/ntp_test.go b/vendor/github.com/google/gopacket/layers/ntp_test.go new file mode 100644 index 00000000..b5222169 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ntp_test.go @@ -0,0 +1,258 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +// +//****************************************************************************** + +package layers + +import ( + "crypto/rand" + "github.com/google/gopacket" + "io" + "reflect" + "testing" +) + +//****************************************************************************** + +// checkNTP() uses the ntp.go code to analyse the packet bytes as an NTP UDP +// packet and generate an NTP object. It then compares the generated NTP object +// with the one provided and throws an error if there is any difference. +// The desc argument is output with any failure message to identify the test. +func checkNTP(desc string, t *testing.T, packetBytes []byte, pExpectedNTP *NTP) { + + // Analyse the packet bytes, yielding a new packet object p. + p := gopacket.NewPacket(packetBytes, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Errorf("Failed to decode packet %s: %v", desc, p.ErrorLayer().Error()) + } + + // Ensure that the packet analysis yielded the correct set of layers: + // Link Layer = Ethernet. + // Network Layer = IPv4. + // Transport Layer = UDP. + // Application Layer = NTP. + checkLayers(p, []gopacket.LayerType{ + LayerTypeEthernet, + LayerTypeIPv4, + LayerTypeUDP, + LayerTypeNTP}, t) + + // Select the Application (NTP) layer. + pResultNTP, ok := p.ApplicationLayer().(*NTP) + if !ok { + t.Error("No NTP layer type found in packet in " + desc + ".") + } + + // Compare the generated NTP object with the expected NTP object. + if !reflect.DeepEqual(pResultNTP, pExpectedNTP) { + t.Errorf("NTP packet processing failed for packet "+desc+ + ":\ngot :\n%#v\n\nwant :\n%#v\n\n", pResultNTP, pExpectedNTP) + } + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} + err := pResultNTP.SerializeTo(buf, opts) + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(pResultNTP.BaseLayer.Contents, buf.Bytes()) { + t.Errorf("NTP packet serialization failed for packet "+desc+ + ":\ngot :\n%x\n\nwant :\n%x\n\n", buf.Bytes(), packetBytes) + } +} + +//****************************************************************************** + +func TestNTPOne(t *testing.T) { + + // This test packet is the first NTP packet in the NTP sample capture + // pcap file NTP_sync.pcap on the Wireshark sample captures page: + // + // https://wiki.wireshark.org/SampleCaptures + // https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=NTP_sync.pcap + var testPacketNTP = []byte{ + 0x00, 0x0c, 0x41, 0x82, 0xb2, 0x53, 0x00, 0xd0, + 0x59, 0x6c, 0x40, 0x4e, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x4c, 0x0a, 0x42, 0x00, 0x00, 0x80, 0x11, + 0xb5, 0xfa, 0xc0, 0xa8, 0x32, 0x32, 0x43, 0x81, + 0x44, 0x09, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x38, + 0xf8, 0xd2, 0xd9, 0x00, 0x0a, 0xfa, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc5, 0x02, 0x04, 0xec, 0xec, 0x42, + 0xee, 0x92, + } + + // Assemble the NTP object that we expect to emerge from this test. + pExpectedNTP := &NTP{ + BaseLayer: BaseLayer{ + Contents: []byte{0xd9, 0x0, 0xa, 0xfa, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xc5, 0x2, 0x4, 0xec, 0xec, 0x42, 0xee, 0x92}, + Payload: nil, + }, + LeapIndicator: 3, + Version: 3, + Mode: 1, + Stratum: 0, + Poll: 10, + Precision: -6, + RootDelay: 0, + RootDispersion: 0x10290, + ReferenceID: 0, + ReferenceTimestamp: 0, + OriginTimestamp: 0, + ReceiveTimestamp: 0, + TransmitTimestamp: 0xc50204ecec42ee92, + ExtensionBytes: []byte{}, + } + + checkNTP("test01", t, testPacketNTP, pExpectedNTP) +} + +//****************************************************************************** + +func TestNTPTwo(t *testing.T) { + + // This test packet is packet #18 in the NTP sample capture + // pcap file NTP_sync.pcap on the Wireshark sample captures page: + // + // https://wiki.wireshark.org/SampleCaptures + // https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=NTP_sync.pcap + // + // This packet was chosen because it is the first NTP packet after the first + // NTP packet that has non-zero timestamps. + + var testPacketNTP = []byte{ + 0x00, 0xd0, 0x59, 0x6c, 0x40, 0x4e, 0x00, 0x0c, + 0x41, 0x82, 0xb2, 0x53, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x4c, 0x32, 0x46, 0x40, 0x00, 0x2f, 0x11, + 0xa8, 0x18, 0x45, 0x2c, 0x39, 0x3c, 0xc0, 0xa8, + 0x32, 0x32, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x38, + 0x09, 0x58, 0x1a, 0x03, 0x0a, 0xee, 0x00, 0x00, + 0x1b, 0xf7, 0x00, 0x00, 0x14, 0xec, 0x51, 0xae, + 0x80, 0xb7, 0xc5, 0x02, 0x03, 0x4c, 0x8d, 0x0e, + 0x66, 0xcb, 0xc5, 0x02, 0x04, 0xec, 0xec, 0x42, + 0xee, 0x92, 0xc5, 0x02, 0x04, 0xeb, 0xcf, 0x49, + 0x59, 0xe6, 0xc5, 0x02, 0x04, 0xeb, 0xcf, 0x4c, + 0x6e, 0x6e, + } + + // Assemble the NTP object that we expect to emerge from this test. + pExpectedNTP := &NTP{ + BaseLayer: BaseLayer{ + Contents: []byte{0x1a, 0x03, 0x0a, 0xee, 0x00, 0x00, + 0x1b, 0xf7, 0x00, 0x00, 0x14, 0xec, 0x51, 0xae, + 0x80, 0xb7, 0xc5, 0x02, 0x03, 0x4c, 0x8d, 0x0e, + 0x66, 0xcb, 0xc5, 0x02, 0x04, 0xec, 0xec, 0x42, + 0xee, 0x92, 0xc5, 0x02, 0x04, 0xeb, 0xcf, 0x49, + 0x59, 0xe6, 0xc5, 0x02, 0x04, 0xeb, 0xcf, 0x4c, + 0x6e, 0x6e}, + Payload: nil, + }, + LeapIndicator: 0, + Version: 3, + Mode: 2, + Stratum: 3, + Poll: 10, + Precision: -18, + RootDelay: 0x1bf7, + RootDispersion: 0x14ec, + ReferenceID: 0x51ae80b7, + ReferenceTimestamp: 0xc502034c8d0e66cb, + OriginTimestamp: 0xc50204ecec42ee92, + ReceiveTimestamp: 0xc50204ebcf4959e6, + TransmitTimestamp: 0xc50204ebcf4c6e6e, + ExtensionBytes: []byte{}, + } + + checkNTP("test02", t, testPacketNTP, pExpectedNTP) +} + +//****************************************************************************** + +func TestNTPThree(t *testing.T) { + + // This test packet is packet #19 in the NTP sample capture + // pcap file NTP_sync.pcap on the Wireshark sample captures page: + // + // https://wiki.wireshark.org/SampleCaptures + // https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=NTP_sync.pcap + + var testPacketNTP = []byte{ + 0x00, 0xd0, 0x59, 0x6c, 0x40, 0x4e, 0x00, 0x0c, + 0x41, 0x82, 0xb2, 0x53, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x4c, 0x00, 0x00, 0x40, 0x00, 0x30, 0x11, + 0x74, 0x65, 0x18, 0x7b, 0xca, 0xe6, 0xc0, 0xa8, + 0x32, 0x32, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x38, + 0x44, 0x05, 0x1a, 0x02, 0x0a, 0xec, 0x00, 0x00, + 0x07, 0xc3, 0x00, 0x00, 0x2f, 0x80, 0xc6, 0x1e, + 0x5c, 0x02, 0xc5, 0x01, 0xf9, 0x95, 0x42, 0x50, + 0x82, 0xcf, 0xc5, 0x02, 0x04, 0xec, 0xec, 0x42, + 0xee, 0x92, 0xc5, 0x02, 0x04, 0xeb, 0xd2, 0x35, + 0x2e, 0xb5, 0xc5, 0x02, 0x04, 0xeb, 0xd2, 0x35, + 0xd6, 0x7b, + } + + // Assemble the NTP object that we expect to emerge from this test. + pExpectedNTP := &NTP{ + BaseLayer: BaseLayer{ + Contents: []byte{0x1a, 0x02, 0x0a, 0xec, 0x00, 0x00, + 0x07, 0xc3, 0x00, 0x00, 0x2f, 0x80, 0xc6, 0x1e, + 0x5c, 0x02, 0xc5, 0x01, 0xf9, 0x95, 0x42, 0x50, + 0x82, 0xcf, 0xc5, 0x02, 0x04, 0xec, 0xec, 0x42, + 0xee, 0x92, 0xc5, 0x02, 0x04, 0xeb, 0xd2, 0x35, + 0x2e, 0xb5, 0xc5, 0x02, 0x04, 0xeb, 0xd2, 0x35, + 0xd6, 0x7b}, + Payload: nil, + }, + LeapIndicator: 0, + Version: 3, + Mode: 2, + Stratum: 2, + Poll: 10, + Precision: -20, + RootDelay: 0x7c3, + RootDispersion: 0x2f80, + ReferenceID: 0xc61e5c02, + ReferenceTimestamp: 0xc501f995425082cf, + OriginTimestamp: 0xc50204ecec42ee92, + ReceiveTimestamp: 0xc50204ebd2352eb5, + TransmitTimestamp: 0xc50204ebd235d67b, + ExtensionBytes: []byte{}, + } + + checkNTP("test03", t, testPacketNTP, pExpectedNTP) +} + +//****************************************************************************** + +// TestNTPIsomorphism tests whether random data gets parsed into NTP layer and +// gets serialized back from it to the same value. +func TestNTPIsomorphism(t *testing.T) { + NTPData := make([]byte, ntpMinimumRecordSizeInBytes+7) + _, err := io.ReadFull(rand.Reader, NTPData) + if err != nil { + t.Error(err) + } + ntpLayer := &NTP{} + err = ntpLayer.DecodeFromBytes(NTPData, gopacket.NilDecodeFeedback) + if err != nil { + t.Error(err) + } + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} + err = ntpLayer.SerializeTo(buf, opts) + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(NTPData, buf.Bytes()) { + t.Errorf("NTP packet is not isomorphic:\ngot :\n%x\n\nwant :\n%x\n\n", buf.Bytes(), NTPData) + } +} diff --git a/vendor/github.com/google/gopacket/layers/pflog.go b/vendor/github.com/google/gopacket/layers/pflog.go new file mode 100644 index 00000000..853882fd --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/pflog.go @@ -0,0 +1,76 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +type PFDirection uint8 + +const ( + PFDirectionInOut PFDirection = 0 + PFDirectionIn PFDirection = 1 + PFDirectionOut PFDirection = 2 +) + +// PFLog provides the layer for 'pf' packet-filter logging, as described at +// http://www.freebsd.org/cgi/man.cgi?query=pflog&sektion=4 +type PFLog struct { + BaseLayer + Length uint8 + Family ProtocolFamily + Action, Reason uint8 + IFName, Ruleset []byte + RuleNum, SubruleNum uint32 + UID uint32 + PID int32 + RuleUID uint32 + RulePID int32 + Direction PFDirection + // The remainder is padding +} + +func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + pf.Length = data[0] + pf.Family = ProtocolFamily(data[1]) + pf.Action = data[2] + pf.Reason = data[3] + pf.IFName = data[4:20] + pf.Ruleset = data[20:36] + pf.RuleNum = binary.BigEndian.Uint32(data[36:40]) + pf.SubruleNum = binary.BigEndian.Uint32(data[40:44]) + pf.UID = binary.BigEndian.Uint32(data[44:48]) + pf.PID = int32(binary.BigEndian.Uint32(data[48:52])) + pf.RuleUID = binary.BigEndian.Uint32(data[52:56]) + pf.RulePID = int32(binary.BigEndian.Uint32(data[56:60])) + pf.Direction = PFDirection(data[60]) + if pf.Length%4 != 1 { + return errors.New("PFLog header length should be 3 less than multiple of 4") + } + actualLength := int(pf.Length) + 3 + pf.Contents = data[:actualLength] + pf.Payload = data[actualLength:] + return nil +} + +// LayerType returns layers.LayerTypePFLog +func (pf *PFLog) LayerType() gopacket.LayerType { return LayerTypePFLog } + +func (pf *PFLog) CanDecode() gopacket.LayerClass { return LayerTypePFLog } + +func (pf *PFLog) NextLayerType() gopacket.LayerType { + return pf.Family.LayerType() +} + +func decodePFLog(data []byte, p gopacket.PacketBuilder) error { + pf := &PFLog{} + return decodingLayerDecoder(pf, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/ports.go b/vendor/github.com/google/gopacket/layers/ports.go new file mode 100644 index 00000000..e9dd5e1e --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ports.go @@ -0,0 +1,139 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "fmt" + "strconv" + + "github.com/google/gopacket" +) + +// TCPPort is a port in a TCP layer. +type TCPPort uint16 + +// UDPPort is a port in a UDP layer. +type UDPPort uint16 + +// RUDPPort is a port in a RUDP layer. +type RUDPPort uint8 + +// SCTPPort is a port in a SCTP layer. +type SCTPPort uint16 + +// UDPLitePort is a port in a UDPLite layer. +type UDPLitePort uint16 + +// RUDPPortNames contains the string names for all RUDP ports. +var RUDPPortNames = map[RUDPPort]string{} + +// UDPLitePortNames contains the string names for all UDPLite ports. +var UDPLitePortNames = map[UDPLitePort]string{} + +// {TCP,UDP,SCTP}PortNames can be found in iana_ports.go + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// TCPPortNames. +func (a TCPPort) String() string { + if name, ok := TCPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// LayerType returns a LayerType that would be able to decode the +// application payload. It uses some well-known ports such as 53 for +// DNS. +// +// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers. +func (a TCPPort) LayerType() gopacket.LayerType { + lt := tcpPortLayerType[uint16(a)] + if lt != 0 { + return lt + } + return gopacket.LayerTypePayload +} + +var tcpPortLayerType = [65536]gopacket.LayerType{ + 53: LayerTypeDNS, +} + +// RegisterTCPPortLayerType creates a new mapping between a TCPPort +// and an underlaying LayerType. +func RegisterTCPPortLayerType(port TCPPort, layerType gopacket.LayerType) { + tcpPortLayerType[port] = layerType +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// UDPPortNames. +func (a UDPPort) String() string { + if name, ok := UDPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// LayerType returns a LayerType that would be able to decode the +// application payload. It uses some well-known ports such as 53 for +// DNS. +// +// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers. +func (a UDPPort) LayerType() gopacket.LayerType { + lt := udpPortLayerType[uint16(a)] + if lt != 0 { + return lt + } + return gopacket.LayerTypePayload +} + +var udpPortLayerType = [65536]gopacket.LayerType{ + 53: LayerTypeDNS, + 123: LayerTypeNTP, + 4789: LayerTypeVXLAN, + 67: LayerTypeDHCPv4, + 68: LayerTypeDHCPv4, + 6343: LayerTypeSFlow, + 6081: LayerTypeGeneve, +} + +// RegisterUDPPortLayerType creates a new mapping between a UDPPort +// and an underlaying LayerType. +func RegisterUDPPortLayerType(port UDPPort, layerType gopacket.LayerType) { + udpPortLayerType[port] = layerType +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// RUDPPortNames. +func (a RUDPPort) String() string { + if name, ok := RUDPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// SCTPPortNames. +func (a SCTPPort) String() string { + if name, ok := SCTPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// UDPLitePortNames. +func (a UDPLitePort) String() string { + if name, ok := UDPLitePortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} diff --git a/vendor/github.com/google/gopacket/layers/ppp.go b/vendor/github.com/google/gopacket/layers/ppp.go new file mode 100644 index 00000000..1d2e7b8c --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ppp.go @@ -0,0 +1,74 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "github.com/google/gopacket" +) + +// PPP is the layer for PPP encapsulation headers. +type PPP struct { + BaseLayer + PPPType PPPType +} + +// PPPEndpoint is a singleton endpoint for PPP. Since there is no actual +// addressing for the two ends of a PPP connection, we use a singleton value +// named 'point' for each endpoint. +var PPPEndpoint = gopacket.NewEndpoint(EndpointPPP, nil) + +// PPPFlow is a singleton flow for PPP. Since there is no actual addressing for +// the two ends of a PPP connection, we use a singleton value to represent the +// flow for all PPP connections. +var PPPFlow = gopacket.NewFlow(EndpointPPP, nil, nil) + +// LayerType returns LayerTypePPP +func (p *PPP) LayerType() gopacket.LayerType { return LayerTypePPP } + +// LinkFlow returns PPPFlow. +func (p *PPP) LinkFlow() gopacket.Flow { return PPPFlow } + +func decodePPP(data []byte, p gopacket.PacketBuilder) error { + ppp := &PPP{} + if data[0]&0x1 == 0 { + if data[1]&0x1 == 0 { + return errors.New("PPP has invalid type") + } + ppp.PPPType = PPPType(binary.BigEndian.Uint16(data[:2])) + ppp.Contents = data[:2] + ppp.Payload = data[2:] + } else { + ppp.PPPType = PPPType(data[0]) + ppp.Contents = data[:1] + ppp.Payload = data[1:] + } + p.AddLayer(ppp) + p.SetLinkLayer(ppp) + return p.NextDecoder(ppp.PPPType) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p *PPP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if p.PPPType&0x100 == 0 { + bytes, err := b.PrependBytes(2) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, uint16(p.PPPType)) + } else { + bytes, err := b.PrependBytes(1) + if err != nil { + return err + } + bytes[0] = uint8(p.PPPType) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/pppoe.go b/vendor/github.com/google/gopacket/layers/pppoe.go new file mode 100644 index 00000000..14cd63a1 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/pppoe.go @@ -0,0 +1,60 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// PPPoE is the layer for PPPoE encapsulation headers. +type PPPoE struct { + BaseLayer + Version uint8 + Type uint8 + Code PPPoECode + SessionId uint16 + Length uint16 +} + +// LayerType returns gopacket.LayerTypePPPoE. +func (p *PPPoE) LayerType() gopacket.LayerType { + return LayerTypePPPoE +} + +// decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516). +func decodePPPoE(data []byte, p gopacket.PacketBuilder) error { + pppoe := &PPPoE{ + Version: data[0] >> 4, + Type: data[0] & 0x0F, + Code: PPPoECode(data[1]), + SessionId: binary.BigEndian.Uint16(data[2:4]), + Length: binary.BigEndian.Uint16(data[4:6]), + } + pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]} + p.AddLayer(pppoe) + return p.NextDecoder(pppoe.Code) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p *PPPoE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + payload := b.Bytes() + bytes, err := b.PrependBytes(6) + if err != nil { + return err + } + bytes[0] = (p.Version << 4) | p.Type + bytes[1] = byte(p.Code) + binary.BigEndian.PutUint16(bytes[2:], p.SessionId) + if opts.FixLengths { + p.Length = uint16(len(payload)) + } + binary.BigEndian.PutUint16(bytes[4:], p.Length) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/prism.go b/vendor/github.com/google/gopacket/layers/prism.go new file mode 100644 index 00000000..e1711e7f --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/prism.go @@ -0,0 +1,146 @@ +// Copyright 2015 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// http://www.tcpdump.org/linktypes/LINKTYPE_IEEE802_11_PRISM.html + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +func decodePrismValue(data []byte, pv *PrismValue) { + pv.DID = PrismDID(binary.LittleEndian.Uint32(data[0:4])) + pv.Status = binary.LittleEndian.Uint16(data[4:6]) + pv.Length = binary.LittleEndian.Uint16(data[6:8]) + pv.Data = data[8 : 8+pv.Length] +} + +type PrismDID uint32 + +const ( + PrismDIDType1HostTime PrismDID = 0x10044 + PrismDIDType2HostTime PrismDID = 0x01041 + PrismDIDType1MACTime PrismDID = 0x20044 + PrismDIDType2MACTime PrismDID = 0x02041 + PrismDIDType1Channel PrismDID = 0x30044 + PrismDIDType2Channel PrismDID = 0x03041 + PrismDIDType1RSSI PrismDID = 0x40044 + PrismDIDType2RSSI PrismDID = 0x04041 + PrismDIDType1SignalQuality PrismDID = 0x50044 + PrismDIDType2SignalQuality PrismDID = 0x05041 + PrismDIDType1Signal PrismDID = 0x60044 + PrismDIDType2Signal PrismDID = 0x06041 + PrismDIDType1Noise PrismDID = 0x70044 + PrismDIDType2Noise PrismDID = 0x07041 + PrismDIDType1Rate PrismDID = 0x80044 + PrismDIDType2Rate PrismDID = 0x08041 + PrismDIDType1TransmittedFrameIndicator PrismDID = 0x90044 + PrismDIDType2TransmittedFrameIndicator PrismDID = 0x09041 + PrismDIDType1FrameLength PrismDID = 0xA0044 + PrismDIDType2FrameLength PrismDID = 0x0A041 +) + +const ( + PrismType1MessageCode uint16 = 0x00000044 + PrismType2MessageCode uint16 = 0x00000041 +) + +func (p PrismDID) String() string { + dids := map[PrismDID]string{ + PrismDIDType1HostTime: "Host Time", + PrismDIDType2HostTime: "Host Time", + PrismDIDType1MACTime: "MAC Time", + PrismDIDType2MACTime: "MAC Time", + PrismDIDType1Channel: "Channel", + PrismDIDType2Channel: "Channel", + PrismDIDType1RSSI: "RSSI", + PrismDIDType2RSSI: "RSSI", + PrismDIDType1SignalQuality: "Signal Quality", + PrismDIDType2SignalQuality: "Signal Quality", + PrismDIDType1Signal: "Signal", + PrismDIDType2Signal: "Signal", + PrismDIDType1Noise: "Noise", + PrismDIDType2Noise: "Noise", + PrismDIDType1Rate: "Rate", + PrismDIDType2Rate: "Rate", + PrismDIDType1TransmittedFrameIndicator: "Transmitted Frame Indicator", + PrismDIDType2TransmittedFrameIndicator: "Transmitted Frame Indicator", + PrismDIDType1FrameLength: "Frame Length", + PrismDIDType2FrameLength: "Frame Length", + } + + if str, ok := dids[p]; ok { + return str + } + + return "Unknown DID" +} + +type PrismValue struct { + DID PrismDID + Status uint16 + Length uint16 + Data []byte +} + +func (pv *PrismValue) IsSupplied() bool { + return pv.Status == 1 +} + +var ErrPrismExpectedMoreData = errors.New("Expected more data.") +var ErrPrismInvalidCode = errors.New("Invalid header code.") + +func decodePrismHeader(data []byte, p gopacket.PacketBuilder) error { + d := &PrismHeader{} + return decodingLayerDecoder(d, data, p) +} + +type PrismHeader struct { + BaseLayer + Code uint16 + Length uint16 + DeviceName string + Values []PrismValue +} + +func (m *PrismHeader) LayerType() gopacket.LayerType { return LayerTypePrismHeader } + +func (m *PrismHeader) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Code = binary.LittleEndian.Uint16(data[0:4]) + m.Length = binary.LittleEndian.Uint16(data[4:8]) + m.DeviceName = string(data[8:24]) + m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: data[m.Length:len(data)]} + + switch m.Code { + case PrismType1MessageCode: + fallthrough + case PrismType2MessageCode: + // valid message code + default: + return ErrPrismInvalidCode + } + + offset := uint16(24) + + m.Values = make([]PrismValue, (m.Length-offset)/12) + for i := 0; i < len(m.Values); i++ { + decodePrismValue(data[offset:offset+12], &m.Values[i]) + offset += 12 + } + + if offset != m.Length { + return ErrPrismExpectedMoreData + } + + return nil +} + +func (m *PrismHeader) CanDecode() gopacket.LayerClass { return LayerTypePrismHeader } +func (m *PrismHeader) NextLayerType() gopacket.LayerType { return LayerTypeDot11 } diff --git a/vendor/github.com/google/gopacket/layers/prism_test.go b/vendor/github.com/google/gopacket/layers/prism_test.go new file mode 100644 index 00000000..136a04cb --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/prism_test.go @@ -0,0 +1,120 @@ +// Copyright 2014, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "net" + "reflect" + "testing" + + "github.com/google/gopacket" +) + +// Generator: python layers/test_creator.py --layerType=LayerTypePrismHeader --linkType=LinkTypePrismHeader --name=Prism%s ~/tmp/dump.pcap +// http://wiki.wireshark.org/SampleCaptures#Sample_Captures + +// testPacketPrism is the packet: +// 21:32:37.616872 BSSID:Broadcast DA:Broadcast SA:cc:fa:00:ad:79:e8 (oui Unknown) Probe Request () [1.0 2.0 5.5 11.0 Mbit] +// 0x0000: 4400 0000 9000 0000 7261 3000 0000 0000 +// 0x0010: 0000 0000 0000 0000 4400 0100 0000 0400 +// 0x0020: f9c1 2900 4400 0200 0000 0000 0000 0000 +// 0x0030: 4400 0300 0000 0400 0a00 0000 4400 0400 +// 0x0040: 0000 0400 e1ff ffff 0000 0000 0000 0000 +// 0x0050: 0000 0000 4400 0600 0000 0400 0000 0000 +// 0x0060: 4400 0700 0000 0400 0000 0000 4400 0800 +// 0x0070: 0000 0400 0200 0000 4400 0900 0000 0000 +// 0x0080: 0000 0000 4400 0a00 0000 0400 7e00 0000 +// 0x0090: 4000 0000 ffff ffff ffff ccfa 00ad 79e8 +// 0x00a0: ffff ffff ffff a041 0000 0104 0204 0b16 +// 0x00b0: 3208 0c12 1824 3048 606c 0301 012d 1a2d +// 0x00c0: 1117 ff00 0000 0000 0000 0000 0000 0000 +// 0x00d0: 0000 0000 0000 0000 007f 0800 0000 0000 +// 0x00e0: 0000 40dd 0900 1018 0200 0010 0000 dd1e +// 0x00f0: 0090 4c33 2d11 17ff 0000 0000 0000 0000 +// 0x0100: 0000 0000 0000 0000 0000 0000 0000 + +var testPacketPrism = []byte{ + 0x44, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x72, 0x61, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, + 0xf9, 0xc1, 0x29, 0x00, 0x44, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x44, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0xe1, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x44, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xfa, 0x00, 0xad, 0x79, 0xe8, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x41, 0x00, 0x00, 0x01, 0x04, 0x02, 0x04, 0x0b, 0x16, + 0x32, 0x08, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0x03, 0x01, 0x01, 0x2d, 0x1a, 0x2d, + 0x11, 0x17, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0xdd, 0x09, 0x00, 0x10, 0x18, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0xdd, 0x1e, + 0x00, 0x90, 0x4c, 0x33, 0x2d, 0x11, 0x17, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestPacketPrism(t *testing.T) { + p := gopacket.NewPacket(testPacketPrism, LinkTypePrismHeader, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypePrismHeader, LayerTypeDot11, LayerTypeDot11MgmtProbeReq}, t) + + if got, ok := p.Layer(LayerTypePrismHeader).(*PrismHeader); ok { + want := &PrismHeader{ + BaseLayer: BaseLayer{ + Contents: []uint8{0x44, 0x0, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x72, 0x61, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0xf9, 0xc1, 0x29, 0x0, 0x44, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0xa, 0x0, 0x0, 0x0, 0x44, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0xe1, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x6, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x7, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x8, 0x0, 0x0, 0x0, 0x4, 0x0, 0x2, 0x0, 0x0, 0x0, 0x44, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0xa, 0x0, 0x0, 0x0, 0x4, 0x0, 0x7e, 0x0, 0x0, 0x0}, + Payload: []uint8{0x40, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xfa, 0x0, 0xad, 0x79, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x41, 0x0, 0x0, 0x1, 0x4, 0x2, 0x4, 0xb, 0x16, 0x32, 0x8, 0xc, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0x3, 0x1, 0x1, 0x2d, 0x1a, 0x2d, 0x11, 0x17, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0xdd, 0x9, 0x0, 0x10, 0x18, 0x2, 0x0, 0x0, 0x10, 0x0, 0x0, 0xdd, 0x1e, 0x0, 0x90, 0x4c, 0x33, 0x2d, 0x11, 0x17, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, Code: 0x44, Length: 0x90, DeviceName: "ra0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + Values: []PrismValue{ + PrismValue{DID: PrismDIDType1HostTime, Status: 0x0, Length: 0x4, Data: []uint8{0xf9, 0xc1, 0x29, 0x0}}, + PrismValue{DID: PrismDIDType1MACTime, Status: 0x0, Length: 0x0, Data: []uint8{}}, + PrismValue{DID: PrismDIDType1Channel, Status: 0x0, Length: 0x4, Data: []uint8{0xa, 0x0, 0x0, 0x0}}, + PrismValue{DID: PrismDIDType1RSSI, Status: 0x0, Length: 0x4, Data: []uint8{0xe1, 0xff, 0xff, 0xff}}, + PrismValue{DID: 0x0, Status: 0x0, Length: 0x0, Data: []uint8{}}, + PrismValue{DID: PrismDIDType1Signal, Status: 0x0, Length: 0x4, Data: []uint8{0x0, 0x0, 0x0, 0x0}}, + PrismValue{DID: PrismDIDType1Noise, Status: 0x0, Length: 0x4, Data: []uint8{0x0, 0x0, 0x0, 0x0}}, + PrismValue{DID: PrismDIDType1Rate, Status: 0x0, Length: 0x4, Data: []uint8{0x2, 0x0, 0x0, 0x0}}, + PrismValue{DID: PrismDIDType1TransmittedFrameIndicator, Status: 0x0, Length: 0x0, Data: []uint8{}}, + PrismValue{DID: PrismDIDType1FrameLength, Status: 0x0, Length: 0x4, Data: []uint8{0x7e, 0x0, 0x0, 0x0}}, + }, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("RadioTap packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } + + if got, ok := p.Layer(LayerTypeDot11).(*Dot11); ok { + want := &Dot11{ + BaseLayer: BaseLayer{ + Contents: []uint8{0x40, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xfa, 0x0, 0xad, 0x79, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x41}, + Payload: []uint8{0x0, 0x0, 0x1, 0x4, 0x2, 0x4, 0xb, 0x16, 0x32, 0x8, 0xc, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0x3, 0x1, 0x1, 0x2d, 0x1a, 0x2d, 0x11, 0x17, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0xdd, 0x9, 0x0, 0x10, 0x18, 0x2, 0x0, 0x0, 0x10, 0x0, 0x0, 0xdd, 0x1e, 0x0, 0x90, 0x4c, 0x33, 0x2d, 0x11, 0x17, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + Type: 0x10, + Proto: 0x0, + Flags: 0x0, + DurationID: 0x0, + Address1: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + Address2: net.HardwareAddr{0xcc, 0xfa, 0x0, 0xad, 0x79, 0xe8}, + Address3: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + Address4: net.HardwareAddr(nil), + SequenceNumber: 0x041a, + FragmentNumber: 0x0, + Checksum: 0x0, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Dot11 packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } +} + +func BenchmarkDecodePacketPrism(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketPrism, LinkTypePrismHeader, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/radiotap.go b/vendor/github.com/google/gopacket/layers/radiotap.go new file mode 100644 index 00000000..4304e753 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/radiotap.go @@ -0,0 +1,1045 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "encoding/binary" + "fmt" + "hash/crc32" + "strings" + + "github.com/google/gopacket" +) + +// align calculates the number of bytes needed to align with the width +// on the offset, returning the number of bytes we need to skip to +// align to the offset (width). +func align(offset uint16, width uint16) uint16 { + return ((((offset) + ((width) - 1)) & (^((width) - 1))) - offset) +} + +type RadioTapPresent uint32 + +const ( + RadioTapPresentTSFT RadioTapPresent = 1 << iota + RadioTapPresentFlags + RadioTapPresentRate + RadioTapPresentChannel + RadioTapPresentFHSS + RadioTapPresentDBMAntennaSignal + RadioTapPresentDBMAntennaNoise + RadioTapPresentLockQuality + RadioTapPresentTxAttenuation + RadioTapPresentDBTxAttenuation + RadioTapPresentDBMTxPower + RadioTapPresentAntenna + RadioTapPresentDBAntennaSignal + RadioTapPresentDBAntennaNoise + RadioTapPresentRxFlags + RadioTapPresentTxFlags + RadioTapPresentRtsRetries + RadioTapPresentDataRetries + _ + RadioTapPresentMCS + RadioTapPresentAMPDUStatus + RadioTapPresentVHT + RadioTapPresentEXT RadioTapPresent = 1 << 31 +) + +func (r RadioTapPresent) TSFT() bool { + return r&RadioTapPresentTSFT != 0 +} +func (r RadioTapPresent) Flags() bool { + return r&RadioTapPresentFlags != 0 +} +func (r RadioTapPresent) Rate() bool { + return r&RadioTapPresentRate != 0 +} +func (r RadioTapPresent) Channel() bool { + return r&RadioTapPresentChannel != 0 +} +func (r RadioTapPresent) FHSS() bool { + return r&RadioTapPresentFHSS != 0 +} +func (r RadioTapPresent) DBMAntennaSignal() bool { + return r&RadioTapPresentDBMAntennaSignal != 0 +} +func (r RadioTapPresent) DBMAntennaNoise() bool { + return r&RadioTapPresentDBMAntennaNoise != 0 +} +func (r RadioTapPresent) LockQuality() bool { + return r&RadioTapPresentLockQuality != 0 +} +func (r RadioTapPresent) TxAttenuation() bool { + return r&RadioTapPresentTxAttenuation != 0 +} +func (r RadioTapPresent) DBTxAttenuation() bool { + return r&RadioTapPresentDBTxAttenuation != 0 +} +func (r RadioTapPresent) DBMTxPower() bool { + return r&RadioTapPresentDBMTxPower != 0 +} +func (r RadioTapPresent) Antenna() bool { + return r&RadioTapPresentAntenna != 0 +} +func (r RadioTapPresent) DBAntennaSignal() bool { + return r&RadioTapPresentDBAntennaSignal != 0 +} +func (r RadioTapPresent) DBAntennaNoise() bool { + return r&RadioTapPresentDBAntennaNoise != 0 +} +func (r RadioTapPresent) RxFlags() bool { + return r&RadioTapPresentRxFlags != 0 +} +func (r RadioTapPresent) TxFlags() bool { + return r&RadioTapPresentTxFlags != 0 +} +func (r RadioTapPresent) RtsRetries() bool { + return r&RadioTapPresentRtsRetries != 0 +} +func (r RadioTapPresent) DataRetries() bool { + return r&RadioTapPresentDataRetries != 0 +} +func (r RadioTapPresent) MCS() bool { + return r&RadioTapPresentMCS != 0 +} +func (r RadioTapPresent) AMPDUStatus() bool { + return r&RadioTapPresentAMPDUStatus != 0 +} +func (r RadioTapPresent) VHT() bool { + return r&RadioTapPresentVHT != 0 +} +func (r RadioTapPresent) EXT() bool { + return r&RadioTapPresentEXT != 0 +} + +type RadioTapChannelFlags uint16 + +const ( + RadioTapChannelFlagsTurbo RadioTapChannelFlags = 0x0010 // Turbo channel + RadioTapChannelFlagsCCK RadioTapChannelFlags = 0x0020 // CCK channel + RadioTapChannelFlagsOFDM RadioTapChannelFlags = 0x0040 // OFDM channel + RadioTapChannelFlagsGhz2 RadioTapChannelFlags = 0x0080 // 2 GHz spectrum channel. + RadioTapChannelFlagsGhz5 RadioTapChannelFlags = 0x0100 // 5 GHz spectrum channel + RadioTapChannelFlagsPassive RadioTapChannelFlags = 0x0200 // Only passive scan allowed + RadioTapChannelFlagsDynamic RadioTapChannelFlags = 0x0400 // Dynamic CCK-OFDM channel + RadioTapChannelFlagsGFSK RadioTapChannelFlags = 0x0800 // GFSK channel (FHSS PHY) +) + +func (r RadioTapChannelFlags) Turbo() bool { + return r&RadioTapChannelFlagsTurbo != 0 +} +func (r RadioTapChannelFlags) CCK() bool { + return r&RadioTapChannelFlagsCCK != 0 +} +func (r RadioTapChannelFlags) OFDM() bool { + return r&RadioTapChannelFlagsOFDM != 0 +} +func (r RadioTapChannelFlags) Ghz2() bool { + return r&RadioTapChannelFlagsGhz2 != 0 +} +func (r RadioTapChannelFlags) Ghz5() bool { + return r&RadioTapChannelFlagsGhz5 != 0 +} +func (r RadioTapChannelFlags) Passive() bool { + return r&RadioTapChannelFlagsPassive != 0 +} +func (r RadioTapChannelFlags) Dynamic() bool { + return r&RadioTapChannelFlagsDynamic != 0 +} +func (r RadioTapChannelFlags) GFSK() bool { + return r&RadioTapChannelFlagsGFSK != 0 +} + +// String provides a human readable string for RadioTapChannelFlags. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the RadioTapChannelFlags value, not its string. +func (a RadioTapChannelFlags) String() string { + var out bytes.Buffer + if a.Turbo() { + out.WriteString("Turbo,") + } + if a.CCK() { + out.WriteString("CCK,") + } + if a.OFDM() { + out.WriteString("OFDM,") + } + if a.Ghz2() { + out.WriteString("Ghz2,") + } + if a.Ghz5() { + out.WriteString("Ghz5,") + } + if a.Passive() { + out.WriteString("Passive,") + } + if a.Dynamic() { + out.WriteString("Dynamic,") + } + if a.GFSK() { + out.WriteString("GFSK,") + } + + if length := out.Len(); length > 0 { + return string(out.Bytes()[:length-1]) // strip final comma + } + return "" +} + +type RadioTapFlags uint8 + +const ( + RadioTapFlagsCFP RadioTapFlags = 1 << iota // sent/received during CFP + RadioTapFlagsShortPreamble // sent/received * with short * preamble + RadioTapFlagsWEP // sent/received * with WEP encryption + RadioTapFlagsFrag // sent/received * with fragmentation + RadioTapFlagsFCS // frame includes FCS + RadioTapFlagsDatapad // frame has padding between * 802.11 header and payload * (to 32-bit boundary) + RadioTapFlagsBadFCS // does not pass FCS check + RadioTapFlagsShortGI // HT short GI +) + +func (r RadioTapFlags) CFP() bool { + return r&RadioTapFlagsCFP != 0 +} +func (r RadioTapFlags) ShortPreamble() bool { + return r&RadioTapFlagsShortPreamble != 0 +} +func (r RadioTapFlags) WEP() bool { + return r&RadioTapFlagsWEP != 0 +} +func (r RadioTapFlags) Frag() bool { + return r&RadioTapFlagsFrag != 0 +} +func (r RadioTapFlags) FCS() bool { + return r&RadioTapFlagsFCS != 0 +} +func (r RadioTapFlags) Datapad() bool { + return r&RadioTapFlagsDatapad != 0 +} +func (r RadioTapFlags) BadFCS() bool { + return r&RadioTapFlagsBadFCS != 0 +} +func (r RadioTapFlags) ShortGI() bool { + return r&RadioTapFlagsShortGI != 0 +} + +// String provides a human readable string for RadioTapFlags. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the RadioTapFlags value, not its string. +func (a RadioTapFlags) String() string { + var out bytes.Buffer + if a.CFP() { + out.WriteString("CFP,") + } + if a.ShortPreamble() { + out.WriteString("SHORT-PREAMBLE,") + } + if a.WEP() { + out.WriteString("WEP,") + } + if a.Frag() { + out.WriteString("FRAG,") + } + if a.FCS() { + out.WriteString("FCS,") + } + if a.Datapad() { + out.WriteString("DATAPAD,") + } + if a.ShortGI() { + out.WriteString("SHORT-GI,") + } + + if length := out.Len(); length > 0 { + return string(out.Bytes()[:length-1]) // strip final comma + } + return "" +} + +type RadioTapRate uint8 + +func (a RadioTapRate) String() string { + return fmt.Sprintf("%v Mb/s", 0.5*float32(a)) +} + +type RadioTapChannelFrequency uint16 + +func (a RadioTapChannelFrequency) String() string { + return fmt.Sprintf("%d MHz", a) +} + +type RadioTapRxFlags uint16 + +const ( + RadioTapRxFlagsBadPlcp RadioTapRxFlags = 0x0002 +) + +func (self RadioTapRxFlags) BadPlcp() bool { + return self&RadioTapRxFlagsBadPlcp != 0 +} + +func (self RadioTapRxFlags) String() string { + if self.BadPlcp() { + return "BADPLCP" + } + return "" +} + +type RadioTapTxFlags uint16 + +const ( + RadioTapTxFlagsFail RadioTapTxFlags = 1 << iota + RadioTapTxFlagsCTS + RadioTapTxFlagsRTS + RadioTapTxFlagsNoACK +) + +func (self RadioTapTxFlags) Fail() bool { return self&RadioTapTxFlagsFail != 0 } +func (self RadioTapTxFlags) CTS() bool { return self&RadioTapTxFlagsCTS != 0 } +func (self RadioTapTxFlags) RTS() bool { return self&RadioTapTxFlagsRTS != 0 } +func (self RadioTapTxFlags) NoACK() bool { return self&RadioTapTxFlagsNoACK != 0 } + +func (self RadioTapTxFlags) String() string { + var tokens []string + if self.Fail() { + tokens = append(tokens, "Fail") + } + if self.CTS() { + tokens = append(tokens, "CTS") + } + if self.RTS() { + tokens = append(tokens, "RTS") + } + if self.NoACK() { + tokens = append(tokens, "NoACK") + } + return strings.Join(tokens, ",") +} + +type RadioTapMCS struct { + Known RadioTapMCSKnown + Flags RadioTapMCSFlags + MCS uint8 +} + +func (self RadioTapMCS) String() string { + var tokens []string + if self.Known.Bandwidth() { + token := "?" + switch self.Flags.Bandwidth() { + case 0: + token = "20" + case 1: + token = "40" + case 2: + token = "40(20L)" + case 3: + token = "40(20U)" + } + tokens = append(tokens, token) + } + if self.Known.MCSIndex() { + tokens = append(tokens, fmt.Sprintf("MCSIndex#%d", self.MCS)) + } + if self.Known.GuardInterval() { + if self.Flags.ShortGI() { + tokens = append(tokens, fmt.Sprintf("shortGI")) + } else { + tokens = append(tokens, fmt.Sprintf("longGI")) + } + } + if self.Known.HTFormat() { + if self.Flags.Greenfield() { + tokens = append(tokens, fmt.Sprintf("HT-greenfield")) + } else { + tokens = append(tokens, fmt.Sprintf("HT-mixed")) + } + } + if self.Known.FECType() { + if self.Flags.FECLDPC() { + tokens = append(tokens, fmt.Sprintf("LDPC")) + } else { + tokens = append(tokens, fmt.Sprintf("BCC")) + } + } + if self.Known.STBC() { + tokens = append(tokens, fmt.Sprintf("STBC#%d", self.Flags.STBC())) + } + if self.Known.NESS() { + num := 0 + if self.Known.NESS1() { + num |= 0x02 + } + if self.Flags.NESS0() { + num |= 0x01 + } + tokens = append(tokens, fmt.Sprintf("num-of-ESS#%d", num)) + } + return strings.Join(tokens, ",") +} + +type RadioTapMCSKnown uint8 + +const ( + RadioTapMCSKnownBandwidth RadioTapMCSKnown = 1 << iota + RadioTapMCSKnownMCSIndex + RadioTapMCSKnownGuardInterval + RadioTapMCSKnownHTFormat + RadioTapMCSKnownFECType + RadioTapMCSKnownSTBC + RadioTapMCSKnownNESS + RadioTapMCSKnownNESS1 +) + +func (self RadioTapMCSKnown) Bandwidth() bool { return self&RadioTapMCSKnownBandwidth != 0 } +func (self RadioTapMCSKnown) MCSIndex() bool { return self&RadioTapMCSKnownMCSIndex != 0 } +func (self RadioTapMCSKnown) GuardInterval() bool { return self&RadioTapMCSKnownGuardInterval != 0 } +func (self RadioTapMCSKnown) HTFormat() bool { return self&RadioTapMCSKnownHTFormat != 0 } +func (self RadioTapMCSKnown) FECType() bool { return self&RadioTapMCSKnownFECType != 0 } +func (self RadioTapMCSKnown) STBC() bool { return self&RadioTapMCSKnownSTBC != 0 } +func (self RadioTapMCSKnown) NESS() bool { return self&RadioTapMCSKnownNESS != 0 } +func (self RadioTapMCSKnown) NESS1() bool { return self&RadioTapMCSKnownNESS1 != 0 } + +type RadioTapMCSFlags uint8 + +const ( + RadioTapMCSFlagsBandwidthMask RadioTapMCSFlags = 0x03 + RadioTapMCSFlagsShortGI = 0x04 + RadioTapMCSFlagsGreenfield = 0x08 + RadioTapMCSFlagsFECLDPC = 0x10 + RadioTapMCSFlagsSTBCMask = 0x60 + RadioTapMCSFlagsNESS0 = 0x80 +) + +func (self RadioTapMCSFlags) Bandwidth() int { + return int(self & RadioTapMCSFlagsBandwidthMask) +} +func (self RadioTapMCSFlags) ShortGI() bool { return self&RadioTapMCSFlagsShortGI != 0 } +func (self RadioTapMCSFlags) Greenfield() bool { return self&RadioTapMCSFlagsGreenfield != 0 } +func (self RadioTapMCSFlags) FECLDPC() bool { return self&RadioTapMCSFlagsFECLDPC != 0 } +func (self RadioTapMCSFlags) STBC() int { + return int(self&RadioTapMCSFlagsSTBCMask) >> 5 +} +func (self RadioTapMCSFlags) NESS0() bool { return self&RadioTapMCSFlagsNESS0 != 0 } + +type RadioTapAMPDUStatus struct { + Reference uint32 + Flags RadioTapAMPDUStatusFlags + CRC uint8 +} + +func (self RadioTapAMPDUStatus) String() string { + tokens := []string{ + fmt.Sprintf("ref#%x", self.Reference), + } + if self.Flags.ReportZerolen() && self.Flags.IsZerolen() { + tokens = append(tokens, fmt.Sprintf("zero-length")) + } + if self.Flags.LastKnown() && self.Flags.IsLast() { + tokens = append(tokens, "last") + } + if self.Flags.DelimCRCErr() { + tokens = append(tokens, "delimiter CRC error") + } + if self.Flags.DelimCRCKnown() { + tokens = append(tokens, fmt.Sprintf("delimiter-CRC=%02x", self.CRC)) + } + return strings.Join(tokens, ",") +} + +type RadioTapAMPDUStatusFlags uint16 + +const ( + RadioTapAMPDUStatusFlagsReportZerolen RadioTapAMPDUStatusFlags = 1 << iota + RadioTapAMPDUIsZerolen + RadioTapAMPDULastKnown + RadioTapAMPDUIsLast + RadioTapAMPDUDelimCRCErr + RadioTapAMPDUDelimCRCKnown +) + +func (self RadioTapAMPDUStatusFlags) ReportZerolen() bool { + return self&RadioTapAMPDUStatusFlagsReportZerolen != 0 +} +func (self RadioTapAMPDUStatusFlags) IsZerolen() bool { return self&RadioTapAMPDUIsZerolen != 0 } +func (self RadioTapAMPDUStatusFlags) LastKnown() bool { return self&RadioTapAMPDULastKnown != 0 } +func (self RadioTapAMPDUStatusFlags) IsLast() bool { return self&RadioTapAMPDUIsLast != 0 } +func (self RadioTapAMPDUStatusFlags) DelimCRCErr() bool { return self&RadioTapAMPDUDelimCRCErr != 0 } +func (self RadioTapAMPDUStatusFlags) DelimCRCKnown() bool { return self&RadioTapAMPDUDelimCRCKnown != 0 } + +type RadioTapVHT struct { + Known RadioTapVHTKnown + Flags RadioTapVHTFlags + Bandwidth uint8 + MCSNSS [4]RadioTapVHTMCSNSS + Coding uint8 + GroupId uint8 + PartialAID uint16 +} + +func (self RadioTapVHT) String() string { + var tokens []string + if self.Known.STBC() { + if self.Flags.STBC() { + tokens = append(tokens, "STBC") + } else { + tokens = append(tokens, "no STBC") + } + } + if self.Known.TXOPPSNotAllowed() { + if self.Flags.TXOPPSNotAllowed() { + tokens = append(tokens, "TXOP doze not allowed") + } else { + tokens = append(tokens, "TXOP doze allowed") + } + } + if self.Known.GI() { + if self.Flags.SGI() { + tokens = append(tokens, "short GI") + } else { + tokens = append(tokens, "long GI") + } + } + if self.Known.SGINSYMDisambiguation() { + if self.Flags.SGINSYMMod() { + tokens = append(tokens, "NSYM mod 10=9") + } else { + tokens = append(tokens, "NSYM mod 10!=9 or no short GI") + } + } + if self.Known.LDPCExtraOFDMSymbol() { + if self.Flags.LDPCExtraOFDMSymbol() { + tokens = append(tokens, "LDPC extra OFDM symbols") + } else { + tokens = append(tokens, "no LDPC extra OFDM symbols") + } + } + if self.Known.Beamformed() { + if self.Flags.Beamformed() { + tokens = append(tokens, "beamformed") + } else { + tokens = append(tokens, "no beamformed") + } + } + if self.Known.Bandwidth() { + token := "?" + switch self.Bandwidth & 0x1f { + case 0: + token = "20" + case 1: + token = "40" + case 2: + token = "40(20L)" + case 3: + token = "40(20U)" + case 4: + token = "80" + case 5: + token = "80(40L)" + case 6: + token = "80(40U)" + case 7: + token = "80(20LL)" + case 8: + token = "80(20LU)" + case 9: + token = "80(20UL)" + case 10: + token = "80(20UU)" + case 11: + token = "160" + case 12: + token = "160(80L)" + case 13: + token = "160(80U)" + case 14: + token = "160(40LL)" + case 15: + token = "160(40LU)" + case 16: + token = "160(40UL)" + case 17: + token = "160(40UU)" + case 18: + token = "160(20LLL)" + case 19: + token = "160(20LLU)" + case 20: + token = "160(20LUL)" + case 21: + token = "160(20LUU)" + case 22: + token = "160(20ULL)" + case 23: + token = "160(20ULU)" + case 24: + token = "160(20UUL)" + case 25: + token = "160(20UUU)" + } + tokens = append(tokens, token) + } + for i, MCSNSS := range self.MCSNSS { + if MCSNSS.Present() { + fec := "?" + switch self.Coding & (1 << uint8(i)) { + case 0: + fec = "BCC" + case 1: + fec = "LDPC" + } + tokens = append(tokens, fmt.Sprintf("user%d(%s,%s)", i, MCSNSS.String(), fec)) + } + } + if self.Known.GroupId() { + tokens = append(tokens, + fmt.Sprintf("group=%d", self.GroupId)) + } + if self.Known.PartialAID() { + tokens = append(tokens, + fmt.Sprintf("partial-AID=%d", self.PartialAID)) + } + return strings.Join(tokens, ",") +} + +type RadioTapVHTKnown uint16 + +const ( + RadioTapVHTKnownSTBC RadioTapVHTKnown = 1 << iota + RadioTapVHTKnownTXOPPSNotAllowed + RadioTapVHTKnownGI + RadioTapVHTKnownSGINSYMDisambiguation + RadioTapVHTKnownLDPCExtraOFDMSymbol + RadioTapVHTKnownBeamformed + RadioTapVHTKnownBandwidth + RadioTapVHTKnownGroupId + RadioTapVHTKnownPartialAID +) + +func (self RadioTapVHTKnown) STBC() bool { return self&RadioTapVHTKnownSTBC != 0 } +func (self RadioTapVHTKnown) TXOPPSNotAllowed() bool { + return self&RadioTapVHTKnownTXOPPSNotAllowed != 0 +} +func (self RadioTapVHTKnown) GI() bool { return self&RadioTapVHTKnownGI != 0 } +func (self RadioTapVHTKnown) SGINSYMDisambiguation() bool { + return self&RadioTapVHTKnownSGINSYMDisambiguation != 0 +} +func (self RadioTapVHTKnown) LDPCExtraOFDMSymbol() bool { + return self&RadioTapVHTKnownLDPCExtraOFDMSymbol != 0 +} +func (self RadioTapVHTKnown) Beamformed() bool { return self&RadioTapVHTKnownBeamformed != 0 } +func (self RadioTapVHTKnown) Bandwidth() bool { return self&RadioTapVHTKnownBandwidth != 0 } +func (self RadioTapVHTKnown) GroupId() bool { return self&RadioTapVHTKnownGroupId != 0 } +func (self RadioTapVHTKnown) PartialAID() bool { return self&RadioTapVHTKnownPartialAID != 0 } + +type RadioTapVHTFlags uint8 + +const ( + RadioTapVHTFlagsSTBC RadioTapVHTFlags = 1 << iota + RadioTapVHTFlagsTXOPPSNotAllowed + RadioTapVHTFlagsSGI + RadioTapVHTFlagsSGINSYMMod + RadioTapVHTFlagsLDPCExtraOFDMSymbol + RadioTapVHTFlagsBeamformed +) + +func (self RadioTapVHTFlags) STBC() bool { return self&RadioTapVHTFlagsSTBC != 0 } +func (self RadioTapVHTFlags) TXOPPSNotAllowed() bool { + return self&RadioTapVHTFlagsTXOPPSNotAllowed != 0 +} +func (self RadioTapVHTFlags) SGI() bool { return self&RadioTapVHTFlagsSGI != 0 } +func (self RadioTapVHTFlags) SGINSYMMod() bool { return self&RadioTapVHTFlagsSGINSYMMod != 0 } +func (self RadioTapVHTFlags) LDPCExtraOFDMSymbol() bool { + return self&RadioTapVHTFlagsLDPCExtraOFDMSymbol != 0 +} +func (self RadioTapVHTFlags) Beamformed() bool { return self&RadioTapVHTFlagsBeamformed != 0 } + +type RadioTapVHTMCSNSS uint8 + +func (self RadioTapVHTMCSNSS) Present() bool { + return self&0x0F != 0 +} + +func (self RadioTapVHTMCSNSS) String() string { + return fmt.Sprintf("NSS#%dMCS#%d", uint32(self&0xf), uint32(self>>4)) +} + +func decodeRadioTap(data []byte, p gopacket.PacketBuilder) error { + d := &RadioTap{} + // TODO: Should we set LinkLayer here? And implement LinkFlow + return decodingLayerDecoder(d, data, p) +} + +type RadioTap struct { + BaseLayer + + // Version 0. Only increases for drastic changes, introduction of compatible new fields does not count. + Version uint8 + // Length of the whole header in bytes, including it_version, it_pad, it_len, and data fields. + Length uint16 + // Present is a bitmap telling which fields are present. Set bit 31 (0x80000000) to extend the bitmap by another 32 bits. Additional extensions are made by setting bit 31. + Present RadioTapPresent + // TSFT: value in microseconds of the MAC's 64-bit 802.11 Time Synchronization Function timer when the first bit of the MPDU arrived at the MAC. For received frames, only. + TSFT uint64 + Flags RadioTapFlags + // Rate Tx/Rx data rate + Rate RadioTapRate + // ChannelFrequency Tx/Rx frequency in MHz, followed by flags + ChannelFrequency RadioTapChannelFrequency + ChannelFlags RadioTapChannelFlags + // FHSS For frequency-hopping radios, the hop set (first byte) and pattern (second byte). + FHSS uint16 + // DBMAntennaSignal RF signal power at the antenna, decibel difference from one milliwatt. + DBMAntennaSignal int8 + // DBMAntennaNoise RF noise power at the antenna, decibel difference from one milliwatt. + DBMAntennaNoise int8 + // LockQuality Quality of Barker code lock. Unitless. Monotonically nondecreasing with "better" lock strength. Called "Signal Quality" in datasheets. + LockQuality uint16 + // TxAttenuation Transmit power expressed as unitless distance from max power set at factory calibration. 0 is max power. Monotonically nondecreasing with lower power levels. + TxAttenuation uint16 + // DBTxAttenuation Transmit power expressed as decibel distance from max power set at factory calibration. 0 is max power. Monotonically nondecreasing with lower power levels. + DBTxAttenuation uint16 + // DBMTxPower Transmit power expressed as dBm (decibels from a 1 milliwatt reference). This is the absolute power level measured at the antenna port. + DBMTxPower int8 + // Antenna Unitless indication of the Rx/Tx antenna for this packet. The first antenna is antenna 0. + Antenna uint8 + // DBAntennaSignal RF signal power at the antenna, decibel difference from an arbitrary, fixed reference. + DBAntennaSignal uint8 + // DBAntennaNoise RF noise power at the antenna, decibel difference from an arbitrary, fixed reference point. + DBAntennaNoise uint8 + // + RxFlags RadioTapRxFlags + TxFlags RadioTapTxFlags + RtsRetries uint8 + DataRetries uint8 + MCS RadioTapMCS + AMPDUStatus RadioTapAMPDUStatus + VHT RadioTapVHT +} + +func (m *RadioTap) LayerType() gopacket.LayerType { return LayerTypeRadioTap } + +func (m *RadioTap) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Version = uint8(data[0]) + m.Length = binary.LittleEndian.Uint16(data[2:4]) + m.Present = RadioTapPresent(binary.LittleEndian.Uint32(data[4:8])) + + offset := uint16(4) + + for (binary.LittleEndian.Uint32(data[offset:offset+4]) & 0x80000000) != 0 { + // This parser only handles standard radiotap namespace, + // and expects all fields are packed in the first it_present. + // Extended bitmap will be just ignored. + offset += 4 + } + offset += 4 // skip the bitmap + + if m.Present.TSFT() { + offset += align(offset, 8) + m.TSFT = binary.LittleEndian.Uint64(data[offset : offset+8]) + offset += 8 + } + if m.Present.Flags() { + m.Flags = RadioTapFlags(data[offset]) + offset++ + } + if m.Present.Rate() { + m.Rate = RadioTapRate(data[offset]) + offset++ + } + if m.Present.Channel() { + offset += align(offset, 2) + m.ChannelFrequency = RadioTapChannelFrequency(binary.LittleEndian.Uint16(data[offset : offset+2])) + offset += 2 + m.ChannelFlags = RadioTapChannelFlags(binary.LittleEndian.Uint16(data[offset : offset+2])) + offset += 2 + } + if m.Present.FHSS() { + m.FHSS = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.DBMAntennaSignal() { + m.DBMAntennaSignal = int8(data[offset]) + offset++ + } + if m.Present.DBMAntennaNoise() { + m.DBMAntennaNoise = int8(data[offset]) + offset++ + } + if m.Present.LockQuality() { + offset += align(offset, 2) + m.LockQuality = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.TxAttenuation() { + offset += align(offset, 2) + m.TxAttenuation = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.DBTxAttenuation() { + offset += align(offset, 2) + m.DBTxAttenuation = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.DBMTxPower() { + m.DBMTxPower = int8(data[offset]) + offset++ + } + if m.Present.Antenna() { + m.Antenna = uint8(data[offset]) + offset++ + } + if m.Present.DBAntennaSignal() { + m.DBAntennaSignal = uint8(data[offset]) + offset++ + } + if m.Present.DBAntennaNoise() { + m.DBAntennaNoise = uint8(data[offset]) + offset++ + } + if m.Present.RxFlags() { + offset += align(offset, 2) + m.RxFlags = RadioTapRxFlags(binary.LittleEndian.Uint16(data[offset:])) + offset += 2 + } + if m.Present.TxFlags() { + offset += align(offset, 2) + m.TxFlags = RadioTapTxFlags(binary.LittleEndian.Uint16(data[offset:])) + offset += 2 + } + if m.Present.RtsRetries() { + m.RtsRetries = uint8(data[offset]) + offset++ + } + if m.Present.DataRetries() { + m.DataRetries = uint8(data[offset]) + offset++ + } + if m.Present.MCS() { + m.MCS = RadioTapMCS{ + RadioTapMCSKnown(data[offset]), + RadioTapMCSFlags(data[offset+1]), + uint8(data[offset+2]), + } + offset += 3 + } + if m.Present.AMPDUStatus() { + offset += align(offset, 4) + m.AMPDUStatus = RadioTapAMPDUStatus{ + Reference: binary.LittleEndian.Uint32(data[offset:]), + Flags: RadioTapAMPDUStatusFlags(binary.LittleEndian.Uint16(data[offset+4:])), + CRC: uint8(data[offset+6]), + } + offset += 8 + } + if m.Present.VHT() { + offset += align(offset, 2) + m.VHT = RadioTapVHT{ + Known: RadioTapVHTKnown(binary.LittleEndian.Uint16(data[offset:])), + Flags: RadioTapVHTFlags(data[offset+2]), + Bandwidth: uint8(data[offset+3]), + MCSNSS: [4]RadioTapVHTMCSNSS{ + RadioTapVHTMCSNSS(data[offset+4]), + RadioTapVHTMCSNSS(data[offset+5]), + RadioTapVHTMCSNSS(data[offset+6]), + RadioTapVHTMCSNSS(data[offset+7]), + }, + Coding: uint8(data[offset+8]), + GroupId: uint8(data[offset+9]), + PartialAID: binary.LittleEndian.Uint16(data[offset+10:]), + } + offset += 12 + } + + payload := data[m.Length:] + if !m.Flags.FCS() { // Dot11.DecodeFromBytes() expects FCS present + fcs := make([]byte, 4) + h := crc32.NewIEEE() + h.Write(payload) + binary.LittleEndian.PutUint32(fcs, h.Sum32()) + payload = append(payload, fcs...) + } + m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: payload} + + return nil +} + +func (m RadioTap) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf := make([]byte, 1024) + + buf[0] = m.Version + buf[1] = 0 + + binary.LittleEndian.PutUint32(buf[4:8], uint32(m.Present)) + + offset := uint16(4) + + for (binary.LittleEndian.Uint32(buf[offset:offset+4]) & 0x80000000) != 0 { + offset += 4 + } + + offset += 4 + + if m.Present.TSFT() { + offset += align(offset, 8) + binary.LittleEndian.PutUint64(buf[offset:offset+8], m.TSFT) + offset += 8 + } + + if m.Present.Flags() { + buf[offset] = uint8(m.Flags) + offset++ + } + + if m.Present.Rate() { + buf[offset] = uint8(m.Rate) + offset++ + } + + if m.Present.Channel() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.ChannelFrequency)) + offset += 2 + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.ChannelFlags)) + offset += 2 + } + + if m.Present.FHSS() { + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.FHSS) + offset += 2 + } + + if m.Present.DBMAntennaSignal() { + buf[offset] = byte(m.DBMAntennaSignal) + offset++ + } + + if m.Present.DBMAntennaNoise() { + buf[offset] = byte(m.DBMAntennaNoise) + offset++ + } + + if m.Present.LockQuality() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.LockQuality) + offset += 2 + } + + if m.Present.TxAttenuation() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.TxAttenuation) + offset += 2 + } + + if m.Present.DBTxAttenuation() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.DBTxAttenuation) + offset += 2 + } + + if m.Present.DBMTxPower() { + buf[offset] = byte(m.DBMTxPower) + offset++ + } + + if m.Present.Antenna() { + buf[offset] = uint8(m.Antenna) + offset++ + } + + if m.Present.DBAntennaSignal() { + buf[offset] = uint8(m.DBAntennaSignal) + offset++ + } + + if m.Present.DBAntennaNoise() { + buf[offset] = uint8(m.DBAntennaNoise) + offset++ + } + + if m.Present.RxFlags() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.RxFlags)) + offset += 2 + } + + if m.Present.TxFlags() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.TxFlags)) + offset += 2 + } + + if m.Present.RtsRetries() { + buf[offset] = m.RtsRetries + offset++ + } + + if m.Present.DataRetries() { + buf[offset] = m.DataRetries + offset++ + } + + if m.Present.MCS() { + buf[offset] = uint8(m.MCS.Known) + buf[offset+1] = uint8(m.MCS.Flags) + buf[offset+2] = uint8(m.MCS.MCS) + + offset += 3 + } + + if m.Present.AMPDUStatus() { + offset += align(offset, 4) + + binary.LittleEndian.PutUint32(buf[offset:offset+4], m.AMPDUStatus.Reference) + binary.LittleEndian.PutUint16(buf[offset+4:offset+6], uint16(m.AMPDUStatus.Flags)) + + buf[offset+6] = m.AMPDUStatus.CRC + + offset += 8 + } + + if m.Present.VHT() { + offset += align(offset, 2) + + binary.LittleEndian.PutUint16(buf[offset:], uint16(m.VHT.Known)) + + buf[offset+2] = uint8(m.VHT.Flags) + buf[offset+3] = uint8(m.VHT.Bandwidth) + buf[offset+4] = uint8(m.VHT.MCSNSS[0]) + buf[offset+5] = uint8(m.VHT.MCSNSS[1]) + buf[offset+6] = uint8(m.VHT.MCSNSS[2]) + buf[offset+7] = uint8(m.VHT.MCSNSS[3]) + buf[offset+8] = uint8(m.VHT.Coding) + buf[offset+9] = uint8(m.VHT.GroupId) + + binary.LittleEndian.PutUint16(buf[offset+10:offset+12], m.VHT.PartialAID) + + offset += 12 + } + + packetBuf, err := b.PrependBytes(int(offset)) + + if err != nil { + return err + } + + if opts.FixLengths { + m.Length = offset + } + + binary.LittleEndian.PutUint16(buf[2:4], m.Length) + + copy(packetBuf, buf) + + return nil +} + +func (m *RadioTap) CanDecode() gopacket.LayerClass { return LayerTypeRadioTap } +func (m *RadioTap) NextLayerType() gopacket.LayerType { return LayerTypeDot11 } diff --git a/vendor/github.com/google/gopacket/layers/radiotap_test.go b/vendor/github.com/google/gopacket/layers/radiotap_test.go new file mode 100644 index 00000000..4d3c7d01 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/radiotap_test.go @@ -0,0 +1,79 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +package layers + +import ( + "github.com/google/gopacket" + "testing" +) + +// testPacketRadiotap0 is the packet: +// 09:34:34.799438 1.0 Mb/s 2412 MHz 11b -58dB signal antenna 7 Acknowledgment RA:88:1f:a1:ae:9d:cb +// 0x0000: 0000 1200 2e48 0000 1002 6c09 a000 c607 .....H....l..... +// 0x0010: 0000 d400 0000 881f a1ae 9dcb c630 4b4b .............0KK +var testPacketRadiotap0 = []byte{ + 0x00, 0x00, 0x12, 0x00, 0x2e, 0x48, 0x00, 0x00, 0x10, 0x02, 0x6c, 0x09, 0xa0, 0x00, 0xc6, 0x07, + 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x88, 0x1f, 0xa1, 0xae, 0x9d, 0xcb, 0xc6, 0x30, 0x4b, 0x4b, +} + +func TestPacketRadiotap0(t *testing.T) { + p := gopacket.NewPacket(testPacketRadiotap0, LayerTypeRadioTap, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11}, t) + rt := p.Layer(LayerTypeRadioTap).(*RadioTap) + if rt.ChannelFrequency != 2412 || rt.DBMAntennaSignal != -58 || rt.Antenna != 7 { + t.Error("Radiotap decode error") + } + if rt.Rate != 2 { // 500Kbps unit + t.Error("Radiotap Rate decode error") + } +} +func BenchmarkDecodePacketRadiotap0(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketRadiotap0, LayerTypeRadioTap, gopacket.NoCopy) + } +} + +// testPacketRadiotap1 is the packet: +// 05:24:21.380948 2412 MHz 11g -36dB signal antenna 5 65.0 Mb/s MCS 7 20 MHz lon GI +// 0x0000: 0000 1500 2a48 0800 1000 6c09 8004 dc05 ....*H....l..... +// 0x0010: 0000 0700 0748 112c 0000 3a9d aaf0 191c .....H.,..:..... +// 0x0020: aba7 f213 9d00 3a9d aaf0 1970 b2ee a9f1 ......:....p.... +// 0x0030: 16 . +var testPacketRadiotap1 = []byte{ + 0x00, 0x00, 0x15, 0x00, 0x2a, 0x48, 0x08, 0x00, 0x10, 0x00, 0x6c, 0x09, 0x80, 0x04, 0xdc, 0x05, + 0x00, 0x00, 0x07, 0x00, 0x07, 0x48, 0x11, 0x2c, 0x00, 0x00, 0x3a, 0x9d, 0xaa, 0xf0, 0x19, 0x1c, + 0xab, 0xa7, 0xf2, 0x13, 0x9d, 0x00, 0x3a, 0x9d, 0xaa, 0xf0, 0x19, 0x70, 0xb2, 0xee, 0xa9, 0xf1, + 0x16, +} + +func TestPacketRadiotap1(t *testing.T) { + p := gopacket.NewPacket(testPacketRadiotap1, LayerTypeRadioTap, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeRadioTap, LayerTypeDot11}, t) + rt := p.Layer(LayerTypeRadioTap).(*RadioTap) + if rt.ChannelFrequency != 2412 || rt.DBMAntennaSignal != -36 || rt.Antenna != 5 { + t.Error("Radiotap decode error") + } + if !rt.MCS.Known.MCSIndex() || rt.MCS.MCS != 7 { + t.Error("Radiotap MCS error") + } + if !rt.MCS.Known.Bandwidth() || rt.MCS.Flags.Bandwidth() != 0 { + t.Error("Radiotap bandwidth error") + } + if !rt.MCS.Known.GuardInterval() || rt.MCS.Flags.ShortGI() { + t.Error("Radiotap GI error") + } +} +func BenchmarkDecodePacketRadiotap1(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketRadiotap1, LayerTypeRadioTap, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/rudp.go b/vendor/github.com/google/gopacket/layers/rudp.go new file mode 100644 index 00000000..8435129b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/rudp.go @@ -0,0 +1,93 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +type RUDP struct { + BaseLayer + SYN, ACK, EACK, RST, NUL bool + Version uint8 + HeaderLength uint8 + SrcPort, DstPort RUDPPort + DataLength uint16 + Seq, Ack, Checksum uint32 + VariableHeaderArea []byte + // RUDPHeaderSyn contains SYN information for the RUDP packet, + // if the SYN flag is set + *RUDPHeaderSYN + // RUDPHeaderEack contains EACK information for the RUDP packet, + // if the EACK flag is set. + *RUDPHeaderEACK +} + +type RUDPHeaderSYN struct { + MaxOutstandingSegments, MaxSegmentSize, OptionFlags uint16 +} + +type RUDPHeaderEACK struct { + SeqsReceivedOK []uint32 +} + +// LayerType returns gopacket.LayerTypeRUDP. +func (r *RUDP) LayerType() gopacket.LayerType { return LayerTypeRUDP } + +func decodeRUDP(data []byte, p gopacket.PacketBuilder) error { + r := &RUDP{ + SYN: data[0]&0x80 != 0, + ACK: data[0]&0x40 != 0, + EACK: data[0]&0x20 != 0, + RST: data[0]&0x10 != 0, + NUL: data[0]&0x08 != 0, + Version: data[0] & 0x3, + HeaderLength: data[1], + SrcPort: RUDPPort(data[2]), + DstPort: RUDPPort(data[3]), + DataLength: binary.BigEndian.Uint16(data[4:6]), + Seq: binary.BigEndian.Uint32(data[6:10]), + Ack: binary.BigEndian.Uint32(data[10:14]), + Checksum: binary.BigEndian.Uint32(data[14:18]), + } + if r.HeaderLength < 9 { + return fmt.Errorf("RUDP packet with too-short header length %d", r.HeaderLength) + } + hlen := int(r.HeaderLength) * 2 + r.Contents = data[:hlen] + r.Payload = data[hlen : hlen+int(r.DataLength)] + r.VariableHeaderArea = data[18:hlen] + headerData := r.VariableHeaderArea + switch { + case r.SYN: + if len(headerData) != 6 { + return fmt.Errorf("RUDP packet invalid SYN header length: %d", len(headerData)) + } + r.RUDPHeaderSYN = &RUDPHeaderSYN{ + MaxOutstandingSegments: binary.BigEndian.Uint16(headerData[:2]), + MaxSegmentSize: binary.BigEndian.Uint16(headerData[2:4]), + OptionFlags: binary.BigEndian.Uint16(headerData[4:6]), + } + case r.EACK: + if len(headerData)%4 != 0 { + return fmt.Errorf("RUDP packet invalid EACK header length: %d", len(headerData)) + } + r.RUDPHeaderEACK = &RUDPHeaderEACK{make([]uint32, len(headerData)/4)} + for i := 0; i < len(headerData); i += 4 { + r.SeqsReceivedOK[i/4] = binary.BigEndian.Uint32(headerData[i : i+4]) + } + } + p.AddLayer(r) + p.SetTransportLayer(r) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +func (r *RUDP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointRUDPPort, []byte{byte(r.SrcPort)}, []byte{byte(r.DstPort)}) +} diff --git a/vendor/github.com/google/gopacket/layers/sctp.go b/vendor/github.com/google/gopacket/layers/sctp.go new file mode 100644 index 00000000..511176e5 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/sctp.go @@ -0,0 +1,746 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "hash/crc32" + + "github.com/google/gopacket" +) + +// SCTP contains information on the top level of an SCTP packet. +type SCTP struct { + BaseLayer + SrcPort, DstPort SCTPPort + VerificationTag uint32 + Checksum uint32 + sPort, dPort []byte +} + +// LayerType returns gopacket.LayerTypeSCTP +func (s *SCTP) LayerType() gopacket.LayerType { return LayerTypeSCTP } + +func decodeSCTP(data []byte, p gopacket.PacketBuilder) error { + sctp := &SCTP{} + err := sctp.DecodeFromBytes(data, p) + p.AddLayer(sctp) + p.SetTransportLayer(sctp) + if err != nil { + return err + } + return p.NextDecoder(sctpChunkTypePrefixDecoder) +} + +var sctpChunkTypePrefixDecoder = gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix) + +// TransportFlow returns a flow based on the source and destination SCTP port. +func (s *SCTP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointSCTPPort, s.sPort, s.dPort) +} + +func decodeWithSCTPChunkTypePrefix(data []byte, p gopacket.PacketBuilder) error { + chunkType := SCTPChunkType(data[0]) + return chunkType.Decode(data, p) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (s SCTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(12) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes[0:2], uint16(s.SrcPort)) + binary.BigEndian.PutUint16(bytes[2:4], uint16(s.DstPort)) + binary.BigEndian.PutUint32(bytes[4:8], s.VerificationTag) + if opts.ComputeChecksums { + // Note: MakeTable(Castagnoli) actually only creates the table once, then + // passes back a singleton on every other call, so this shouldn't cause + // excessive memory allocation. + binary.LittleEndian.PutUint32(bytes[8:12], crc32.Checksum(b.Bytes(), crc32.MakeTable(crc32.Castagnoli))) + } + return nil +} + +func (sctp *SCTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 12 { + return errors.New("Invalid SCTP common header length") + } + sctp.SrcPort = SCTPPort(binary.BigEndian.Uint16(data[:2])) + sctp.sPort = data[:2] + sctp.DstPort = SCTPPort(binary.BigEndian.Uint16(data[2:4])) + sctp.dPort = data[2:4] + sctp.VerificationTag = binary.BigEndian.Uint32(data[4:8]) + sctp.Checksum = binary.BigEndian.Uint32(data[8:12]) + sctp.BaseLayer = BaseLayer{data[:12], data[12:]} + + return nil +} + +func (t *SCTP) CanDecode() gopacket.LayerClass { + return LayerTypeSCTP +} + +func (t *SCTP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// SCTPChunk contains the common fields in all SCTP chunks. +type SCTPChunk struct { + BaseLayer + Type SCTPChunkType + Flags uint8 + Length uint16 + // ActualLength is the total length of an SCTP chunk, including padding. + // SCTP chunks start and end on 4-byte boundaries. So if a chunk has a length + // of 18, it means that it has data up to and including byte 18, then padding + // up to the next 4-byte boundary, 20. In this case, Length would be 18, and + // ActualLength would be 20. + ActualLength int +} + +func roundUpToNearest4(i int) int { + if i%4 == 0 { + return i + } + return i + 4 - (i % 4) +} + +func decodeSCTPChunk(data []byte) (SCTPChunk, error) { + length := binary.BigEndian.Uint16(data[2:4]) + if length < 4 { + return SCTPChunk{}, errors.New("invalid SCTP chunk length") + } + actual := roundUpToNearest4(int(length)) + ct := SCTPChunkType(data[0]) + + // For SCTP Data, use a separate layer for the payload + delta := 0 + if ct == SCTPChunkTypeData { + delta = int(actual) - int(length) + actual = 16 + } + + return SCTPChunk{ + Type: ct, + Flags: data[1], + Length: length, + ActualLength: actual, + BaseLayer: BaseLayer{data[:actual], data[actual : len(data)-delta]}, + }, nil +} + +// SCTPParameter is a TLV parameter inside a SCTPChunk. +type SCTPParameter struct { + Type uint16 + Length uint16 + ActualLength int + Value []byte +} + +func decodeSCTPParameter(data []byte) SCTPParameter { + length := binary.BigEndian.Uint16(data[2:4]) + return SCTPParameter{ + Type: binary.BigEndian.Uint16(data[0:2]), + Length: length, + Value: data[4:length], + ActualLength: roundUpToNearest4(int(length)), + } +} + +func (p SCTPParameter) Bytes() []byte { + length := 4 + len(p.Value) + data := make([]byte, roundUpToNearest4(length)) + binary.BigEndian.PutUint16(data[0:2], p.Type) + binary.BigEndian.PutUint16(data[2:4], uint16(length)) + copy(data[4:], p.Value) + return data +} + +// SCTPUnknownChunkType is the layer type returned when we don't recognize the +// chunk type. Since there's a length in a known location, we can skip over +// it even if we don't know what it is, and continue parsing the rest of the +// chunks. This chunk is stored as an ErrorLayer in the packet. +type SCTPUnknownChunkType struct { + SCTPChunk + bytes []byte +} + +func decodeSCTPChunkTypeUnknown(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPUnknownChunkType{SCTPChunk: chunk} + sc.bytes = data[:sc.ActualLength] + p.AddLayer(sc) + p.SetErrorLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (s SCTPUnknownChunkType) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(s.ActualLength) + if err != nil { + return err + } + copy(bytes, s.bytes) + return nil +} + +// LayerType returns gopacket.LayerTypeSCTPUnknownChunkType. +func (s *SCTPUnknownChunkType) LayerType() gopacket.LayerType { return LayerTypeSCTPUnknownChunkType } + +// Payload returns all bytes in this header, including the decoded Type, Length, +// and Flags. +func (s *SCTPUnknownChunkType) Payload() []byte { return s.bytes } + +// Error implements ErrorLayer. +func (s *SCTPUnknownChunkType) Error() error { + return fmt.Errorf("No decode method available for SCTP chunk type %s", s.Type) +} + +// SCTPData is the SCTP Data chunk layer. +type SCTPData struct { + SCTPChunk + Unordered, BeginFragment, EndFragment bool + TSN uint32 + StreamId uint16 + StreamSequence uint16 + PayloadProtocol SCTPPayloadProtocol +} + +// LayerType returns gopacket.LayerTypeSCTPData. +func (s *SCTPData) LayerType() gopacket.LayerType { return LayerTypeSCTPData } + +// SCTPPayloadProtocol represents a payload protocol +type SCTPPayloadProtocol uint32 + +// SCTPPayloadProtocol constonts from http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml +const ( + SCTPProtocolReserved SCTPPayloadProtocol = 0 + SCTPPayloadUIA = 1 + SCTPPayloadM2UA = 2 + SCTPPayloadM3UA = 3 + SCTPPayloadSUA = 4 + SCTPPayloadM2PA = 5 + SCTPPayloadV5UA = 6 + SCTPPayloadH248 = 7 + SCTPPayloadBICC = 8 + SCTPPayloadTALI = 9 + SCTPPayloadDUA = 10 + SCTPPayloadASAP = 11 + SCTPPayloadENRP = 12 + SCTPPayloadH323 = 13 + SCTPPayloadQIPC = 14 + SCTPPayloadSIMCO = 15 + SCTPPayloadDDPSegment = 16 + SCTPPayloadDDPStream = 17 + SCTPPayloadS1AP = 18 +) + +func (p SCTPPayloadProtocol) String() string { + switch p { + case SCTPProtocolReserved: + return "Reserved" + case SCTPPayloadUIA: + return "UIA" + case SCTPPayloadM2UA: + return "M2UA" + case SCTPPayloadM3UA: + return "M3UA" + case SCTPPayloadSUA: + return "SUA" + case SCTPPayloadM2PA: + return "M2PA" + case SCTPPayloadV5UA: + return "V5UA" + case SCTPPayloadH248: + return "H.248" + case SCTPPayloadBICC: + return "BICC" + case SCTPPayloadTALI: + return "TALI" + case SCTPPayloadDUA: + return "DUA" + case SCTPPayloadASAP: + return "ASAP" + case SCTPPayloadENRP: + return "ENRP" + case SCTPPayloadH323: + return "H.323" + case SCTPPayloadQIPC: + return "QIPC" + case SCTPPayloadSIMCO: + return "SIMCO" + case SCTPPayloadDDPSegment: + return "DDPSegment" + case SCTPPayloadDDPStream: + return "DDPStream" + case SCTPPayloadS1AP: + return "S1AP" + } + return fmt.Sprintf("Unknown(%d)", p) +} + +func decodeSCTPData(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPData{ + SCTPChunk: chunk, + Unordered: data[1]&0x4 != 0, + BeginFragment: data[1]&0x2 != 0, + EndFragment: data[1]&0x1 != 0, + TSN: binary.BigEndian.Uint32(data[4:8]), + StreamId: binary.BigEndian.Uint16(data[8:10]), + StreamSequence: binary.BigEndian.Uint16(data[10:12]), + PayloadProtocol: SCTPPayloadProtocol(binary.BigEndian.Uint32(data[12:16])), + } + // Length is the length in bytes of the data, INCLUDING the 16-byte header. + p.AddLayer(sc) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPData) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + payload := b.Bytes() + // Pad the payload to a 32 bit boundary + if rem := len(payload) % 4; rem != 0 { + b.AppendBytes(4 - rem) + } + length := 16 + bytes, err := b.PrependBytes(length) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + flags := uint8(0) + if sc.Unordered { + flags |= 0x4 + } + if sc.BeginFragment { + flags |= 0x2 + } + if sc.EndFragment { + flags |= 0x1 + } + bytes[1] = flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length+len(payload))) + binary.BigEndian.PutUint32(bytes[4:8], sc.TSN) + binary.BigEndian.PutUint16(bytes[8:10], sc.StreamId) + binary.BigEndian.PutUint16(bytes[10:12], sc.StreamSequence) + binary.BigEndian.PutUint32(bytes[12:16], uint32(sc.PayloadProtocol)) + return nil +} + +// SCTPInitParameter is a parameter for an SCTP Init or InitAck packet. +type SCTPInitParameter SCTPParameter + +// SCTPInit is used as the return value for both SCTPInit and SCTPInitAck +// messages. +type SCTPInit struct { + SCTPChunk + InitiateTag uint32 + AdvertisedReceiverWindowCredit uint32 + OutboundStreams, InboundStreams uint16 + InitialTSN uint32 + Parameters []SCTPInitParameter +} + +// LayerType returns either gopacket.LayerTypeSCTPInit or gopacket.LayerTypeSCTPInitAck. +func (sc *SCTPInit) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeInitAck { + return LayerTypeSCTPInitAck + } + // sc.Type == SCTPChunkTypeInit + return LayerTypeSCTPInit +} + +func decodeSCTPInit(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPInit{ + SCTPChunk: chunk, + InitiateTag: binary.BigEndian.Uint32(data[4:8]), + AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]), + OutboundStreams: binary.BigEndian.Uint16(data[12:14]), + InboundStreams: binary.BigEndian.Uint16(data[14:16]), + InitialTSN: binary.BigEndian.Uint32(data[16:20]), + } + paramData := data[20:sc.ActualLength] + for len(paramData) > 0 { + p := SCTPInitParameter(decodeSCTPParameter(paramData)) + paramData = paramData[p.ActualLength:] + sc.Parameters = append(sc.Parameters, p) + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPInit) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var payload []byte + for _, param := range sc.Parameters { + payload = append(payload, SCTPParameter(param).Bytes()...) + } + length := 20 + len(payload) + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + binary.BigEndian.PutUint32(bytes[4:8], sc.InitiateTag) + binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit) + binary.BigEndian.PutUint16(bytes[12:14], sc.OutboundStreams) + binary.BigEndian.PutUint16(bytes[14:16], sc.InboundStreams) + binary.BigEndian.PutUint32(bytes[16:20], sc.InitialTSN) + copy(bytes[20:], payload) + return nil +} + +// SCTPSack is the SCTP Selective ACK chunk layer. +type SCTPSack struct { + SCTPChunk + CumulativeTSNAck uint32 + AdvertisedReceiverWindowCredit uint32 + NumGapACKs, NumDuplicateTSNs uint16 + GapACKs []uint16 + DuplicateTSNs []uint32 +} + +// LayerType return LayerTypeSCTPSack +func (sc *SCTPSack) LayerType() gopacket.LayerType { + return LayerTypeSCTPSack +} + +func decodeSCTPSack(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPSack{ + SCTPChunk: chunk, + CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]), + AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]), + NumGapACKs: binary.BigEndian.Uint16(data[12:14]), + NumDuplicateTSNs: binary.BigEndian.Uint16(data[14:16]), + } + // We maximize gapAcks and dupTSNs here so we're not allocating tons + // of memory based on a user-controlable field. Our maximums are not exact, + // but should give us sane defaults... we'll still hit slice boundaries and + // fail if the user-supplied values are too high (in the for loops below), but + // the amount of memory we'll have allocated because of that should be small + // (< sc.ActualLength) + gapAcks := sc.SCTPChunk.ActualLength / 2 + dupTSNs := (sc.SCTPChunk.ActualLength - gapAcks*2) / 4 + if gapAcks > int(sc.NumGapACKs) { + gapAcks = int(sc.NumGapACKs) + } + if dupTSNs > int(sc.NumDuplicateTSNs) { + dupTSNs = int(sc.NumDuplicateTSNs) + } + sc.GapACKs = make([]uint16, 0, gapAcks) + sc.DuplicateTSNs = make([]uint32, 0, dupTSNs) + bytesRemaining := data[16:] + for i := 0; i < int(sc.NumGapACKs); i++ { + sc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2])) + bytesRemaining = bytesRemaining[2:] + } + for i := 0; i < int(sc.NumDuplicateTSNs); i++ { + sc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4])) + bytesRemaining = bytesRemaining[4:] + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPSack) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + length := 16 + 2*len(sc.GapACKs) + 4*len(sc.DuplicateTSNs) + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck) + binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit) + binary.BigEndian.PutUint16(bytes[12:14], uint16(len(sc.GapACKs))) + binary.BigEndian.PutUint16(bytes[14:16], uint16(len(sc.DuplicateTSNs))) + for i, v := range sc.GapACKs { + binary.BigEndian.PutUint16(bytes[16+i*2:], v) + } + offset := 16 + 2*len(sc.GapACKs) + for i, v := range sc.DuplicateTSNs { + binary.BigEndian.PutUint32(bytes[offset+i*4:], v) + } + return nil +} + +// SCTPHeartbeatParameter is the parameter type used by SCTP heartbeat and +// heartbeat ack layers. +type SCTPHeartbeatParameter SCTPParameter + +// SCTPHeartbeat is the SCTP heartbeat layer, also used for heatbeat ack. +type SCTPHeartbeat struct { + SCTPChunk + Parameters []SCTPHeartbeatParameter +} + +// LayerType returns gopacket.LayerTypeSCTPHeartbeat. +func (sc *SCTPHeartbeat) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeHeartbeatAck { + return LayerTypeSCTPHeartbeatAck + } + // sc.Type == SCTPChunkTypeHeartbeat + return LayerTypeSCTPHeartbeat +} + +func decodeSCTPHeartbeat(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPHeartbeat{ + SCTPChunk: chunk, + } + paramData := data[4:sc.Length] + for len(paramData) > 0 { + p := SCTPHeartbeatParameter(decodeSCTPParameter(paramData)) + paramData = paramData[p.ActualLength:] + sc.Parameters = append(sc.Parameters, p) + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPHeartbeat) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var payload []byte + for _, param := range sc.Parameters { + payload = append(payload, SCTPParameter(param).Bytes()...) + } + length := 4 + len(payload) + + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + copy(bytes[4:], payload) + return nil +} + +// SCTPErrorParameter is the parameter type used by SCTP Abort and Error layers. +type SCTPErrorParameter SCTPParameter + +// SCTPError is the SCTP error layer, also used for SCTP aborts. +type SCTPError struct { + SCTPChunk + Parameters []SCTPErrorParameter +} + +// LayerType returns LayerTypeSCTPAbort or LayerTypeSCTPError. +func (sc *SCTPError) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeAbort { + return LayerTypeSCTPAbort + } + // sc.Type == SCTPChunkTypeError + return LayerTypeSCTPError +} + +func decodeSCTPError(data []byte, p gopacket.PacketBuilder) error { + // remarkably similar to decodeSCTPHeartbeat ;) + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPError{ + SCTPChunk: chunk, + } + paramData := data[4:sc.Length] + for len(paramData) > 0 { + p := SCTPErrorParameter(decodeSCTPParameter(paramData)) + paramData = paramData[p.ActualLength:] + sc.Parameters = append(sc.Parameters, p) + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPError) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var payload []byte + for _, param := range sc.Parameters { + payload = append(payload, SCTPParameter(param).Bytes()...) + } + length := 4 + len(payload) + + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + copy(bytes[4:], payload) + return nil +} + +// SCTPShutdown is the SCTP shutdown layer. +type SCTPShutdown struct { + SCTPChunk + CumulativeTSNAck uint32 +} + +// LayerType returns gopacket.LayerTypeSCTPShutdown. +func (sc *SCTPShutdown) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdown } + +func decodeSCTPShutdown(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPShutdown{ + SCTPChunk: chunk, + CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]), + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPShutdown) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], 8) + binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck) + return nil +} + +// SCTPShutdownAck is the SCTP shutdown layer. +type SCTPShutdownAck struct { + SCTPChunk +} + +// LayerType returns gopacket.LayerTypeSCTPShutdownAck. +func (sc *SCTPShutdownAck) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdownAck } + +func decodeSCTPShutdownAck(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPShutdownAck{ + SCTPChunk: chunk, + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPShutdownAck) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], 4) + return nil +} + +// SCTPCookieEcho is the SCTP Cookie Echo layer. +type SCTPCookieEcho struct { + SCTPChunk + Cookie []byte +} + +// LayerType returns gopacket.LayerTypeSCTPCookieEcho. +func (sc *SCTPCookieEcho) LayerType() gopacket.LayerType { return LayerTypeSCTPCookieEcho } + +func decodeSCTPCookieEcho(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPCookieEcho{ + SCTPChunk: chunk, + } + sc.Cookie = data[4:sc.Length] + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPCookieEcho) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + length := 4 + len(sc.Cookie) + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + copy(bytes[4:], sc.Cookie) + return nil +} + +// This struct is used by all empty SCTP chunks (currently CookieAck and +// ShutdownComplete). +type SCTPEmptyLayer struct { + SCTPChunk +} + +// LayerType returns either gopacket.LayerTypeSCTPShutdownComplete or +// LayerTypeSCTPCookieAck. +func (sc *SCTPEmptyLayer) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeShutdownComplete { + return LayerTypeSCTPShutdownComplete + } + // sc.Type == SCTPChunkTypeCookieAck + return LayerTypeSCTPCookieAck +} + +func decodeSCTPEmptyLayer(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPEmptyLayer{ + SCTPChunk: chunk, + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPEmptyLayer) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], 4) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/sflow.go b/vendor/github.com/google/gopacket/layers/sflow.go new file mode 100644 index 00000000..55ce31e7 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/sflow.go @@ -0,0 +1,2187 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/* +This layer decodes SFlow version 5 datagrams. + +The specification can be found here: http://sflow.org/sflow_version_5.txt + +Additional developer information about sflow can be found at: +http://sflow.org/developers/specifications.php + +And SFlow in general: +http://sflow.org/index.php + +Two forms of sample data are defined: compact and expanded. The +Specification has this to say: + + Compact and expand forms of counter and flow samples are defined. + An agent must not mix compact/expanded encodings. If an agent + will never use ifIndex numbers >= 2^24 then it must use compact + encodings for all interfaces. Otherwise the expanded formats must + be used for all interfaces. + +This decoder only supports the compact form, because that is the only +one for which data was avaialble. + +The datagram is composed of one or more samples of type flow or counter, +and each sample is composed of one or more records describing the sample. +A sample is a single instance of sampled inforamtion, and each record in +the sample gives additional / supplimentary information about the sample. + +The following sample record types are supported: + + Raw Packet Header + opaque = flow_data; enterprise = 0; format = 1 + + Extended Switch Data + opaque = flow_data; enterprise = 0; format = 1001 + + Extended Router Data + opaque = flow_data; enterprise = 0; format = 1002 + + Extended Gateway Data + opaque = flow_data; enterprise = 0; format = 1003 + + Extended User Data + opaque = flow_data; enterprise = 0; format = 1004 + + Extended URL Data + opaque = flow_data; enterprise = 0; format = 1005 + +The following types of counter records are supported: + + Generic Interface Counters - see RFC 2233 + opaque = counter_data; enterprise = 0; format = 1 + + Ethernet Interface Counters - see RFC 2358 + opaque = counter_data; enterprise = 0; format = 2 + +SFlow is encoded using XDR (RFC4506). There are a few places +where the standard 4-byte fields are partitioned into two +bitfields of different lengths. I'm not sure why the designers +chose to pack together two values like this in some places, and +in others they use the entire 4-byte value to store a number that +will never be more than a few bits. In any case, there are a couple +of types defined to handle the decoding of these bitfields, and +that's why they're there. */ + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// SFlowRecord holds both flow sample records and counter sample records. +// A Record is the structure that actually holds the sampled data +// and / or counters. +type SFlowRecord interface { +} + +// SFlowDataSource encodes a 2-bit SFlowSourceFormat in its most significant +// 2 bits, and an SFlowSourceValue in its least significant 30 bits. +// These types and values define the meaning of the inteface information +// presented in the sample metadata. +type SFlowDataSource int32 + +func (sdc SFlowDataSource) decode() (SFlowSourceFormat, SFlowSourceValue) { + leftField := sdc >> 30 + rightField := uint32(0x3FFFFFFF) & uint32(sdc) + return SFlowSourceFormat(leftField), SFlowSourceValue(rightField) +} + +type SFlowDataSourceExpanded struct { + SourceIDClass SFlowSourceFormat + SourceIDIndex SFlowSourceValue +} + +func (sdce SFlowDataSourceExpanded) decode() (SFlowSourceFormat, SFlowSourceValue) { + leftField := sdce.SourceIDClass >> 30 + rightField := uint32(0x3FFFFFFF) & uint32(sdce.SourceIDIndex) + return SFlowSourceFormat(leftField), SFlowSourceValue(rightField) +} + +type SFlowSourceFormat uint32 + +type SFlowSourceValue uint32 + +const ( + SFlowTypeSingleInterface SFlowSourceFormat = 0 + SFlowTypePacketDiscarded SFlowSourceFormat = 1 + SFlowTypeMultipleDestinations SFlowSourceFormat = 2 +) + +func (sdf SFlowSourceFormat) String() string { + switch sdf { + case SFlowTypeSingleInterface: + return "Single Interface" + case SFlowTypePacketDiscarded: + return "Packet Discarded" + case SFlowTypeMultipleDestinations: + return "Multiple Destinations" + default: + return "UNKNOWN" + } +} + +func decodeSFlow(data []byte, p gopacket.PacketBuilder) error { + s := &SFlowDatagram{} + err := s.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(s) + p.SetApplicationLayer(s) + return nil +} + +// SFlowDatagram is the outermost container which holds some basic information +// about the reporting agent, and holds at least one sample record +type SFlowDatagram struct { + BaseLayer + + DatagramVersion uint32 + AgentAddress net.IP + SubAgentID uint32 + SequenceNumber uint32 + AgentUptime uint32 + SampleCount uint32 + FlowSamples []SFlowFlowSample + CounterSamples []SFlowCounterSample +} + +// An SFlow datagram's outer container has the following +// structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sFlow version (2|4|5) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int IP version of the Agent (1=v4|2=v6) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Agent IP address (v4=4byte|v6=16byte) / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sub agent id | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int datagram sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int switch uptime in ms | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int n samples in datagram | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / n samples / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// SFlowDataFormat encodes the EnterpriseID in the most +// significant 12 bits, and the SampleType in the least significant +// 20 bits. +type SFlowDataFormat uint32 + +func (sdf SFlowDataFormat) decode() (SFlowEnterpriseID, SFlowSampleType) { + leftField := sdf >> 12 + rightField := uint32(0xFFF) & uint32(sdf) + return SFlowEnterpriseID(leftField), SFlowSampleType(rightField) +} + +// SFlowEnterpriseID is used to differentiate between the +// official SFlow standard, and other, vendor-specific +// types of flow data. (Similiar to SNMP's enterprise MIB +// OIDs) Only the office SFlow Enterprise ID is decoded +// here. +type SFlowEnterpriseID uint32 + +const ( + SFlowStandard SFlowEnterpriseID = 0 +) + +func (eid SFlowEnterpriseID) String() string { + switch eid { + case SFlowStandard: + return "Standard SFlow" + default: + return "" + } +} + +func (eid SFlowEnterpriseID) GetType() SFlowEnterpriseID { + return SFlowStandard +} + +// SFlowSampleType specifies the type of sample. Only flow samples +// and counter samples are supported +type SFlowSampleType uint32 + +const ( + SFlowTypeFlowSample SFlowSampleType = 1 + SFlowTypeCounterSample SFlowSampleType = 2 + SFlowTypeExpandedFlowSample SFlowSampleType = 3 + SFlowTypeExpandedCounterSample SFlowSampleType = 4 +) + +func (st SFlowSampleType) GetType() SFlowSampleType { + switch st { + case SFlowTypeFlowSample: + return SFlowTypeFlowSample + case SFlowTypeCounterSample: + return SFlowTypeCounterSample + case SFlowTypeExpandedFlowSample: + return SFlowTypeExpandedFlowSample + case SFlowTypeExpandedCounterSample: + return SFlowTypeExpandedCounterSample + default: + panic("Invalid Sample Type") + } +} + +func (st SFlowSampleType) String() string { + switch st { + case SFlowTypeFlowSample: + return "Flow Sample" + case SFlowTypeCounterSample: + return "Counter Sample" + case SFlowTypeExpandedFlowSample: + return "Expanded Flow Sample" + case SFlowTypeExpandedCounterSample: + return "Expanded Counter Sample" + default: + return "" + } +} + +func (s *SFlowDatagram) LayerType() gopacket.LayerType { return LayerTypeSFlow } + +func (d *SFlowDatagram) Payload() []byte { return nil } + +func (d *SFlowDatagram) CanDecode() gopacket.LayerClass { return LayerTypeSFlow } + +func (d *SFlowDatagram) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } + +// SFlowIPType determines what form the IP address being decoded will +// take. This is an XDR union type allowing for both IPv4 and IPv6 +type SFlowIPType uint32 + +const ( + SFlowIPv4 SFlowIPType = 1 + SFlowIPv6 SFlowIPType = 2 +) + +func (s SFlowIPType) String() string { + switch s { + case SFlowIPv4: + return "IPv4" + case SFlowIPv6: + return "IPv6" + default: + return "" + } +} + +func (s SFlowIPType) Length() int { + switch s { + case SFlowIPv4: + return 4 + case SFlowIPv6: + return 16 + default: + return 0 + } +} + +func (s *SFlowDatagram) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + var agentAddressType SFlowIPType + + data, s.DatagramVersion = data[4:], binary.BigEndian.Uint32(data[:4]) + data, agentAddressType = data[4:], SFlowIPType(binary.BigEndian.Uint32(data[:4])) + data, s.AgentAddress = data[agentAddressType.Length():], data[:agentAddressType.Length()] + data, s.SubAgentID = data[4:], binary.BigEndian.Uint32(data[:4]) + data, s.SequenceNumber = data[4:], binary.BigEndian.Uint32(data[:4]) + data, s.AgentUptime = data[4:], binary.BigEndian.Uint32(data[:4]) + data, s.SampleCount = data[4:], binary.BigEndian.Uint32(data[:4]) + + if s.SampleCount < 1 { + return fmt.Errorf("SFlow Datagram has invalid sample length: %d", s.SampleCount) + } + for i := uint32(0); i < s.SampleCount; i++ { + sdf := SFlowDataFormat(binary.BigEndian.Uint32(data[:4])) + _, sampleType := sdf.decode() + switch sampleType { + case SFlowTypeFlowSample: + if flowSample, err := decodeFlowSample(&data, false); err == nil { + s.FlowSamples = append(s.FlowSamples, flowSample) + } else { + return err + } + case SFlowTypeCounterSample: + if counterSample, err := decodeCounterSample(&data, false); err == nil { + s.CounterSamples = append(s.CounterSamples, counterSample) + } else { + return err + } + case SFlowTypeExpandedFlowSample: + if flowSample, err := decodeFlowSample(&data, true); err == nil { + s.FlowSamples = append(s.FlowSamples, flowSample) + } else { + return err + } + case SFlowTypeExpandedCounterSample: + if counterSample, err := decodeCounterSample(&data, true); err == nil { + s.CounterSamples = append(s.CounterSamples, counterSample) + } else { + return err + } + + default: + return fmt.Errorf("Unsupported SFlow sample type %d", sampleType) + } + } + return nil +} + +// SFlowFlowSample represents a sampled packet and contains +// one or more records describing the packet +type SFlowFlowSample struct { + EnterpriseID SFlowEnterpriseID + Format SFlowSampleType + SampleLength uint32 + SequenceNumber uint32 + SourceIDClass SFlowSourceFormat + SourceIDIndex SFlowSourceValue + SamplingRate uint32 + SamplePool uint32 + Dropped uint32 + InputInterfaceFormat uint32 + InputInterface uint32 + OutputInterfaceFormat uint32 + OutputInterface uint32 + RecordCount uint32 + Records []SFlowRecord +} + +// Flow samples have the following structure. Note +// the bit fields to encode the Enterprise ID and the +// Flow record format: type 1 + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | sample length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// |id type | src id index value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sampling rate | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample pool | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int drops | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int input ifIndex | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int output ifIndex | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int number of records | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / flow records / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// Flow samples have the following structure. +// Flow record format: type 3 + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | sample length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int src id type | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int src id index value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sampling rate | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample pool | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int drops | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int input interface format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int input interface value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int output interface format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int output interface value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int number of records | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / flow records / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowFlowDataFormat uint32 + +func (fdf SFlowFlowDataFormat) decode() (SFlowEnterpriseID, SFlowFlowRecordType) { + leftField := fdf >> 12 + rightField := uint32(0xFFF) & uint32(fdf) + return SFlowEnterpriseID(leftField), SFlowFlowRecordType(rightField) +} + +func (fs SFlowFlowSample) GetRecords() []SFlowRecord { + return fs.Records +} + +func (fs SFlowFlowSample) GetType() SFlowSampleType { + return SFlowTypeFlowSample +} + +func skipRecord(data *[]byte) { + recordLength := int(binary.BigEndian.Uint32((*data)[4:])) + *data = (*data)[(recordLength+((4-recordLength)%4))+8:] +} + +func decodeFlowSample(data *[]byte, expanded bool) (SFlowFlowSample, error) { + s := SFlowFlowSample{} + var sdf SFlowDataFormat + *data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + var sdc SFlowDataSource + + s.EnterpriseID, s.Format = sdf.decode() + *data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if expanded { + *data, s.SourceIDClass = (*data)[4:], SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4])) + *data, s.SourceIDIndex = (*data)[4:], SFlowSourceValue(binary.BigEndian.Uint32((*data)[:4])) + } else { + *data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4])) + s.SourceIDClass, s.SourceIDIndex = sdc.decode() + } + *data, s.SamplingRate = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.SamplePool = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.Dropped = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + if expanded { + *data, s.InputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.OutputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + } else { + *data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + } + *data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + for i := uint32(0); i < s.RecordCount; i++ { + rdf := SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + _, flowRecordType := rdf.decode() + + switch flowRecordType { + case SFlowTypeRawPacketFlow: + if record, err := decodeRawPacketFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedUserFlow: + if record, err := decodeExtendedUserFlow(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedUrlFlow: + if record, err := decodeExtendedURLRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedSwitchFlow: + if record, err := decodeExtendedSwitchFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedRouterFlow: + if record, err := decodeExtendedRouterFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedGatewayFlow: + if record, err := decodeExtendedGatewayFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeEthernetFrameFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeEthernetFrameFlow") + case SFlowTypeIpv4Flow: + if record, err := decodeSFlowIpv4Record(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeIpv6Flow: + if record, err := decodeSFlowIpv6Record(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedMlpsFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsFlow") + case SFlowTypeExtendedNatFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedNatFlow") + case SFlowTypeExtendedMlpsTunnelFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsTunnelFlow") + case SFlowTypeExtendedMlpsVcFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsVcFlow") + case SFlowTypeExtendedMlpsFecFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsFecFlow") + case SFlowTypeExtendedMlpsLvpFecFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsLvpFecFlow") + case SFlowTypeExtendedVlanFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedVlanFlow") + case SFlowTypeExtendedIpv4TunnelEgressFlow: + if record, err := decodeExtendedIpv4TunnelEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedIpv4TunnelIngressFlow: + if record, err := decodeExtendedIpv4TunnelIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedIpv6TunnelEgressFlow: + if record, err := decodeExtendedIpv6TunnelEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedIpv6TunnelIngressFlow: + if record, err := decodeExtendedIpv6TunnelIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedDecapsulateEgressFlow: + if record, err := decodeExtendedDecapsulateEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedDecapsulateIngressFlow: + if record, err := decodeExtendedDecapsulateIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedVniEgressFlow: + if record, err := decodeExtendedVniEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedVniIngressFlow: + if record, err := decodeExtendedVniIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + default: + return s, fmt.Errorf("Unsupported flow record type: %d", flowRecordType) + } + } + return s, nil +} + +// Counter samples report information about various counter +// objects. Typically these are items like IfInOctets, or +// CPU / Memory stats, etc. SFlow will report these at regular +// intervals as configured on the agent. If one were sufficiently +// industrious, this could be used to replace the typical +// SNMP polling used for such things. +type SFlowCounterSample struct { + EnterpriseID SFlowEnterpriseID + Format SFlowSampleType + SampleLength uint32 + SequenceNumber uint32 + SourceIDClass SFlowSourceFormat + SourceIDIndex SFlowSourceValue + RecordCount uint32 + Records []SFlowRecord +} + +// Counter samples have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// |id type | src id index value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int number of records | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / counter records / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowCounterDataFormat uint32 + +func (cdf SFlowCounterDataFormat) decode() (SFlowEnterpriseID, SFlowCounterRecordType) { + leftField := cdf >> 12 + rightField := uint32(0xFFF) & uint32(cdf) + return SFlowEnterpriseID(leftField), SFlowCounterRecordType(rightField) +} + +// GetRecords will return a slice of interface types +// representing records. A type switch can be used to +// get at the underlying SFlowCounterRecordType. +func (cs SFlowCounterSample) GetRecords() []SFlowRecord { + return cs.Records +} + +// GetType will report the type of sample. Only the +// compact form of counter samples is supported +func (cs SFlowCounterSample) GetType() SFlowSampleType { + return SFlowTypeCounterSample +} + +type SFlowCounterRecordType uint32 + +const ( + SFlowTypeGenericInterfaceCounters SFlowCounterRecordType = 1 + SFlowTypeEthernetInterfaceCounters SFlowCounterRecordType = 2 + SFlowTypeTokenRingInterfaceCounters SFlowCounterRecordType = 3 + SFlowType100BaseVGInterfaceCounters SFlowCounterRecordType = 4 + SFlowTypeVLANCounters SFlowCounterRecordType = 5 + SFlowTypeProcessorCounters SFlowCounterRecordType = 1001 +) + +func (cr SFlowCounterRecordType) String() string { + switch cr { + case SFlowTypeGenericInterfaceCounters: + return "Generic Interface Counters" + case SFlowTypeEthernetInterfaceCounters: + return "Ethernet Interface Counters" + case SFlowTypeTokenRingInterfaceCounters: + return "Token Ring Interface Counters" + case SFlowType100BaseVGInterfaceCounters: + return "100BaseVG Interface Counters" + case SFlowTypeVLANCounters: + return "VLAN Counters" + case SFlowTypeProcessorCounters: + return "Processor Counters" + default: + return "" + + } +} + +func decodeCounterSample(data *[]byte, expanded bool) (SFlowCounterSample, error) { + s := SFlowCounterSample{} + var sdc SFlowDataSource + var sdce SFlowDataSourceExpanded + var sdf SFlowDataFormat + + *data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + s.EnterpriseID, s.Format = sdf.decode() + *data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if expanded { + *data, sdce = (*data)[8:], SFlowDataSourceExpanded{SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4])), SFlowSourceValue(binary.BigEndian.Uint32((*data)[4:8]))} + s.SourceIDClass, s.SourceIDIndex = sdce.decode() + } else { + *data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4])) + s.SourceIDClass, s.SourceIDIndex = sdc.decode() + } + *data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + for i := uint32(0); i < s.RecordCount; i++ { + cdf := SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + _, counterRecordType := cdf.decode() + switch counterRecordType { + case SFlowTypeGenericInterfaceCounters: + if record, err := decodeGenericInterfaceCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeEthernetInterfaceCounters: + if record, err := decodeEthernetCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeTokenRingInterfaceCounters: + skipRecord(data) + return s, errors.New("skipping TypeTokenRingInterfaceCounters") + case SFlowType100BaseVGInterfaceCounters: + skipRecord(data) + return s, errors.New("skipping Type100BaseVGInterfaceCounters") + case SFlowTypeVLANCounters: + skipRecord(data) + return s, errors.New("skipping TypeVLANCounters") + case SFlowTypeProcessorCounters: + if record, err := decodeProcessorCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + default: + return s, fmt.Errorf("Invalid counter record type: %d", counterRecordType) + } + } + return s, nil +} + +// SFlowBaseFlowRecord holds the fields common to all records +// of type SFlowFlowRecordType +type SFlowBaseFlowRecord struct { + EnterpriseID SFlowEnterpriseID + Format SFlowFlowRecordType + FlowDataLength uint32 +} + +func (bfr SFlowBaseFlowRecord) GetType() SFlowFlowRecordType { + return bfr.Format +} + +// SFlowFlowRecordType denotes what kind of Flow Record is +// represented. See RFC 3176 +type SFlowFlowRecordType uint32 + +const ( + SFlowTypeRawPacketFlow SFlowFlowRecordType = 1 + SFlowTypeEthernetFrameFlow SFlowFlowRecordType = 2 + SFlowTypeIpv4Flow SFlowFlowRecordType = 3 + SFlowTypeIpv6Flow SFlowFlowRecordType = 4 + SFlowTypeExtendedSwitchFlow SFlowFlowRecordType = 1001 + SFlowTypeExtendedRouterFlow SFlowFlowRecordType = 1002 + SFlowTypeExtendedGatewayFlow SFlowFlowRecordType = 1003 + SFlowTypeExtendedUserFlow SFlowFlowRecordType = 1004 + SFlowTypeExtendedUrlFlow SFlowFlowRecordType = 1005 + SFlowTypeExtendedMlpsFlow SFlowFlowRecordType = 1006 + SFlowTypeExtendedNatFlow SFlowFlowRecordType = 1007 + SFlowTypeExtendedMlpsTunnelFlow SFlowFlowRecordType = 1008 + SFlowTypeExtendedMlpsVcFlow SFlowFlowRecordType = 1009 + SFlowTypeExtendedMlpsFecFlow SFlowFlowRecordType = 1010 + SFlowTypeExtendedMlpsLvpFecFlow SFlowFlowRecordType = 1011 + SFlowTypeExtendedVlanFlow SFlowFlowRecordType = 1012 + SFlowTypeExtendedIpv4TunnelEgressFlow SFlowFlowRecordType = 1023 + SFlowTypeExtendedIpv4TunnelIngressFlow SFlowFlowRecordType = 1024 + SFlowTypeExtendedIpv6TunnelEgressFlow SFlowFlowRecordType = 1025 + SFlowTypeExtendedIpv6TunnelIngressFlow SFlowFlowRecordType = 1026 + SFlowTypeExtendedDecapsulateEgressFlow SFlowFlowRecordType = 1027 + SFlowTypeExtendedDecapsulateIngressFlow SFlowFlowRecordType = 1028 + SFlowTypeExtendedVniEgressFlow SFlowFlowRecordType = 1029 + SFlowTypeExtendedVniIngressFlow SFlowFlowRecordType = 1030 +) + +func (rt SFlowFlowRecordType) String() string { + switch rt { + case SFlowTypeRawPacketFlow: + return "Raw Packet Flow Record" + case SFlowTypeEthernetFrameFlow: + return "Ethernet Frame Flow Record" + case SFlowTypeIpv4Flow: + return "IPv4 Flow Record" + case SFlowTypeIpv6Flow: + return "IPv6 Flow Record" + case SFlowTypeExtendedSwitchFlow: + return "Extended Switch Flow Record" + case SFlowTypeExtendedRouterFlow: + return "Extended Router Flow Record" + case SFlowTypeExtendedGatewayFlow: + return "Extended Gateway Flow Record" + case SFlowTypeExtendedUserFlow: + return "Extended User Flow Record" + case SFlowTypeExtendedUrlFlow: + return "Extended URL Flow Record" + case SFlowTypeExtendedMlpsFlow: + return "Extended MPLS Flow Record" + case SFlowTypeExtendedNatFlow: + return "Extended NAT Flow Record" + case SFlowTypeExtendedMlpsTunnelFlow: + return "Extended MPLS Tunnel Flow Record" + case SFlowTypeExtendedMlpsVcFlow: + return "Extended MPLS VC Flow Record" + case SFlowTypeExtendedMlpsFecFlow: + return "Extended MPLS FEC Flow Record" + case SFlowTypeExtendedMlpsLvpFecFlow: + return "Extended MPLS LVP FEC Flow Record" + case SFlowTypeExtendedVlanFlow: + return "Extended VLAN Flow Record" + case SFlowTypeExtendedIpv4TunnelEgressFlow: + return "Extended IPv4 Tunnel Egress Record" + case SFlowTypeExtendedIpv4TunnelIngressFlow: + return "Extended IPv4 Tunnel Ingress Record" + case SFlowTypeExtendedIpv6TunnelEgressFlow: + return "Extended IPv6 Tunnel Egress Record" + case SFlowTypeExtendedIpv6TunnelIngressFlow: + return "Extended IPv6 Tunnel Ingress Record" + case SFlowTypeExtendedDecapsulateEgressFlow: + return "Extended Decapsulate Egress Record" + case SFlowTypeExtendedDecapsulateIngressFlow: + return "Extended Decapsulate Ingress Record" + case SFlowTypeExtendedVniEgressFlow: + return "Extended VNI Ingress Record" + case SFlowTypeExtendedVniIngressFlow: + return "Extended VNI Ingress Record" + default: + return "" + } +} + +// SFlowRawPacketFlowRecords hold information about a sampled +// packet grabbed as it transited the agent. This is +// perhaps the most useful and interesting record type, +// as it holds the headers of the sampled packet and +// can be used to build up a complete picture of the +// traffic patterns on a network. +// +// The raw packet header is sent back into gopacket for +// decoding, and the resulting gopackt.Packet is stored +// in the Header member +type SFlowRawPacketFlowRecord struct { + SFlowBaseFlowRecord + HeaderProtocol SFlowRawHeaderProtocol + FrameLength uint32 + PayloadRemoved uint32 + HeaderLength uint32 + Header gopacket.Packet +} + +// Raw packet record types have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Header Protocol | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Frame Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Payload Removed | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Header Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// \ Header \ +// \ \ +// \ \ +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowRawHeaderProtocol uint32 + +const ( + SFlowProtoEthernet SFlowRawHeaderProtocol = 1 + SFlowProtoISO88024 SFlowRawHeaderProtocol = 2 + SFlowProtoISO88025 SFlowRawHeaderProtocol = 3 + SFlowProtoFDDI SFlowRawHeaderProtocol = 4 + SFlowProtoFrameRelay SFlowRawHeaderProtocol = 5 + SFlowProtoX25 SFlowRawHeaderProtocol = 6 + SFlowProtoPPP SFlowRawHeaderProtocol = 7 + SFlowProtoSMDS SFlowRawHeaderProtocol = 8 + SFlowProtoAAL5 SFlowRawHeaderProtocol = 9 + SFlowProtoAAL5_IP SFlowRawHeaderProtocol = 10 /* e.g. Cisco AAL5 mux */ + SFlowProtoIPv4 SFlowRawHeaderProtocol = 11 + SFlowProtoIPv6 SFlowRawHeaderProtocol = 12 + SFlowProtoMPLS SFlowRawHeaderProtocol = 13 + SFlowProtoPOS SFlowRawHeaderProtocol = 14 /* RFC 1662, 2615 */ +) + +func (sfhp SFlowRawHeaderProtocol) String() string { + switch sfhp { + case SFlowProtoEthernet: + return "ETHERNET-ISO88023" + case SFlowProtoISO88024: + return "ISO88024-TOKENBUS" + case SFlowProtoISO88025: + return "ISO88025-TOKENRING" + case SFlowProtoFDDI: + return "FDDI" + case SFlowProtoFrameRelay: + return "FRAME-RELAY" + case SFlowProtoX25: + return "X25" + case SFlowProtoPPP: + return "PPP" + case SFlowProtoSMDS: + return "SMDS" + case SFlowProtoAAL5: + return "AAL5" + case SFlowProtoAAL5_IP: + return "AAL5-IP" + case SFlowProtoIPv4: + return "IPv4" + case SFlowProtoIPv6: + return "IPv6" + case SFlowProtoMPLS: + return "MPLS" + case SFlowProtoPOS: + return "POS" + } + return "UNKNOWN" +} + +func decodeRawPacketFlowRecord(data *[]byte) (SFlowRawPacketFlowRecord, error) { + rec := SFlowRawPacketFlowRecord{} + header := []byte{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.HeaderProtocol = (*data)[4:], SFlowRawHeaderProtocol(binary.BigEndian.Uint32((*data)[:4])) + *data, rec.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.PayloadRemoved = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.HeaderLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + headerLenWithPadding := int(rec.HeaderLength + ((4 - rec.HeaderLength) % 4)) + *data, header = (*data)[headerLenWithPadding:], (*data)[:headerLenWithPadding] + rec.Header = gopacket.NewPacket(header, LayerTypeEthernet, gopacket.Default) + return rec, nil +} + +// SFlowExtendedSwitchFlowRecord give additional information +// about the sampled packet if it's available. It's mainly +// useful for getting at the incoming and outgoing VLANs +// An agent may or may not provide this information. +type SFlowExtendedSwitchFlowRecord struct { + SFlowBaseFlowRecord + IncomingVLAN uint32 + IncomingVLANPriority uint32 + OutgoingVLAN uint32 + OutgoingVLANPriority uint32 +} + +// Extended switch records have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Incoming VLAN | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Incoming VLAN Priority | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Outgoing VLAN | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Outgoing VLAN Priority | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +func decodeExtendedSwitchFlowRecord(data *[]byte) (SFlowExtendedSwitchFlowRecord, error) { + es := SFlowExtendedSwitchFlowRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + es.EnterpriseID, es.Format = fdf.decode() + *data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.IncomingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.IncomingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.OutgoingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.OutgoingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return es, nil +} + +// SFlowExtendedRouterFlowRecord gives additional information +// about the layer 3 routing information used to forward +// the packet +type SFlowExtendedRouterFlowRecord struct { + SFlowBaseFlowRecord + NextHop net.IP + NextHopSourceMask uint32 + NextHopDestinationMask uint32 +} + +// Extended router records have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IP version of next hop router (1=v4|2=v6) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Next Hop address (v4=4byte|v6=16byte) / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Next Hop Source Mask | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Next Hop Destination Mask | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +func decodeExtendedRouterFlowRecord(data *[]byte) (SFlowExtendedRouterFlowRecord, error) { + er := SFlowExtendedRouterFlowRecord{} + var fdf SFlowFlowDataFormat + var extendedRouterAddressType SFlowIPType + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + er.EnterpriseID, er.Format = fdf.decode() + *data, er.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, extendedRouterAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4])) + *data, er.NextHop = (*data)[extendedRouterAddressType.Length():], (*data)[:extendedRouterAddressType.Length()] + *data, er.NextHopSourceMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, er.NextHopDestinationMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return er, nil +} + +// SFlowExtendedGatewayFlowRecord describes information treasured by +// nework engineers everywhere: AS path information listing which +// BGP peer sent the packet, and various other BGP related info. +// This information is vital because it gives a picture of how much +// traffic is being sent from / received by various BGP peers. + +// Extended gateway records have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IP version of next hop router (1=v4|2=v6) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Next Hop address (v4=4byte|v6=16byte) / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | AS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source AS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Peer AS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | AS Path Count | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / AS Path / Sequence / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Communities / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Local Pref | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// AS Path / Sequence: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | AS Source Type (Path=1 / Sequence=2) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Path / Sequence length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Path / Sequence Members / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// Communities: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | communitiy length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / communitiy Members / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowExtendedGatewayFlowRecord struct { + SFlowBaseFlowRecord + NextHop net.IP + AS uint32 + SourceAS uint32 + PeerAS uint32 + ASPathCount uint32 + ASPath []SFlowASDestination + Communities []uint32 + LocalPref uint32 +} + +type SFlowASPathType uint32 + +const ( + SFlowASSet SFlowASPathType = 1 + SFlowASSequence SFlowASPathType = 2 +) + +func (apt SFlowASPathType) String() string { + switch apt { + case SFlowASSet: + return "AS Set" + case SFlowASSequence: + return "AS Sequence" + default: + return "" + } +} + +type SFlowASDestination struct { + Type SFlowASPathType + Count uint32 + Members []uint32 +} + +func (asd SFlowASDestination) String() string { + switch asd.Type { + case SFlowASSet: + return fmt.Sprint("AS Set:", asd.Members) + case SFlowASSequence: + return fmt.Sprint("AS Sequence:", asd.Members) + default: + return "" + } +} + +func (ad *SFlowASDestination) decodePath(data *[]byte) { + *data, ad.Type = (*data)[4:], SFlowASPathType(binary.BigEndian.Uint32((*data)[:4])) + *data, ad.Count = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + ad.Members = make([]uint32, ad.Count) + for i := uint32(0); i < ad.Count; i++ { + var member uint32 + *data, member = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + ad.Members[i] = member + } +} + +func decodeExtendedGatewayFlowRecord(data *[]byte) (SFlowExtendedGatewayFlowRecord, error) { + eg := SFlowExtendedGatewayFlowRecord{} + var fdf SFlowFlowDataFormat + var extendedGatewayAddressType SFlowIPType + var communitiesLength uint32 + var community uint32 + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + eg.EnterpriseID, eg.Format = fdf.decode() + *data, eg.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, extendedGatewayAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4])) + *data, eg.NextHop = (*data)[extendedGatewayAddressType.Length():], (*data)[:extendedGatewayAddressType.Length()] + *data, eg.AS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eg.SourceAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eg.PeerAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eg.ASPathCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + for i := uint32(0); i < eg.ASPathCount; i++ { + asPath := SFlowASDestination{} + asPath.decodePath(data) + eg.ASPath = append(eg.ASPath, asPath) + } + *data, communitiesLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + eg.Communities = make([]uint32, communitiesLength) + for j := uint32(0); j < communitiesLength; j++ { + *data, community = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + eg.Communities[j] = community + } + *data, eg.LocalPref = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return eg, nil +} + +// ************************************************** +// Extended URL Flow Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | direction | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | URL | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Host | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowURLDirection uint32 + +const ( + SFlowURLsrc SFlowURLDirection = 1 + SFlowURLdst SFlowURLDirection = 2 +) + +func (urld SFlowURLDirection) String() string { + switch urld { + case SFlowURLsrc: + return "Source address is the server" + case SFlowURLdst: + return "Destination address is the server" + default: + return "" + } +} + +type SFlowExtendedURLRecord struct { + SFlowBaseFlowRecord + Direction SFlowURLDirection + URL string + Host string +} + +func decodeExtendedURLRecord(data *[]byte) (SFlowExtendedURLRecord, error) { + eur := SFlowExtendedURLRecord{} + var fdf SFlowFlowDataFormat + var urlLen uint32 + var urlLenWithPad int + var hostLen uint32 + var hostLenWithPad int + var urlBytes []byte + var hostBytes []byte + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + eur.EnterpriseID, eur.Format = fdf.decode() + *data, eur.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eur.Direction = (*data)[4:], SFlowURLDirection(binary.BigEndian.Uint32((*data)[:4])) + *data, urlLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + urlLenWithPad = int(urlLen + ((4 - urlLen) % 4)) + *data, urlBytes = (*data)[urlLenWithPad:], (*data)[:urlLenWithPad] + eur.URL = string(urlBytes[:urlLen]) + *data, hostLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + hostLenWithPad = int(hostLen + ((4 - hostLen) % 4)) + *data, hostBytes = (*data)[hostLenWithPad:], (*data)[:hostLenWithPad] + eur.Host = string(hostBytes[:hostLen]) + return eur, nil +} + +// ************************************************** +// Extended User Flow Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source Character Set | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source User Id | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination Character Set | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination User ID | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowExtendedUserFlow struct { + SFlowBaseFlowRecord + SourceCharSet SFlowCharSet + SourceUserID string + DestinationCharSet SFlowCharSet + DestinationUserID string +} + +type SFlowCharSet uint32 + +const ( + SFlowCSunknown SFlowCharSet = 2 + SFlowCSASCII SFlowCharSet = 3 + SFlowCSISOLatin1 SFlowCharSet = 4 + SFlowCSISOLatin2 SFlowCharSet = 5 + SFlowCSISOLatin3 SFlowCharSet = 6 + SFlowCSISOLatin4 SFlowCharSet = 7 + SFlowCSISOLatinCyrillic SFlowCharSet = 8 + SFlowCSISOLatinArabic SFlowCharSet = 9 + SFlowCSISOLatinGreek SFlowCharSet = 10 + SFlowCSISOLatinHebrew SFlowCharSet = 11 + SFlowCSISOLatin5 SFlowCharSet = 12 + SFlowCSISOLatin6 SFlowCharSet = 13 + SFlowCSISOTextComm SFlowCharSet = 14 + SFlowCSHalfWidthKatakana SFlowCharSet = 15 + SFlowCSJISEncoding SFlowCharSet = 16 + SFlowCSShiftJIS SFlowCharSet = 17 + SFlowCSEUCPkdFmtJapanese SFlowCharSet = 18 + SFlowCSEUCFixWidJapanese SFlowCharSet = 19 + SFlowCSISO4UnitedKingdom SFlowCharSet = 20 + SFlowCSISO11SwedishForNames SFlowCharSet = 21 + SFlowCSISO15Italian SFlowCharSet = 22 + SFlowCSISO17Spanish SFlowCharSet = 23 + SFlowCSISO21German SFlowCharSet = 24 + SFlowCSISO60DanishNorwegian SFlowCharSet = 25 + SFlowCSISO69French SFlowCharSet = 26 + SFlowCSISO10646UTF1 SFlowCharSet = 27 + SFlowCSISO646basic1983 SFlowCharSet = 28 + SFlowCSINVARIANT SFlowCharSet = 29 + SFlowCSISO2IntlRefVersion SFlowCharSet = 30 + SFlowCSNATSSEFI SFlowCharSet = 31 + SFlowCSNATSSEFIADD SFlowCharSet = 32 + SFlowCSNATSDANO SFlowCharSet = 33 + SFlowCSNATSDANOADD SFlowCharSet = 34 + SFlowCSISO10Swedish SFlowCharSet = 35 + SFlowCSKSC56011987 SFlowCharSet = 36 + SFlowCSISO2022KR SFlowCharSet = 37 + SFlowCSEUCKR SFlowCharSet = 38 + SFlowCSISO2022JP SFlowCharSet = 39 + SFlowCSISO2022JP2 SFlowCharSet = 40 + SFlowCSISO13JISC6220jp SFlowCharSet = 41 + SFlowCSISO14JISC6220ro SFlowCharSet = 42 + SFlowCSISO16Portuguese SFlowCharSet = 43 + SFlowCSISO18Greek7Old SFlowCharSet = 44 + SFlowCSISO19LatinGreek SFlowCharSet = 45 + SFlowCSISO25French SFlowCharSet = 46 + SFlowCSISO27LatinGreek1 SFlowCharSet = 47 + SFlowCSISO5427Cyrillic SFlowCharSet = 48 + SFlowCSISO42JISC62261978 SFlowCharSet = 49 + SFlowCSISO47BSViewdata SFlowCharSet = 50 + SFlowCSISO49INIS SFlowCharSet = 51 + SFlowCSISO50INIS8 SFlowCharSet = 52 + SFlowCSISO51INISCyrillic SFlowCharSet = 53 + SFlowCSISO54271981 SFlowCharSet = 54 + SFlowCSISO5428Greek SFlowCharSet = 55 + SFlowCSISO57GB1988 SFlowCharSet = 56 + SFlowCSISO58GB231280 SFlowCharSet = 57 + SFlowCSISO61Norwegian2 SFlowCharSet = 58 + SFlowCSISO70VideotexSupp1 SFlowCharSet = 59 + SFlowCSISO84Portuguese2 SFlowCharSet = 60 + SFlowCSISO85Spanish2 SFlowCharSet = 61 + SFlowCSISO86Hungarian SFlowCharSet = 62 + SFlowCSISO87JISX0208 SFlowCharSet = 63 + SFlowCSISO88Greek7 SFlowCharSet = 64 + SFlowCSISO89ASMO449 SFlowCharSet = 65 + SFlowCSISO90 SFlowCharSet = 66 + SFlowCSISO91JISC62291984a SFlowCharSet = 67 + SFlowCSISO92JISC62991984b SFlowCharSet = 68 + SFlowCSISO93JIS62291984badd SFlowCharSet = 69 + SFlowCSISO94JIS62291984hand SFlowCharSet = 70 + SFlowCSISO95JIS62291984handadd SFlowCharSet = 71 + SFlowCSISO96JISC62291984kana SFlowCharSet = 72 + SFlowCSISO2033 SFlowCharSet = 73 + SFlowCSISO99NAPLPS SFlowCharSet = 74 + SFlowCSISO102T617bit SFlowCharSet = 75 + SFlowCSISO103T618bit SFlowCharSet = 76 + SFlowCSISO111ECMACyrillic SFlowCharSet = 77 + SFlowCSa71 SFlowCharSet = 78 + SFlowCSa72 SFlowCharSet = 79 + SFlowCSISO123CSAZ24341985gr SFlowCharSet = 80 + SFlowCSISO88596E SFlowCharSet = 81 + SFlowCSISO88596I SFlowCharSet = 82 + SFlowCSISO128T101G2 SFlowCharSet = 83 + SFlowCSISO88598E SFlowCharSet = 84 + SFlowCSISO88598I SFlowCharSet = 85 + SFlowCSISO139CSN369103 SFlowCharSet = 86 + SFlowCSISO141JUSIB1002 SFlowCharSet = 87 + SFlowCSISO143IECP271 SFlowCharSet = 88 + SFlowCSISO146Serbian SFlowCharSet = 89 + SFlowCSISO147Macedonian SFlowCharSet = 90 + SFlowCSISO150 SFlowCharSet = 91 + SFlowCSISO151Cuba SFlowCharSet = 92 + SFlowCSISO6937Add SFlowCharSet = 93 + SFlowCSISO153GOST1976874 SFlowCharSet = 94 + SFlowCSISO8859Supp SFlowCharSet = 95 + SFlowCSISO10367Box SFlowCharSet = 96 + SFlowCSISO158Lap SFlowCharSet = 97 + SFlowCSISO159JISX02121990 SFlowCharSet = 98 + SFlowCSISO646Danish SFlowCharSet = 99 + SFlowCSUSDK SFlowCharSet = 100 + SFlowCSDKUS SFlowCharSet = 101 + SFlowCSKSC5636 SFlowCharSet = 102 + SFlowCSUnicode11UTF7 SFlowCharSet = 103 + SFlowCSISO2022CN SFlowCharSet = 104 + SFlowCSISO2022CNEXT SFlowCharSet = 105 + SFlowCSUTF8 SFlowCharSet = 106 + SFlowCSISO885913 SFlowCharSet = 109 + SFlowCSISO885914 SFlowCharSet = 110 + SFlowCSISO885915 SFlowCharSet = 111 + SFlowCSISO885916 SFlowCharSet = 112 + SFlowCSGBK SFlowCharSet = 113 + SFlowCSGB18030 SFlowCharSet = 114 + SFlowCSOSDEBCDICDF0415 SFlowCharSet = 115 + SFlowCSOSDEBCDICDF03IRV SFlowCharSet = 116 + SFlowCSOSDEBCDICDF041 SFlowCharSet = 117 + SFlowCSISO115481 SFlowCharSet = 118 + SFlowCSKZ1048 SFlowCharSet = 119 + SFlowCSUnicode SFlowCharSet = 1000 + SFlowCSUCS4 SFlowCharSet = 1001 + SFlowCSUnicodeASCII SFlowCharSet = 1002 + SFlowCSUnicodeLatin1 SFlowCharSet = 1003 + SFlowCSUnicodeJapanese SFlowCharSet = 1004 + SFlowCSUnicodeIBM1261 SFlowCharSet = 1005 + SFlowCSUnicodeIBM1268 SFlowCharSet = 1006 + SFlowCSUnicodeIBM1276 SFlowCharSet = 1007 + SFlowCSUnicodeIBM1264 SFlowCharSet = 1008 + SFlowCSUnicodeIBM1265 SFlowCharSet = 1009 + SFlowCSUnicode11 SFlowCharSet = 1010 + SFlowCSSCSU SFlowCharSet = 1011 + SFlowCSUTF7 SFlowCharSet = 1012 + SFlowCSUTF16BE SFlowCharSet = 1013 + SFlowCSUTF16LE SFlowCharSet = 1014 + SFlowCSUTF16 SFlowCharSet = 1015 + SFlowCSCESU8 SFlowCharSet = 1016 + SFlowCSUTF32 SFlowCharSet = 1017 + SFlowCSUTF32BE SFlowCharSet = 1018 + SFlowCSUTF32LE SFlowCharSet = 1019 + SFlowCSBOCU1 SFlowCharSet = 1020 + SFlowCSWindows30Latin1 SFlowCharSet = 2000 + SFlowCSWindows31Latin1 SFlowCharSet = 2001 + SFlowCSWindows31Latin2 SFlowCharSet = 2002 + SFlowCSWindows31Latin5 SFlowCharSet = 2003 + SFlowCSHPRoman8 SFlowCharSet = 2004 + SFlowCSAdobeStandardEncoding SFlowCharSet = 2005 + SFlowCSVenturaUS SFlowCharSet = 2006 + SFlowCSVenturaInternational SFlowCharSet = 2007 + SFlowCSDECMCS SFlowCharSet = 2008 + SFlowCSPC850Multilingual SFlowCharSet = 2009 + SFlowCSPCp852 SFlowCharSet = 2010 + SFlowCSPC8CodePage437 SFlowCharSet = 2011 + SFlowCSPC8DanishNorwegian SFlowCharSet = 2012 + SFlowCSPC862LatinHebrew SFlowCharSet = 2013 + SFlowCSPC8Turkish SFlowCharSet = 2014 + SFlowCSIBMSymbols SFlowCharSet = 2015 + SFlowCSIBMThai SFlowCharSet = 2016 + SFlowCSHPLegal SFlowCharSet = 2017 + SFlowCSHPPiFont SFlowCharSet = 2018 + SFlowCSHPMath8 SFlowCharSet = 2019 + SFlowCSHPPSMath SFlowCharSet = 2020 + SFlowCSHPDesktop SFlowCharSet = 2021 + SFlowCSVenturaMath SFlowCharSet = 2022 + SFlowCSMicrosoftPublishing SFlowCharSet = 2023 + SFlowCSWindows31J SFlowCharSet = 2024 + SFlowCSGB2312 SFlowCharSet = 2025 + SFlowCSBig5 SFlowCharSet = 2026 + SFlowCSMacintosh SFlowCharSet = 2027 + SFlowCSIBM037 SFlowCharSet = 2028 + SFlowCSIBM038 SFlowCharSet = 2029 + SFlowCSIBM273 SFlowCharSet = 2030 + SFlowCSIBM274 SFlowCharSet = 2031 + SFlowCSIBM275 SFlowCharSet = 2032 + SFlowCSIBM277 SFlowCharSet = 2033 + SFlowCSIBM278 SFlowCharSet = 2034 + SFlowCSIBM280 SFlowCharSet = 2035 + SFlowCSIBM281 SFlowCharSet = 2036 + SFlowCSIBM284 SFlowCharSet = 2037 + SFlowCSIBM285 SFlowCharSet = 2038 + SFlowCSIBM290 SFlowCharSet = 2039 + SFlowCSIBM297 SFlowCharSet = 2040 + SFlowCSIBM420 SFlowCharSet = 2041 + SFlowCSIBM423 SFlowCharSet = 2042 + SFlowCSIBM424 SFlowCharSet = 2043 + SFlowCSIBM500 SFlowCharSet = 2044 + SFlowCSIBM851 SFlowCharSet = 2045 + SFlowCSIBM855 SFlowCharSet = 2046 + SFlowCSIBM857 SFlowCharSet = 2047 + SFlowCSIBM860 SFlowCharSet = 2048 + SFlowCSIBM861 SFlowCharSet = 2049 + SFlowCSIBM863 SFlowCharSet = 2050 + SFlowCSIBM864 SFlowCharSet = 2051 + SFlowCSIBM865 SFlowCharSet = 2052 + SFlowCSIBM868 SFlowCharSet = 2053 + SFlowCSIBM869 SFlowCharSet = 2054 + SFlowCSIBM870 SFlowCharSet = 2055 + SFlowCSIBM871 SFlowCharSet = 2056 + SFlowCSIBM880 SFlowCharSet = 2057 + SFlowCSIBM891 SFlowCharSet = 2058 + SFlowCSIBM903 SFlowCharSet = 2059 + SFlowCSIBBM904 SFlowCharSet = 2060 + SFlowCSIBM905 SFlowCharSet = 2061 + SFlowCSIBM918 SFlowCharSet = 2062 + SFlowCSIBM1026 SFlowCharSet = 2063 + SFlowCSIBMEBCDICATDE SFlowCharSet = 2064 + SFlowCSEBCDICATDEA SFlowCharSet = 2065 + SFlowCSEBCDICCAFR SFlowCharSet = 2066 + SFlowCSEBCDICDKNO SFlowCharSet = 2067 + SFlowCSEBCDICDKNOA SFlowCharSet = 2068 + SFlowCSEBCDICFISE SFlowCharSet = 2069 + SFlowCSEBCDICFISEA SFlowCharSet = 2070 + SFlowCSEBCDICFR SFlowCharSet = 2071 + SFlowCSEBCDICIT SFlowCharSet = 2072 + SFlowCSEBCDICPT SFlowCharSet = 2073 + SFlowCSEBCDICES SFlowCharSet = 2074 + SFlowCSEBCDICESA SFlowCharSet = 2075 + SFlowCSEBCDICESS SFlowCharSet = 2076 + SFlowCSEBCDICUK SFlowCharSet = 2077 + SFlowCSEBCDICUS SFlowCharSet = 2078 + SFlowCSUnknown8BiT SFlowCharSet = 2079 + SFlowCSMnemonic SFlowCharSet = 2080 + SFlowCSMnem SFlowCharSet = 2081 + SFlowCSVISCII SFlowCharSet = 2082 + SFlowCSVIQR SFlowCharSet = 2083 + SFlowCSKOI8R SFlowCharSet = 2084 + SFlowCSHZGB2312 SFlowCharSet = 2085 + SFlowCSIBM866 SFlowCharSet = 2086 + SFlowCSPC775Baltic SFlowCharSet = 2087 + SFlowCSKOI8U SFlowCharSet = 2088 + SFlowCSIBM00858 SFlowCharSet = 2089 + SFlowCSIBM00924 SFlowCharSet = 2090 + SFlowCSIBM01140 SFlowCharSet = 2091 + SFlowCSIBM01141 SFlowCharSet = 2092 + SFlowCSIBM01142 SFlowCharSet = 2093 + SFlowCSIBM01143 SFlowCharSet = 2094 + SFlowCSIBM01144 SFlowCharSet = 2095 + SFlowCSIBM01145 SFlowCharSet = 2096 + SFlowCSIBM01146 SFlowCharSet = 2097 + SFlowCSIBM01147 SFlowCharSet = 2098 + SFlowCSIBM01148 SFlowCharSet = 2099 + SFlowCSIBM01149 SFlowCharSet = 2100 + SFlowCSBig5HKSCS SFlowCharSet = 2101 + SFlowCSIBM1047 SFlowCharSet = 2102 + SFlowCSPTCP154 SFlowCharSet = 2103 + SFlowCSAmiga1251 SFlowCharSet = 2104 + SFlowCSKOI7switched SFlowCharSet = 2105 + SFlowCSBRF SFlowCharSet = 2106 + SFlowCSTSCII SFlowCharSet = 2107 + SFlowCSCP51932 SFlowCharSet = 2108 + SFlowCSWindows874 SFlowCharSet = 2109 + SFlowCSWindows1250 SFlowCharSet = 2250 + SFlowCSWindows1251 SFlowCharSet = 2251 + SFlowCSWindows1252 SFlowCharSet = 2252 + SFlowCSWindows1253 SFlowCharSet = 2253 + SFlowCSWindows1254 SFlowCharSet = 2254 + SFlowCSWindows1255 SFlowCharSet = 2255 + SFlowCSWindows1256 SFlowCharSet = 2256 + SFlowCSWindows1257 SFlowCharSet = 2257 + SFlowCSWindows1258 SFlowCharSet = 2258 + SFlowCSTIS620 SFlowCharSet = 2259 + SFlowCS50220 SFlowCharSet = 2260 + SFlowCSreserved SFlowCharSet = 3000 +) + +func decodeExtendedUserFlow(data *[]byte) (SFlowExtendedUserFlow, error) { + eu := SFlowExtendedUserFlow{} + var fdf SFlowFlowDataFormat + var srcUserLen uint32 + var srcUserLenWithPad int + var srcUserBytes []byte + var dstUserLen uint32 + var dstUserLenWithPad int + var dstUserBytes []byte + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + eu.EnterpriseID, eu.Format = fdf.decode() + *data, eu.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eu.SourceCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4])) + *data, srcUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + srcUserLenWithPad = int(srcUserLen + ((4 - srcUserLen) % 4)) + *data, srcUserBytes = (*data)[srcUserLenWithPad:], (*data)[:srcUserLenWithPad] + eu.SourceUserID = string(srcUserBytes[:srcUserLen]) + *data, eu.DestinationCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4])) + *data, dstUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + dstUserLenWithPad = int(dstUserLen + ((4 - dstUserLen) % 4)) + *data, dstUserBytes = (*data)[dstUserLenWithPad:], (*data)[:dstUserLenWithPad] + eu.DestinationUserID = string(dstUserBytes[:dstUserLen]) + return eu, nil +} + +// ************************************************** +// Packet IP version 4 Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Protocol | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destionation Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TCP Flags | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TOS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowIpv4Record struct { + // The length of the IP packet excluding ower layer encapsulations + Length uint32 + // IP Protocol type (for example, TCP = 6, UDP = 17) + Protocol uint32 + // Source IP Address + IPSrc net.IP + // Destination IP Address + IPDst net.IP + // TCP/UDP source port number or equivalent + PortSrc uint32 + // TCP/UDP destination port number or equivalent + PortDst uint32 + // TCP flags + TCPFlags uint32 + // IP type of service + TOS uint32 +} + +func decodeSFlowIpv4Record(data *[]byte) (SFlowIpv4Record, error) { + si := SFlowIpv4Record{} + + *data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.IPSrc = (*data)[4:], net.IP((*data)[:4]) + *data, si.IPDst = (*data)[4:], net.IP((*data)[:4]) + *data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.TOS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return si, nil +} + +// ************************************************** +// Packet IP version 6 Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Protocol | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destionation Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TCP Flags | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Priority | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowIpv6Record struct { + // The length of the IP packet excluding ower layer encapsulations + Length uint32 + // IP Protocol type (for example, TCP = 6, UDP = 17) + Protocol uint32 + // Source IP Address + IPSrc net.IP + // Destination IP Address + IPDst net.IP + // TCP/UDP source port number or equivalent + PortSrc uint32 + // TCP/UDP destination port number or equivalent + PortDst uint32 + // TCP flags + TCPFlags uint32 + // IP priority + Priority uint32 +} + +func decodeSFlowIpv6Record(data *[]byte) (SFlowIpv6Record, error) { + si := SFlowIpv6Record{} + + *data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.IPSrc = (*data)[16:], net.IP((*data)[:16]) + *data, si.IPDst = (*data)[16:], net.IP((*data)[:16]) + *data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.Priority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return si, nil +} + +// ************************************************** +// Extended IPv4 Tunnel Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 4 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv4TunnelEgressRecord struct { + SFlowBaseFlowRecord + SFlowIpv4Record SFlowIpv4Record +} + +func decodeExtendedIpv4TunnelEgress(data *[]byte) (SFlowExtendedIpv4TunnelEgressRecord, error) { + rec := SFlowExtendedIpv4TunnelEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data) + + return rec, nil +} + +// ************************************************** +// Extended IPv4 Tunnel Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 4 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv4TunnelIngressRecord struct { + SFlowBaseFlowRecord + SFlowIpv4Record SFlowIpv4Record +} + +func decodeExtendedIpv4TunnelIngress(data *[]byte) (SFlowExtendedIpv4TunnelIngressRecord, error) { + rec := SFlowExtendedIpv4TunnelIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data) + + return rec, nil +} + +// ************************************************** +// Extended IPv6 Tunnel Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 6 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv6TunnelEgressRecord struct { + SFlowBaseFlowRecord + SFlowIpv6Record +} + +func decodeExtendedIpv6TunnelEgress(data *[]byte) (SFlowExtendedIpv6TunnelEgressRecord, error) { + rec := SFlowExtendedIpv6TunnelEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data) + + return rec, nil +} + +// ************************************************** +// Extended IPv6 Tunnel Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 6 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv6TunnelIngressRecord struct { + SFlowBaseFlowRecord + SFlowIpv6Record +} + +func decodeExtendedIpv6TunnelIngress(data *[]byte) (SFlowExtendedIpv6TunnelIngressRecord, error) { + rec := SFlowExtendedIpv6TunnelIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data) + + return rec, nil +} + +// ************************************************** +// Extended Decapsulate Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Inner Header Offset | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedDecapsulateEgressRecord struct { + SFlowBaseFlowRecord + InnerHeaderOffset uint32 +} + +func decodeExtendedDecapsulateEgress(data *[]byte) (SFlowExtendedDecapsulateEgressRecord, error) { + rec := SFlowExtendedDecapsulateEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Extended Decapsulate Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Inner Header Offset | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedDecapsulateIngressRecord struct { + SFlowBaseFlowRecord + InnerHeaderOffset uint32 +} + +func decodeExtendedDecapsulateIngress(data *[]byte) (SFlowExtendedDecapsulateIngressRecord, error) { + rec := SFlowExtendedDecapsulateIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Extended VNI Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | VNI | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedVniEgressRecord struct { + SFlowBaseFlowRecord + VNI uint32 +} + +func decodeExtendedVniEgress(data *[]byte) (SFlowExtendedVniEgressRecord, error) { + rec := SFlowExtendedVniEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Extended VNI Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | VNI | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedVniIngressRecord struct { + SFlowBaseFlowRecord + VNI uint32 +} + +func decodeExtendedVniIngress(data *[]byte) (SFlowExtendedVniIngressRecord, error) { + rec := SFlowExtendedVniIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Counter Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / counter data / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowBaseCounterRecord struct { + EnterpriseID SFlowEnterpriseID + Format SFlowCounterRecordType + FlowDataLength uint32 +} + +func (bcr SFlowBaseCounterRecord) GetType() SFlowCounterRecordType { + switch bcr.Format { + case SFlowTypeGenericInterfaceCounters: + return SFlowTypeGenericInterfaceCounters + case SFlowTypeEthernetInterfaceCounters: + return SFlowTypeEthernetInterfaceCounters + case SFlowTypeTokenRingInterfaceCounters: + return SFlowTypeTokenRingInterfaceCounters + case SFlowType100BaseVGInterfaceCounters: + return SFlowType100BaseVGInterfaceCounters + case SFlowTypeVLANCounters: + return SFlowTypeVLANCounters + case SFlowTypeProcessorCounters: + return SFlowTypeProcessorCounters + + } + unrecognized := fmt.Sprint("Unrecognized counter record type:", bcr.Format) + panic(unrecognized) +} + +// ************************************************** +// Counter Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfIndex | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfType | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfSpeed | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfDirection | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfStatus | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IFInOctets | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInUcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInMulticastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInBroadcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInDiscards | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | InInErrors | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInUnknownProtos | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutOctets | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutUcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutMulticastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutBroadcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutDiscards | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOUtErrors | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfPromiscouousMode | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowGenericInterfaceCounters struct { + SFlowBaseCounterRecord + IfIndex uint32 + IfType uint32 + IfSpeed uint64 + IfDirection uint32 + IfStatus uint32 + IfInOctets uint64 + IfInUcastPkts uint32 + IfInMulticastPkts uint32 + IfInBroadcastPkts uint32 + IfInDiscards uint32 + IfInErrors uint32 + IfInUnknownProtos uint32 + IfOutOctets uint64 + IfOutUcastPkts uint32 + IfOutMulticastPkts uint32 + IfOutBroadcastPkts uint32 + IfOutDiscards uint32 + IfOutErrors uint32 + IfPromiscuousMode uint32 +} + +func decodeGenericInterfaceCounters(data *[]byte) (SFlowGenericInterfaceCounters, error) { + gic := SFlowGenericInterfaceCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + gic.EnterpriseID, gic.Format = cdf.decode() + *data, gic.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfIndex = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfType = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfSpeed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, gic.IfDirection = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfStatus = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, gic.IfInUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInUnknownProtos = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, gic.IfOutUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfPromiscuousMode = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return gic, nil +} + +// ************************************************** +// Counter Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / counter data / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowEthernetCounters struct { + SFlowBaseCounterRecord + AlignmentErrors uint32 + FCSErrors uint32 + SingleCollisionFrames uint32 + MultipleCollisionFrames uint32 + SQETestErrors uint32 + DeferredTransmissions uint32 + LateCollisions uint32 + ExcessiveCollisions uint32 + InternalMacTransmitErrors uint32 + CarrierSenseErrors uint32 + FrameTooLongs uint32 + InternalMacReceiveErrors uint32 + SymbolErrors uint32 +} + +func decodeEthernetCounters(data *[]byte) (SFlowEthernetCounters, error) { + ec := SFlowEthernetCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + ec.EnterpriseID, ec.Format = cdf.decode() + *data, ec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.AlignmentErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.FCSErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.SingleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.MultipleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.SQETestErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.DeferredTransmissions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.LateCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.ExcessiveCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.InternalMacTransmitErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.CarrierSenseErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.FrameTooLongs = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.InternalMacReceiveErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ec.SymbolErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return ec, nil +} + +// ************************************************** +// Processor Counter Record +// ************************************************** +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | FiveSecCpu | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | OneMinCpu | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | GiveMinCpu | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TotalMemory | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | FreeMemory | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowProcessorCounters struct { + SFlowBaseCounterRecord + FiveSecCpu uint32 // 5 second average CPU utilization + OneMinCpu uint32 // 1 minute average CPU utilization + FiveMinCpu uint32 // 5 minute average CPU utilization + TotalMemory uint64 // total memory (in bytes) + FreeMemory uint64 // free memory (in bytes) +} + +func decodeProcessorCounters(data *[]byte) (SFlowProcessorCounters, error) { + pc := SFlowProcessorCounters{} + var cdf SFlowCounterDataFormat + var high32, low32 uint32 + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + pc.EnterpriseID, pc.Format = cdf.decode() + *data, pc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + *data, pc.FiveSecCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, pc.OneMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, pc.FiveMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + pc.TotalMemory = (uint64(high32) << 32) + uint64(low32) + *data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + pc.FreeMemory = (uint64(high32)) + uint64(low32) + + return pc, nil +} diff --git a/vendor/github.com/google/gopacket/layers/sflow_test.go b/vendor/github.com/google/gopacket/layers/sflow_test.go new file mode 100644 index 00000000..ca0f2abd --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/sflow_test.go @@ -0,0 +1,1246 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +package layers + +import ( + "github.com/google/gopacket" + "net" + "reflect" + "testing" +) + +// Test packet collected from live network. See the test below for contents +var SFlowTestPacket1 = []byte{ + 0x84, 0x2b, 0x2b, 0x16, 0x8b, 0x62, 0xf0, 0x50, 0x56, 0x85, 0x3a, 0xfd, 0x08, 0x00, 0x45, 0x00, + 0x05, 0xbc, 0x9c, 0x04, 0x40, 0x00, 0xff, 0x11, 0xc7, 0x00, 0x0a, 0x01, 0xff, 0x0e, 0x0a, 0x01, + 0x00, 0x1b, 0xc7, 0x57, 0x18, 0xc7, 0x05, 0xa8, 0x22, 0x3b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x01, 0x0a, 0x01, 0xf8, 0x16, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x9d, 0xfb, 0x40, 0x49, + 0xc6, 0xcd, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x26, + 0x27, 0xe8, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x3e, 0x80, 0x50, 0xbd, 0xe5, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xd2, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x3c, 0x8a, 0xb0, 0xe7, 0x54, 0x41, 0xb8, 0xca, 0x3a, 0x6d, + 0xf0, 0x40, 0x08, 0x00, 0x45, 0x00, 0x05, 0xc0, 0x6b, 0xaa, 0x40, 0x00, 0x40, 0x06, 0x8f, 0x41, + 0x0a, 0x01, 0x0e, 0x16, 0x36, 0xf0, 0xeb, 0x45, 0x76, 0xfd, 0x00, 0x50, 0xca, 0x77, 0xef, 0x96, + 0xfc, 0x28, 0x63, 0x40, 0x50, 0x10, 0x00, 0x3c, 0x64, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0xf4, 0x00, 0x00, 0x02, 0x77, 0x00, 0x00, 0x00, 0xfd, 0x3b, 0x8c, 0xe7, 0x04, 0x4a, 0x2d, 0xb2, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x01, 0x48, 0xcc, 0x11, 0x0d, 0xe3, 0x00, + 0x26, 0x85, 0x30, 0x00, 0x00, 0x07, 0x66, 0x00, 0x02, 0xd0, 0x8a, 0x00, 0x02, 0xce, 0xf0, 0x00, + 0x29, 0x7e, 0x80, 0x00, 0x02, 0xd0, 0x98, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x26, 0x85, 0x30, 0x00, + 0x00, 0x00, 0xf4, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x5e, 0x5c, 0x1e, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, + 0x07, 0xd0, 0xb1, 0x2f, 0xa2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x05, 0xee, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x3c, 0x8a, + 0xb0, 0xe7, 0x54, 0x41, 0xb8, 0xca, 0x3a, 0x6f, 0xbe, 0xd8, 0x08, 0x00, 0x45, 0x00, 0x05, 0xdc, + 0x9f, 0xfd, 0x40, 0x00, 0x40, 0x06, 0x6a, 0xfa, 0x0a, 0x01, 0x0e, 0x10, 0x0a, 0x01, 0x08, 0x13, + 0x23, 0x84, 0xb7, 0x22, 0x8a, 0xc9, 0x50, 0xb5, 0x4e, 0x10, 0x2a, 0x87, 0x80, 0x10, 0x06, 0x01, + 0x10, 0xa6, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xef, 0x1f, 0xf4, 0x07, 0x99, 0x3a, 0xd8, 0x5b, + 0x01, 0x46, 0x09, 0x00, 0x0c, 0x00, 0x0c, 0x3c, 0xac, 0x4a, 0x1b, 0x06, 0x04, 0x78, 0x78, 0x4e, + 0xc2, 0x05, 0x46, 0x43, 0x06, 0x04, 0x78, 0x78, 0xee, 0x9c, 0x00, 0x41, 0xef, 0x05, 0x81, 0x32, + 0x1b, 0x06, 0x04, 0x78, 0x78, 0x56, 0x72, 0x05, 0x4e, 0x92, 0x00, 0x96, 0x39, 0x00, 0xea, 0x3f, + 0x01, 0x15, 0xa3, 0x08, 0x04, 0x42, 0x6a, 0x82, 0x87, 0x08, 0x05, 0xcc, 0x00, 0x04, 0x00, 0x00, + 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x5a, + 0xcd, 0xd0, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x07, 0xd0, 0x95, 0x67, 0xe1, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x46, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x3c, 0x8a, 0xb0, 0xe7, 0x54, 0x41, 0xb8, 0xca, 0x3a, 0x6f, + 0x11, 0x28, 0x08, 0x00, 0x45, 0x00, 0x04, 0x34, 0xdb, 0x36, 0x40, 0x00, 0x40, 0x06, 0x38, 0xac, + 0x0a, 0x01, 0x0e, 0x11, 0x0a, 0x01, 0x00, 0xcf, 0x23, 0x84, 0xa0, 0x3f, 0x3c, 0xce, 0xd5, 0x4a, + 0x72, 0x0b, 0x5d, 0x1a, 0x80, 0x10, 0x06, 0x01, 0x8a, 0x50, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, + 0xef, 0x1f, 0xa2, 0xba, 0xe6, 0xfa, 0xae, 0xb3, 0xfe, 0xcf, 0x00, 0x19, 0xcf, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xb9, 0x79, 0xdd, 0x42, 0x00, 0x00, 0x02, 0x84, 0x9b, 0xa9, 0x02, 0xe2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x32, 0x39, 0x35, 0x34, 0x33, 0x36, 0x00, 0x00, 0x02, 0x70, 0xcd, + 0x16, 0x40, 0xa6, 0x98, 0x88, 0x24, 0x06, 0x50, 0xb0, 0xf4, 0xee, 0x03, 0xa6, 0xfa, 0x87, 0xaf, + 0xc1, 0x99, 0x52, 0x0d, 0x07, 0xa8, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x20, 0xf2, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x54, 0x0b, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x01, 0x29, 0x82, 0x6d, 0xb0, 0x6c, 0x0b, 0xcb, 0x0d, 0xdd, 0x96, 0x00, 0x06, + 0xa8, 0xc6, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x02, 0x35, 0x58, 0x7c, 0x9e, 0x56, 0x64, 0x25, 0x71, 0x00, 0x70, + 0x5a, 0xc4, 0x00, 0x09, 0x08, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x5e, 0x5c, 0x1f, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, + 0x07, 0xd0, 0xb1, 0x2f, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x05, 0xee, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x3c, 0x8a, + 0xb0, 0xe7, 0x54, 0x41, 0xb8, 0xca, 0x3a, 0x6f, 0xbe, 0xd8, 0x08, 0x00, 0x45, 0x00, 0x05, 0xdc, + 0x0f, 0xba, 0x40, 0x00, 0x40, 0x06, 0xf4, 0x3f, 0x0a, 0x01, 0x0e, 0x10, 0x0a, 0x01, 0x0f, 0x11, + 0x23, 0x84, 0xcd, 0xc0, 0xf4, 0x0e, 0x90, 0x23, 0xd7, 0x32, 0x8b, 0x31, 0x80, 0x10, 0x00, 0x1d, + 0x6b, 0x12, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xef, 0x1f, 0xf4, 0x28, 0xef, 0x1f, 0xec, 0x76, + 0xaa, 0x25, 0x01, 0x04, 0xc0, 0xac, 0xfe, 0x25, 0x01, 0x8e, 0x25, 0x01, 0x16, 0xc7, 0x28, 0xfe, + 0x7e, 0x70, 0xfe, 0x7e, 0x70, 0x52, 0x7e, 0x70, 0x15, 0x9b, 0xfe, 0x35, 0x01, 0xfe, 0x35, 0x01, + 0x42, 0x35, 0x01, 0xfe, 0x95, 0x77, 0xfe, 0x95, 0x77, 0xfe, 0x95, 0x77, 0x52, 0x95, 0x77, 0x00, + 0xd2, 0xfe, 0x70, 0x02, 0x92, 0x70, 0x02, 0x16, 0x60, 0x22, 0x00, 0x7e, 0xb2, 0x15, 0x00, 0x00, + 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x5a, + 0xcd, 0xd1, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x07, 0xd0, 0x95, 0x67, 0xe9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x55, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0xee, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0xb8, 0xca, 0x3a, 0x6f, 0xbe, 0xd8, 0xb8, 0xca, 0x3a, 0x6f, + 0x11, 0x28, 0x08, 0x00, 0x45, 0x00, 0x05, 0xdc, 0xfe, 0x05, 0x40, 0x00, 0x40, 0x06, 0x06, 0xf4, + 0x0a, 0x01, 0x0e, 0x11, 0x0a, 0x01, 0x0e, 0x10, 0x23, 0x84, 0xfa, 0x29, 0xae, 0xd4, 0x95, 0x03, + 0x99, 0xb8, 0x77, 0xd0, 0x80, 0x10, 0x00, 0x1d, 0x6f, 0x4f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, + 0xef, 0x1f, 0xa2, 0xcc, 0xef, 0x1f, 0xf4, 0x2c, 0xfe, 0xdb, 0x05, 0xa1, 0xdb, 0x04, 0x9e, 0xc0, + 0xfe, 0x30, 0x08, 0xb2, 0x30, 0x08, 0xda, 0x2b, 0xbd, 0xfe, 0x2a, 0x01, 0xfe, 0x2a, 0x01, 0x21, + 0x2a, 0x00, 0xb2, 0xfe, 0x57, 0xb0, 0xb6, 0x57, 0xb0, 0x14, 0x74, 0xf4, 0xf0, 0x4c, 0x05, 0x68, + 0xfe, 0x54, 0x02, 0xfe, 0x54, 0x02, 0xd2, 0x54, 0x02, 0x00, 0xbe, 0xfe, 0x32, 0x0f, 0xb6, 0x32, + 0x0f, 0x14, 0x2e, 0x16, 0xaf, 0x47, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x94, 0x01, 0x5e, 0x5c, 0x20, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, + 0x07, 0xd0, 0xb1, 0x2f, 0xb2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x57, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x8a, + 0xb0, 0xe7, 0x54, 0x41, 0xb8, 0xca, 0x3a, 0x6f, 0xbe, 0xd8, 0x08, 0x00, 0x45, 0x00, 0x00, 0x34, + 0xa8, 0x23, 0x40, 0x00, 0x40, 0x06, 0x61, 0x7f, 0x0a, 0x01, 0x0e, 0x10, 0x0a, 0x01, 0x0f, 0x10, + 0x97, 0x91, 0x23, 0x84, 0x24, 0xfa, 0x91, 0xf7, 0xb4, 0xe8, 0xf3, 0x2d, 0x80, 0x10, 0x00, 0xab, + 0x7b, 0x7d, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xef, 0x1f, 0xf4, 0x36, 0xef, 0x1f, 0xdc, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +// Test collected from the SFlow reference agent. Contains dummy data for several record types +// that wern't available on an actual network for sampling. +var SFlowTestPacket2 = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, + 0x04, 0x88, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, 0x38, 0x63, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, + 0x00, 0x01, 0xdc, 0xb8, 0x18, 0xc7, 0x04, 0x74, 0x02, 0x88, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x01, 0xc0, 0xa8, 0x5b, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x3a, 0x00, 0x00, + 0xcb, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x54, 0x00, 0x02, + 0x1f, 0x6e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x1f, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x03, 0xed, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6f, + 0x72, 0x67, 0x00, 0x00, 0x00, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x31, 0x2e, 0x73, 0x66, 0x6c, 0x6f, + 0x77, 0x2e, 0x6f, 0x72, 0x67, 0x06, 0x00, 0x00, 0x03, 0xec, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x75, 0x73, 0x65, + 0x72, 0xdc, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72, 0x00, 0x00, 0x03, 0xeb, 0x00, 0x00, + 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x0d, 0x0c, 0x0b, 0x0a, 0x00, 0x00, 0xfd, 0xe9, 0x00, 0x00, + 0x00, 0x7b, 0x00, 0x00, 0x03, 0xe7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x03, 0x15, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x2b, 0x67, 0x00, 0x00, 0x56, 0xce, 0x00, 0x00, + 0x82, 0x35, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, + 0x03, 0x78, 0x00, 0x00, 0x03, 0xe7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0c, + 0x29, 0x67, 0xa0, 0xe5, 0x00, 0x50, 0x56, 0xc0, 0x00, 0x09, 0x08, 0x00, 0x45, 0x10, 0x00, 0x34, + 0x92, 0xc3, 0x40, 0x00, 0x40, 0x06, 0x70, 0x8d, 0xc0, 0xa8, 0x5b, 0x01, 0xc0, 0xa8, 0x5b, 0x11, + 0xd3, 0xdd, 0x00, 0x16, 0xe3, 0x2e, 0x84, 0x77, 0x13, 0x6d, 0xc5, 0x53, 0x80, 0x10, 0x1f, 0xf7, + 0xe7, 0x7d, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x2e, 0xc6, 0x70, 0x3a, 0x00, 0x0f, 0x84, 0x7a, + 0xbc, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x90, 0x00, 0x02, 0x1f, 0x6f, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x1f, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0xed, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x00, + 0x00, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x31, 0x2e, 0x73, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6f, 0x72, + 0x67, 0x03, 0x00, 0x00, 0x03, 0xec, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, + 0x00, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x77, 0x00, 0x00, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72, 0x00, 0x00, 0x03, 0xeb, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x00, 0x01, 0x0d, 0x0c, 0x0b, 0x0a, 0x00, 0x00, 0xfd, 0xe9, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, + 0x03, 0xe7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x7b, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x03, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x2b, 0x67, 0x00, 0x00, 0x56, 0xce, 0x00, 0x00, 0x82, 0x35, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x03, 0x78, 0x00, 0x00, + 0x03, 0xe7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x01, 0xb0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x86, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x50, 0x56, 0xc0, 0x00, 0x09, + 0x00, 0x0c, 0x29, 0x67, 0xa0, 0xe5, 0x08, 0x00, 0x45, 0x10, 0x01, 0x74, 0xbb, 0xfa, 0x40, 0x00, + 0x40, 0x06, 0x46, 0x16, 0xc0, 0xa8, 0x5b, 0x11, 0xc0, 0xa8, 0x5b, 0x01, 0x00, 0x16, 0xd3, 0xdd, + 0x13, 0x6d, 0xc5, 0x53, 0xe3, 0x2e, 0x84, 0x77, 0x80, 0x18, 0x01, 0x10, 0x38, 0xca, 0x00, 0x00, + 0x01, 0x01, 0x08, 0x0a, 0x00, 0x0f, 0x84, 0x7d, 0x2e, 0xc6, 0x70, 0x3a, 0xe3, 0x92, 0x97, 0x1a, + 0x67, 0x3b, 0xac, 0xec, 0xfa, 0x43, 0x71, 0x5e, 0x36, 0xa1, 0x0a, 0xc6, 0x1a, 0x6a, 0xed, 0x08, + 0xac, 0xf4, 0xbe, 0xd8, 0x36, 0x59, 0xf6, 0xe2, 0x3d, 0x34, 0x26, 0xf2, 0x42, 0xbd, 0x32, 0xd3, + 0x37, 0x52, 0xb8, 0xf4, 0x38, 0xf0, 0xf4, 0xeb, 0x76, 0x3b, 0xda, 0x23, 0xf1, 0x92, 0x96, 0xca, + 0xbb, 0x9c, 0x20, 0x0a, 0x38, 0x37, 0x6f, 0xd9, 0x26, 0xe6, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x54, 0x00, 0x02, 0x1f, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, + 0x1f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x03, 0xed, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x14, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x66, 0x6c, + 0x6f, 0x77, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x00, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x31, 0x2e, + 0x73, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6f, 0x72, 0x67, 0xff, 0x00, 0x00, 0x03, 0xec, 0x00, 0x00, + 0x00, 0x2c, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x20, 0x75, 0x73, 0x65, 0x72, 0x77, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x10, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72, 0x00, 0x00, + 0x03, 0xeb, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x0d, 0x0c, 0x0b, 0x0a, 0x00, 0x00, + 0xfd, 0xe9, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x03, 0xe7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, + 0x03, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x2b, 0x67, 0x00, 0x00, + 0x56, 0xce, 0x00, 0x00, 0x82, 0x35, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x09, 0x00, 0x00, 0x03, 0x78, 0x00, 0x00, 0x03, 0xe7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x42, 0x00, 0x0c, 0x29, 0x67, 0xa0, 0xe5, 0x00, 0x50, 0x56, 0xc0, 0x00, 0x09, 0x08, 0x00, + 0x45, 0x10, 0x00, 0x34, 0x65, 0x7d, 0x40, 0x00, 0x40, 0x06, 0x9d, 0xd3, 0xc0, 0xa8, 0x5b, 0x01, + 0xc0, 0xa8, 0x5b, 0x11, 0xd3, 0xdd, 0x00, 0x16, 0xe3, 0x2e, 0x84, 0x77, 0x13, 0x6d, 0xc6, 0x93, + 0x80, 0x10, 0x1f, 0xec, 0xe6, 0x43, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x2e, 0xc6, 0x70, 0x3c, + 0x00, 0x0f, 0x84, 0x7d, 0x00, 0x50, +} + +// processor counter sample +var SFlowTestPacket3 = []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x0a, 0x14, 0x04, 0x00, 0x00, 0x00, 0x00, 0x64, + 0x00, 0x01, 0x78, 0xe0, 0x73, 0x03, 0x48, 0x78, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x34, 0x00, 0x01, 0x78, 0xe0, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe9, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x05, 0xaa, + 0x00, 0x00, 0x05, 0x5a, 0x00, 0x00, 0x05, 0x32, + 0x00, 0x00, 0x00, 0x00, 0xe7, 0x8d, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0xe7, 0x70, 0x00, +} + +// expanded flow sample - extended switch flow record +var SFlowTestPacket4 = []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0xc0, 0xa8, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x7e, 0x32, 0xe0, 0xe4, 0x7c, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x01, 0x23, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x03, 0x37, 0x00, 0x00, 0x56, 0x23, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe9, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, + 0xff, 0xff, 0xff, 0xff, +} + +// expanded flow sample - extended router flow record +var SFlowTestPacket5 = []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0xc0, 0xa8, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x7e, 0x32, 0xe0, 0xe4, 0x7c, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x01, 0x23, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x56, 0x02, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xea, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, + 0xc0, 0xa8, 0x01, 0x21, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x80, +} + +// expanded flow sample - Ipv4 Tunnel Ingress record +var SFlowTestPacket6 = []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x01, 0xbd, 0x50, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x74, 0x02, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, + 0xc0, 0xa8, 0x00, 0x54, 0xc0, 0xa8, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +// expanded flow sample - Ipv4 Tunnel Egress record +var SFlowTestPacket7 = []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x42, 0x68, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, + 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x00, 0x54, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +} + +// expanded flow sample - extended router flow record IPv6 next hop +var SFlowTestPacket8 = []byte{ + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0xc0, 0xa8, 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x7e, 0x32, 0xe0, 0xe4, 0x7c, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x01, 0x23, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x03, 0x34, 0x00, 0x00, 0x56, 0x02, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xea, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, +} + +func TestDecodeUDPSFlow(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket1, LayerTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeSFlow}, t) + if got, ok := p.TransportLayer().(*UDP); ok { + want := &UDP{ + BaseLayer: BaseLayer{SFlowTestPacket1[34:42], SFlowTestPacket1[42:]}, + sPort: []byte{199, 87}, + dPort: []byte{24, 199}, + SrcPort: 51031, + DstPort: 6343, + Checksum: 8763, + Length: 1448, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("UDP layer mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } else { + t.Error("Transport layer packet not UDP") + } +} + +func TestDecodeSFlowDatagram(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket1, LayerTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeSFlow}, t) + if got, ok := p.ApplicationLayer().(*SFlowDatagram); ok { + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{0xa, 0x1, 0xf8, 0x16}, + SubAgentID: uint32(17), + SequenceNumber: uint32(40443), + AgentUptime: uint32(1078576845), + SampleCount: uint32(7), + FlowSamples: []SFlowFlowSample{ + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 0xd0, + SequenceNumber: 0x2627e8, + SourceIDClass: 0x0, + SourceIDIndex: 0x213, + SamplingRate: 0x3e80, + SamplePool: 0x50bde580, + Dropped: 0x0, + InputInterface: 0x213, + OutputInterface: 0x0, + RecordCount: 0x2, + Records: []SFlowRecord{ + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 0x90, + }, + HeaderProtocol: 0x1, + FrameLength: 0x5d2, + PayloadRemoved: 0x4, + HeaderLength: 0x80, + Header: gopacket.NewPacket(SFlowTestPacket1[134:262], LayerTypeEthernet, gopacket.Default), + }, + SFlowExtendedSwitchFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x3e9, + FlowDataLength: 0x10, + }, + IncomingVLAN: 0x202, + IncomingVLANPriority: 0x0, + OutgoingVLAN: 0x0, + OutgoingVLANPriority: 0x0, + }, + }, + }, + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 0xd0, + SequenceNumber: 0x15e5c1e, + SourceIDClass: 0x0, + SourceIDIndex: 0x257, + SamplingRate: 0x7d0, + SamplePool: 0xb12fa290, + Dropped: 0x0, + InputInterface: 0x257, + OutputInterface: 0x0, + RecordCount: 0x2, + Records: []SFlowRecord{ + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 0x90, + }, + HeaderProtocol: 0x1, + FrameLength: 0x5ee, + PayloadRemoved: 0x4, + HeaderLength: 0x80, + Header: gopacket.NewPacket(SFlowTestPacket1[350:478], LayerTypeEthernet, gopacket.Default), + }, + SFlowExtendedSwitchFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x3e9, + FlowDataLength: 0x10, + }, + IncomingVLAN: 0x202, + IncomingVLANPriority: 0x0, + OutgoingVLAN: 0x0, + OutgoingVLANPriority: 0x0, + }, + }, + }, + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 0xd0, + SequenceNumber: 0x15acdd0, + SourceIDClass: 0x0, + SourceIDIndex: 0x255, + SamplingRate: 0x7d0, + SamplePool: 0x9567e130, + Dropped: 0x0, + InputInterface: 0x255, + OutputInterface: 0x0, + RecordCount: 0x2, + Records: []SFlowRecord{ + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 0x90, + }, + HeaderProtocol: 0x1, + FrameLength: 0x446, + PayloadRemoved: 0x4, + HeaderLength: 0x80, + Header: gopacket.NewPacket(SFlowTestPacket1[566:694], LayerTypeEthernet, gopacket.Default), + }, + SFlowExtendedSwitchFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x3e9, + FlowDataLength: 0x10, + }, + IncomingVLAN: 0x202, + IncomingVLANPriority: 0x0, + OutgoingVLAN: 0x0, + OutgoingVLANPriority: 0x0, + }, + }, + }, + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 0xd0, + SequenceNumber: 0x15e5c1f, + SourceIDClass: 0x0, + SourceIDIndex: 0x257, + SamplingRate: 0x7d0, + SamplePool: 0xb12faa60, + Dropped: 0x0, + InputInterface: 0x257, + OutputInterface: 0x0, + RecordCount: 0x2, + Records: []SFlowRecord{ + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 0x90, + }, + HeaderProtocol: 0x1, + FrameLength: 0x5ee, + PayloadRemoved: 0x4, + HeaderLength: 0x80, + Header: gopacket.NewPacket(SFlowTestPacket1[958:1086], LayerTypeEthernet, gopacket.Default), + }, + SFlowExtendedSwitchFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x3e9, + FlowDataLength: 0x10, + }, + IncomingVLAN: 0x202, + IncomingVLANPriority: 0x0, + OutgoingVLAN: 0x0, + OutgoingVLANPriority: 0x0, + }, + }, + }, + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 0xd0, + SequenceNumber: 0x15acdd1, + SourceIDClass: 0x0, + SourceIDIndex: 0x255, + SamplingRate: 0x7d0, + SamplePool: 0x9567e900, + Dropped: 0x0, + InputInterface: 0x255, + OutputInterface: 0x257, + RecordCount: 0x2, + Records: []SFlowRecord{ + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 0x90, + }, + HeaderProtocol: 0x1, + FrameLength: 0x5ee, + PayloadRemoved: 0x4, + HeaderLength: 0x80, + Header: gopacket.NewPacket(SFlowTestPacket1[1174:1302], LayerTypeEthernet, gopacket.Default), + }, + SFlowExtendedSwitchFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x3e9, + FlowDataLength: 0x10, + }, + IncomingVLAN: 0x202, + IncomingVLANPriority: 0x0, + OutgoingVLAN: 0x202, + OutgoingVLANPriority: 0x0, + }, + }, + }, + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 0x94, + SequenceNumber: 0x15e5c20, + SourceIDClass: 0x0, + SourceIDIndex: 0x257, + SamplingRate: 0x7d0, + SamplePool: 0xb12fb230, + Dropped: 0x0, + InputInterface: 0x257, + OutputInterface: 0x0, + RecordCount: 0x2, + Records: []SFlowRecord{ + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 0x54, + }, + HeaderProtocol: 0x1, + FrameLength: 0x46, + PayloadRemoved: 0x4, + HeaderLength: 0x42, + Header: gopacket.NewPacket(SFlowTestPacket1[1390:1458], LayerTypeEthernet, gopacket.Default), + }, + SFlowExtendedSwitchFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x3e9, + FlowDataLength: 0x10, + }, + IncomingVLAN: 0x202, + IncomingVLANPriority: 0x0, + OutgoingVLAN: 0x0, + OutgoingVLANPriority: 0x0, + }, + }, + }, + }, + CounterSamples: []SFlowCounterSample{ + SFlowCounterSample{ + Format: 0x2, + SampleLength: 0xa8, + SequenceNumber: 0x20f2, + SourceIDClass: 0x0, + SourceIDIndex: 0x20a, + RecordCount: 0x2, + Records: []SFlowRecord{ + SFlowGenericInterfaceCounters{ + SFlowBaseCounterRecord: SFlowBaseCounterRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 0x58, + }, + IfIndex: 0x20a, + IfType: 0x6, + IfSpeed: 0x2540be400, + IfDirection: 0x1, + IfStatus: 0x3, + IfInOctets: 0x129826db06c0b, + IfInUcastPkts: 0xcb0ddd96, + IfInMulticastPkts: 0x6a8c6, + IfInBroadcastPkts: 0x7b, + IfInDiscards: 0x0, + IfInErrors: 0x0, + IfInUnknownProtos: 0x0, + IfOutOctets: 0x340235587c9e, + IfOutUcastPkts: 0x56642571, + IfOutMulticastPkts: 0x705ac4, + IfOutBroadcastPkts: 0x908f1, + IfOutDiscards: 0x0, + IfOutErrors: 0x0, + IfPromiscuousMode: 0x0, + }, + SFlowEthernetCounters{ + SFlowBaseCounterRecord: SFlowBaseCounterRecord{ + EnterpriseID: 0x0, + Format: 0x2, + FlowDataLength: 0x34, + }, + AlignmentErrors: 0x0, + FCSErrors: 0x0, + SingleCollisionFrames: 0x0, + MultipleCollisionFrames: 0x0, + SQETestErrors: 0x0, + DeferredTransmissions: 0x0, + LateCollisions: 0x0, + ExcessiveCollisions: 0x0, + InternalMacTransmitErrors: 0x0, + CarrierSenseErrors: 0x0, + FrameTooLongs: 0x0, + InternalMacReceiveErrors: 0x0, + SymbolErrors: 0x0, + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } + } else { + t.Error("Application layer packet not UDP") + } +} + +func TestPacketPacket0(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket2, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeSFlow}, t) + if got, ok := p.ApplicationLayer().(*SFlowDatagram); ok { + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{192, 168, 91, 17}, + SubAgentID: uint32(0), + SequenceNumber: uint32(46394), + AgentUptime: uint32(52000), + SampleCount: uint32(3), + FlowSamples: []SFlowFlowSample{ + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 340, + SequenceNumber: 139118, + SourceIDClass: 0, + SourceIDIndex: 3, + SamplingRate: 1, + SamplePool: 139118, + Dropped: 0, + InputInterface: 3, + OutputInterface: 1073741823, + RecordCount: 4, + Records: []SFlowRecord{ + SFlowExtendedURLRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1005, + FlowDataLength: 48, + }, + Direction: SFlowURLsrc, + URL: "http://www.sflow.org", + Host: "host1.sflow.org", + }, + SFlowExtendedUserFlow{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1004, + FlowDataLength: 44, + }, + SourceCharSet: SFlowCSUTF8, + SourceUserID: "source user", + DestinationCharSet: SFlowCSUTF8, + DestinationUserID: "destination user", + }, + SFlowExtendedGatewayFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1003, + FlowDataLength: 100, + }, + NextHop: []byte{0x0d, 0x0c, 0x0b, 0x0a}, + AS: 65001, + SourceAS: 123, + PeerAS: 999, + ASPathCount: 3, + ASPath: []SFlowASDestination{ + SFlowASDestination{ + Type: SFlowASSequence, + Count: 3, + Members: []uint32{123, 456, 789}, + }, + SFlowASDestination{ + Type: SFlowASSet, + Count: 3, + Members: []uint32{11111, 22222, 33333}, + }, + SFlowASDestination{ + Type: SFlowASSequence, + Count: 3, + Members: []uint32{777, 888, 999}, + }, + }, + Communities: []uint32{12, 13}, + LocalPref: 432, + }, + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 84, + }, + HeaderProtocol: 1, + FrameLength: 70, + PayloadRemoved: 4, + HeaderLength: 0x42, + Header: gopacket.NewPacket(SFlowTestPacket2[350:418], LayerTypeEthernet, gopacket.Default), + }, + }, + }, + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 400, + SequenceNumber: 139119, + SourceIDClass: 0, + SourceIDIndex: 3, + SamplingRate: 1, + SamplePool: 139119, + Dropped: 0, + InputInterface: 1073741823, + OutputInterface: 3, + RecordCount: 4, + Records: []SFlowRecord{ + SFlowExtendedURLRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1005, + FlowDataLength: 48, + }, + Direction: SFlowURLsrc, + URL: "http://www.sflow.org", + Host: "host1.sflow.org", + }, + SFlowExtendedUserFlow{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1004, + FlowDataLength: 44, + }, + SourceCharSet: SFlowCSUTF8, + SourceUserID: "source user", + DestinationCharSet: SFlowCSUTF8, + DestinationUserID: "destination user", + }, + SFlowExtendedGatewayFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1003, + FlowDataLength: 100, + }, + NextHop: []byte{0x0d, 0x0c, 0x0b, 0x0a}, + AS: 65001, + SourceAS: 123, + PeerAS: 999, + ASPathCount: 3, + ASPath: []SFlowASDestination{ + SFlowASDestination{ + Type: SFlowASSequence, + Count: 3, + Members: []uint32{123, 456, 789}, + }, + SFlowASDestination{ + Type: SFlowASSet, + Count: 3, + Members: []uint32{11111, 22222, 33333}, + }, + SFlowASDestination{ + Type: SFlowASSequence, + Count: 3, + Members: []uint32{777, 888, 999}, + }, + }, + Communities: []uint32{12, 13}, + LocalPref: 432, + }, + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 144, + }, + HeaderProtocol: 1, + FrameLength: 390, + PayloadRemoved: 4, + HeaderLength: 0x80, + Header: gopacket.NewPacket(SFlowTestPacket2[698:826], LayerTypeEthernet, gopacket.Default), + }, + }, + }, + SFlowFlowSample{ + EnterpriseID: 0x0, + Format: 0x1, + SampleLength: 340, + SequenceNumber: 139120, + SourceIDClass: 0, + SourceIDIndex: 3, + SamplingRate: 1, + SamplePool: 139120, + Dropped: 0, + InputInterface: 3, + OutputInterface: 1073741823, + RecordCount: 4, + Records: []SFlowRecord{ + SFlowExtendedURLRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1005, + FlowDataLength: 48, + }, + Direction: SFlowURLsrc, + URL: "http://www.sflow.org", + Host: "host1.sflow.org", + }, + SFlowExtendedUserFlow{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1004, + FlowDataLength: 44, + }, + SourceCharSet: SFlowCSUTF8, + SourceUserID: "source user", + DestinationCharSet: SFlowCSUTF8, + DestinationUserID: "destination user", + }, + SFlowExtendedGatewayFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0, + Format: 1003, + FlowDataLength: 100, + }, + NextHop: []byte{0x0d, 0x0c, 0x0b, 0x0a}, + AS: 65001, + SourceAS: 123, + PeerAS: 999, + ASPathCount: 3, + ASPath: []SFlowASDestination{ + SFlowASDestination{ + Type: SFlowASSequence, + Count: 3, + Members: []uint32{123, 456, 789}, + }, + SFlowASDestination{ + Type: SFlowASSet, + Count: 3, + Members: []uint32{11111, 22222, 33333}, + }, + SFlowASDestination{ + Type: SFlowASSequence, + Count: 3, + Members: []uint32{777, 888, 999}, + }, + }, + Communities: []uint32{12, 13}, + LocalPref: 432, + }, + SFlowRawPacketFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: 0x1, + FlowDataLength: 84, + }, + HeaderProtocol: 1, + FrameLength: 70, + PayloadRemoved: 4, + HeaderLength: 0x42, + Header: gopacket.NewPacket(SFlowTestPacket2[1106:1174], LayerTypeEthernet, gopacket.Default), + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } + } else { + t.Error("Application layer packet not UDP") + } +} + +func TestDecodeProcessorCounter(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket3, LayerTypeSFlow, gopacket.Default) + + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeSFlow}, t) + + got := p.ApplicationLayer().(*SFlowDatagram) + + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{0x0a, 0x14, 0x04, 0x00}, + SubAgentID: uint32(0x64), + SequenceNumber: uint32(96480), + AgentUptime: uint32(1929595000), + SampleCount: uint32(1), + CounterSamples: []SFlowCounterSample{ + SFlowCounterSample{ + Format: SFlowTypeExpandedCounterSample, + SampleLength: 0x34, + SequenceNumber: 0x0178e0, + SourceIDClass: 0x00, + SourceIDIndex: 0x01, + RecordCount: 0x01, + Records: []SFlowRecord{ + SFlowProcessorCounters{ + SFlowBaseCounterRecord: SFlowBaseCounterRecord{ + EnterpriseID: 0x0, + Format: SFlowTypeProcessorCounters, + FlowDataLength: 0x1c, + }, + FiveSecCpu: 0x05aa, + OneMinCpu: 0x055a, + FiveMinCpu: 0x0532, + TotalMemory: 0xe78d7000, + FreeMemory: 0x55e77000, + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } +} + +func TestDecodeExtendedSwitchFlow(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket4, LayerTypeSFlow, gopacket.Default) + + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeSFlow}, t) + + got := p.ApplicationLayer().(*SFlowDatagram) + + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{0xc0, 0xa8, 0x01, 0x07}, + SubAgentID: uint32(0x00), + SequenceNumber: uint32(0x027e), + AgentUptime: uint32(0x32e0e47c), + SampleCount: uint32(1), + FlowSamples: []SFlowFlowSample{ + SFlowFlowSample{ + Format: SFlowTypeFlowSample, + SampleLength: 0x38, + SequenceNumber: 0x123, + SourceIDClass: 0x00, + SourceIDIndex: 0x1d, + SamplingRate: 0x100, + SamplePool: 0x337, + Dropped: 0x5623, + InputInterfaceFormat: 0x00, + InputInterface: 0x1d, + OutputInterfaceFormat: 0x00, + OutputInterface: 0x04, + RecordCount: 0x01, + Records: []SFlowRecord{ + SFlowExtendedSwitchFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: SFlowTypeExtendedSwitchFlow, + FlowDataLength: 0x10, + }, + IncomingVLAN: 0x03, + IncomingVLANPriority: 0x02, + OutgoingVLAN: 0x05, + OutgoingVLANPriority: 0xffffffff, + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } +} + +func TestDecodeExtendedRouterFlow(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket5, LayerTypeSFlow, gopacket.Default) + + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeSFlow}, t) + + got := p.ApplicationLayer().(*SFlowDatagram) + + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{0xc0, 0xa8, 0x01, 0x12}, + SubAgentID: uint32(0x00), + SequenceNumber: uint32(0x027e), + AgentUptime: uint32(0x32e0e47c), + SampleCount: uint32(1), + FlowSamples: []SFlowFlowSample{ + SFlowFlowSample{ + Format: SFlowTypeFlowSample, + SampleLength: 0x34, + SequenceNumber: 0x123, + SourceIDClass: 0x00, + SourceIDIndex: 0x1d, + SamplingRate: 0x100, + SamplePool: 0x334, + Dropped: 0x5602, + InputInterfaceFormat: 0x00, + InputInterface: 0x1d, + OutputInterfaceFormat: 0x00, + OutputInterface: 0x04, + RecordCount: 0x01, + Records: []SFlowRecord{ + SFlowExtendedRouterFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: SFlowTypeExtendedRouterFlow, + FlowDataLength: 0x10, + }, + NextHop: []byte{0xc0, 0xa8, 0x01, 0x21}, + NextHopSourceMask: 0xffffffff, + NextHopDestinationMask: 0xffffff80, + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } +} + +func TestDecodeExtendedRouterFlowIPv6(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket8, LayerTypeSFlow, gopacket.Default) + + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeSFlow}, t) + + got := p.ApplicationLayer().(*SFlowDatagram) + + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{0xc0, 0xa8, 0x01, 0x12}, + SubAgentID: uint32(0x00), + SequenceNumber: uint32(0x027e), + AgentUptime: uint32(0x32e0e47c), + SampleCount: uint32(1), + FlowSamples: []SFlowFlowSample{ + SFlowFlowSample{ + Format: SFlowTypeFlowSample, + SampleLength: 0x34, + SequenceNumber: 0x123, + SourceIDClass: 0x00, + SourceIDIndex: 0x1d, + SamplingRate: 0x100, + SamplePool: 0x334, + Dropped: 0x5602, + InputInterfaceFormat: 0x00, + InputInterface: 0x1d, + OutputInterfaceFormat: 0x00, + OutputInterface: 0x04, + RecordCount: 0x01, + Records: []SFlowRecord{ + SFlowExtendedRouterFlowRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: SFlowTypeExtendedRouterFlow, + FlowDataLength: 0x1c, + }, + NextHop: []byte{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + NextHopSourceMask: 0xffffffff, + NextHopDestinationMask: 0xffffff80, + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } +} + +func TestDecodeExtendedIpv4TunnelIngressFlow(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket6, LayerTypeSFlow, gopacket.Default) + + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeSFlow}, t) + + got := p.ApplicationLayer().(*SFlowDatagram) + + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{0x7f, 0x0, 0x0, 0x1}, + SubAgentID: uint32(0), + SequenceNumber: uint32(0x72), + AgentUptime: uint32(0x1bd50), + SampleCount: uint32(1), + FlowSamples: []SFlowFlowSample{ + SFlowFlowSample{ + Format: SFlowTypeFlowSample, + SampleLength: 0x48, + SequenceNumber: 0x74, + SourceIDClass: 0x0, + SourceIDIndex: 0x20003e8, + SamplingRate: 0x1, + SamplePool: 0x74, + Dropped: 0x0, + InputInterfaceFormat: 0x0, + InputInterface: 0x0, + OutputInterfaceFormat: 0x0, + OutputInterface: 0x80000001, + RecordCount: 0x1, + Records: []SFlowRecord{ + SFlowExtendedIpv4TunnelIngressRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: SFlowTypeExtendedIpv4TunnelIngressFlow, + FlowDataLength: 0x20, + }, + SFlowIpv4Record: SFlowIpv4Record{ + Length: 0x0, + Protocol: 0x2f, + IPSrc: net.IP{0xc0, 0xa8, 0x0, 0x54}, + IPDst: net.IP{0xc0, 0xa8, 0x0, 0x53}, + PortSrc: 0x0, + PortDst: 0x0, + TCPFlags: 0x0, + TOS: 0x0, + }, + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } +} + +func TestDecodeExtendedIpv4TunnelEgressFlow(t *testing.T) { + p := gopacket.NewPacket(SFlowTestPacket7, LayerTypeSFlow, gopacket.Default) + + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeSFlow}, t) + + got := p.ApplicationLayer().(*SFlowDatagram) + + want := &SFlowDatagram{ + DatagramVersion: uint32(5), + AgentAddress: []byte{0x7f, 0x0, 0x0, 0x1}, + SubAgentID: uint32(1), + SequenceNumber: uint32(0x12), + AgentUptime: uint32(0x4268), + SampleCount: uint32(1), + FlowSamples: []SFlowFlowSample{ + SFlowFlowSample{ + Format: SFlowTypeFlowSample, + SampleLength: 0x48, + SequenceNumber: 0x12, + SourceIDClass: 0x0, + SourceIDIndex: 0x20003e9, + SamplingRate: 0x1, + SamplePool: 0x12, + Dropped: 0x0, + InputInterfaceFormat: 0x0, + InputInterface: 0x6c, + OutputInterfaceFormat: 0x0, + OutputInterface: 0x80000001, + RecordCount: 0x1, + Records: []SFlowRecord{ + SFlowExtendedIpv4TunnelEgressRecord{ + SFlowBaseFlowRecord: SFlowBaseFlowRecord{ + EnterpriseID: 0x0, + Format: SFlowTypeExtendedIpv4TunnelEgressFlow, + FlowDataLength: 0x20, + }, + SFlowIpv4Record: SFlowIpv4Record{ + Length: 0x0, + Protocol: 0x0, + IPSrc: net.IP{0x0, 0x0, 0x0, 0x0}, + IPDst: net.IP{0xc0, 0xa8, 0x0, 0x54}, + PortSrc: 0x0, + PortDst: 0x0, + TCPFlags: 0x0, + TOS: 0x0, + }, + }, + }, + }, + }, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("SFlow layer mismatch, \nwant:\n\n%#v\ngot:\n\n\n%#v\n\n", want, got) + } +} + +func BenchmarkDecodeSFlowPacket1(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(SFlowTestPacket1, LinkTypeEthernet, gopacket.NoCopy) + } +} + +func BenchmarkDecodeSFlowPacket2(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(SFlowTestPacket2, LinkTypeEthernet, gopacket.NoCopy) + } +} + +func BenchmarkDecodeSFlowPacket3(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(SFlowTestPacket3, LinkTypeEthernet, gopacket.NoCopy) + } +} + +func BenchmarkDecodeSFlowPacket4(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(SFlowTestPacket4, LinkTypeEthernet, gopacket.NoCopy) + } +} + +func BenchmarkDecodeSFlowLayerPacket1(b *testing.B) { + var sflow SFlowDatagram + for i := 0; i < b.N; i++ { + sflow.DecodeFromBytes(SFlowTestPacket1[ /*eth*/ 14+ /*ipv4*/ 20+ /*udp*/ 8:], gopacket.NilDecodeFeedback) + } +} + +func BenchmarkDecodeSFlowLayerPacket2(b *testing.B) { + var sflow SFlowDatagram + for i := 0; i < b.N; i++ { + sflow.DecodeFromBytes(SFlowTestPacket2[ /*eth*/ 14+ /*ipv4*/ 20+ /*udp*/ 8:], gopacket.NilDecodeFeedback) + } +} diff --git a/vendor/github.com/google/gopacket/layers/stp.go b/vendor/github.com/google/gopacket/layers/stp.go new file mode 100644 index 00000000..bde7d7c8 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/stp.go @@ -0,0 +1,27 @@ +// Copyright 2017 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" +) + +// STP decode spanning tree protocol packets to transport BPDU (bridge protocol data unit) message. +type STP struct { + BaseLayer +} + +// LayerType returns gopacket.LayerTypeSTP. +func (s *STP) LayerType() gopacket.LayerType { return LayerTypeSTP } + +func decodeSTP(data []byte, p gopacket.PacketBuilder) error { + stp := &STP{} + stp.Contents = data[:] + // TODO: parse the STP protocol into actual subfields. + p.AddLayer(stp) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/tcp.go b/vendor/github.com/google/gopacket/layers/tcp.go new file mode 100644 index 00000000..fb731da1 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tcp.go @@ -0,0 +1,327 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// TCP is the layer for TCP headers. +type TCP struct { + BaseLayer + SrcPort, DstPort TCPPort + Seq uint32 + Ack uint32 + DataOffset uint8 + FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool + Window uint16 + Checksum uint16 + Urgent uint16 + sPort, dPort []byte + Options []TCPOption + Padding []byte + opts [4]TCPOption + tcpipchecksum +} + +// TCPOptionKind represents a TCP option code. +type TCPOptionKind uint8 + +const ( + TCPOptionKindEndList = 0 + TCPOptionKindNop = 1 + TCPOptionKindMSS = 2 // len = 4 + TCPOptionKindWindowScale = 3 // len = 3 + TCPOptionKindSACKPermitted = 4 // len = 2 + TCPOptionKindSACK = 5 // len = n + TCPOptionKindEcho = 6 // len = 6, obsolete + TCPOptionKindEchoReply = 7 // len = 6, obsolete + TCPOptionKindTimestamps = 8 // len = 10 + TCPOptionKindPartialOrderConnectionPermitted = 9 // len = 2, obsolete + TCPOptionKindPartialOrderServiceProfile = 10 // len = 3, obsolete + TCPOptionKindCC = 11 // obsolete + TCPOptionKindCCNew = 12 // obsolete + TCPOptionKindCCEcho = 13 // obsolete + TCPOptionKindAltChecksum = 14 // len = 3, obsolete + TCPOptionKindAltChecksumData = 15 // len = n, obsolete +) + +func (k TCPOptionKind) String() string { + switch k { + case TCPOptionKindEndList: + return "EndList" + case TCPOptionKindNop: + return "NOP" + case TCPOptionKindMSS: + return "MSS" + case TCPOptionKindWindowScale: + return "WindowScale" + case TCPOptionKindSACKPermitted: + return "SACKPermitted" + case TCPOptionKindSACK: + return "SACK" + case TCPOptionKindEcho: + return "Echo" + case TCPOptionKindEchoReply: + return "EchoReply" + case TCPOptionKindTimestamps: + return "Timestamps" + case TCPOptionKindPartialOrderConnectionPermitted: + return "PartialOrderConnectionPermitted" + case TCPOptionKindPartialOrderServiceProfile: + return "PartialOrderServiceProfile" + case TCPOptionKindCC: + return "CC" + case TCPOptionKindCCNew: + return "CCNew" + case TCPOptionKindCCEcho: + return "CCEcho" + case TCPOptionKindAltChecksum: + return "AltChecksum" + case TCPOptionKindAltChecksumData: + return "AltChecksumData" + default: + return fmt.Sprintf("Unknown(%d)", k) + } +} + +type TCPOption struct { + OptionType TCPOptionKind + OptionLength uint8 + OptionData []byte +} + +func (t TCPOption) String() string { + hd := hex.EncodeToString(t.OptionData) + if len(hd) > 0 { + hd = " 0x" + hd + } + switch t.OptionType { + case TCPOptionKindMSS: + return fmt.Sprintf("TCPOption(%s:%v%s)", + t.OptionType, + binary.BigEndian.Uint16(t.OptionData), + hd) + + case TCPOptionKindTimestamps: + if len(t.OptionData) == 8 { + return fmt.Sprintf("TCPOption(%s:%v/%v%s)", + t.OptionType, + binary.BigEndian.Uint32(t.OptionData[:4]), + binary.BigEndian.Uint32(t.OptionData[4:8]), + hd) + } + } + return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd) +} + +// LayerType returns gopacket.LayerTypeTCP +func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP } + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var optionLength int + for _, o := range t.Options { + switch o.OptionType { + case 0, 1: + optionLength += 1 + default: + optionLength += 2 + len(o.OptionData) + } + } + if opts.FixLengths { + if rem := optionLength % 4; rem != 0 { + t.Padding = lotsOfZeros[:4-rem] + } + t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4) + } + bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding)) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, uint16(t.SrcPort)) + binary.BigEndian.PutUint16(bytes[2:], uint16(t.DstPort)) + binary.BigEndian.PutUint32(bytes[4:], t.Seq) + binary.BigEndian.PutUint32(bytes[8:], t.Ack) + binary.BigEndian.PutUint16(bytes[12:], t.flagsAndOffset()) + binary.BigEndian.PutUint16(bytes[14:], t.Window) + binary.BigEndian.PutUint16(bytes[18:], t.Urgent) + start := 20 + for _, o := range t.Options { + bytes[start] = byte(o.OptionType) + switch o.OptionType { + case 0, 1: + start++ + default: + if opts.FixLengths { + o.OptionLength = uint8(len(o.OptionData) + 2) + } + bytes[start+1] = o.OptionLength + copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData) + start += int(o.OptionLength) + } + } + copy(bytes[start:], t.Padding) + if opts.ComputeChecksums { + // zero out checksum bytes in current serialization. + bytes[16] = 0 + bytes[17] = 0 + csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP) + if err != nil { + return err + } + t.Checksum = csum + } + binary.BigEndian.PutUint16(bytes[16:], t.Checksum) + return nil +} + +func (t *TCP) ComputeChecksum() (uint16, error) { + return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP) +} + +func (t *TCP) flagsAndOffset() uint16 { + f := uint16(t.DataOffset) << 12 + if t.FIN { + f |= 0x0001 + } + if t.SYN { + f |= 0x0002 + } + if t.RST { + f |= 0x0004 + } + if t.PSH { + f |= 0x0008 + } + if t.ACK { + f |= 0x0010 + } + if t.URG { + f |= 0x0020 + } + if t.ECE { + f |= 0x0040 + } + if t.CWR { + f |= 0x0080 + } + if t.NS { + f |= 0x0100 + } + return f +} + +func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2])) + tcp.sPort = data[0:2] + tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4])) + tcp.dPort = data[2:4] + tcp.Seq = binary.BigEndian.Uint32(data[4:8]) + tcp.Ack = binary.BigEndian.Uint32(data[8:12]) + tcp.DataOffset = data[12] >> 4 + tcp.FIN = data[13]&0x01 != 0 + tcp.SYN = data[13]&0x02 != 0 + tcp.RST = data[13]&0x04 != 0 + tcp.PSH = data[13]&0x08 != 0 + tcp.ACK = data[13]&0x10 != 0 + tcp.URG = data[13]&0x20 != 0 + tcp.ECE = data[13]&0x40 != 0 + tcp.CWR = data[13]&0x80 != 0 + tcp.NS = data[12]&0x01 != 0 + tcp.Window = binary.BigEndian.Uint16(data[14:16]) + tcp.Checksum = binary.BigEndian.Uint16(data[16:18]) + tcp.Urgent = binary.BigEndian.Uint16(data[18:20]) + tcp.Options = tcp.opts[:0] + if tcp.DataOffset < 5 { + return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset) + } + dataStart := int(tcp.DataOffset) * 4 + if dataStart > len(data) { + df.SetTruncated() + tcp.Payload = nil + tcp.Contents = data + return errors.New("TCP data offset greater than packet length") + } + tcp.Contents = data[:dataStart] + tcp.Payload = data[dataStart:] + // From here on, data points just to the header options. + data = data[20:dataStart] + for len(data) > 0 { + if tcp.Options == nil { + // Pre-allocate to avoid allocating a slice. + tcp.Options = tcp.opts[:0] + } + tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])}) + opt := &tcp.Options[len(tcp.Options)-1] + switch opt.OptionType { + case TCPOptionKindEndList: // End of options + opt.OptionLength = 1 + tcp.Padding = data[1:] + break + case TCPOptionKindNop: // 1 byte padding + opt.OptionLength = 1 + default: + opt.OptionLength = data[1] + if opt.OptionLength < 2 { + return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength) + } else if int(opt.OptionLength) > len(data) { + return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data)) + } + opt.OptionData = data[2:opt.OptionLength] + } + data = data[opt.OptionLength:] + } + return nil +} + +func (t *TCP) CanDecode() gopacket.LayerClass { + return LayerTypeTCP +} + +func (t *TCP) NextLayerType() gopacket.LayerType { + lt := t.DstPort.LayerType() + if lt == gopacket.LayerTypePayload { + lt = t.SrcPort.LayerType() + } + return lt +} + +func decodeTCP(data []byte, p gopacket.PacketBuilder) error { + tcp := &TCP{} + err := tcp.DecodeFromBytes(data, p) + p.AddLayer(tcp) + p.SetTransportLayer(tcp) + if err != nil { + return err + } + if p.DecodeOptions().DecodeStreamsAsDatagrams { + return p.NextDecoder(tcp.NextLayerType()) + } else { + return p.NextDecoder(gopacket.LayerTypePayload) + } +} + +func (t *TCP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort) +} + +// For testing only +func (t *TCP) SetInternalPortsForTesting() { + t.sPort = make([]byte, 2) + t.dPort = make([]byte, 2) + binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort)) + binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort)) +} diff --git a/vendor/github.com/google/gopacket/layers/tcp_test.go b/vendor/github.com/google/gopacket/layers/tcp_test.go new file mode 100644 index 00000000..56a6aaa8 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tcp_test.go @@ -0,0 +1,60 @@ +// Copyright 2016, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "testing" + + "github.com/google/gopacket" +) + +func TestTCPOptionKindString(t *testing.T) { + testData := []struct { + o *TCPOption + s string + }{ + {&TCPOption{ + OptionType: TCPOptionKindNop, + OptionLength: 1, + }, + "TCPOption(NOP:)"}, + {&TCPOption{ + OptionType: TCPOptionKindMSS, + OptionLength: 4, + OptionData: []byte{0x12, 0x34}, + }, + "TCPOption(MSS:4660 0x1234)"}, + {&TCPOption{ + OptionType: TCPOptionKindTimestamps, + OptionLength: 10, + OptionData: []byte{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}, + }, + "TCPOption(Timestamps:2/1 0x0000000200000001)"}} + + for _, tc := range testData { + if s := tc.o.String(); s != tc.s { + t.Errorf("expected %#v string to be %s, got %s", tc.o, tc.s, s) + } + } +} + +func TestTCPSerializePadding(t *testing.T) { + tcp := &TCP{} + tcp.Options = append(tcp.Options, TCPOption{ + OptionType: TCPOptionKindNop, + OptionLength: 1, + }) + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true} + err := gopacket.SerializeLayers(buf, opts, tcp) + if err != nil { + t.Fatal(err) + } + if len(buf.Bytes())%4 != 0 { + t.Errorf("TCP data of len %d not padding to 32 bit boundary", len(buf.Bytes())) + } +} diff --git a/vendor/github.com/google/gopacket/layers/tcpip.go b/vendor/github.com/google/gopacket/layers/tcpip.go new file mode 100644 index 00000000..64ba51cc --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tcpip.go @@ -0,0 +1,104 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// Checksum computation for TCP/UDP. +type tcpipchecksum struct { + pseudoheader tcpipPseudoHeader +} + +type tcpipPseudoHeader interface { + pseudoheaderChecksum() (uint32, error) +} + +func (ip *IPv4) pseudoheaderChecksum() (csum uint32, err error) { + if err := ip.AddressTo4(); err != nil { + return 0, err + } + csum += (uint32(ip.SrcIP[0]) + uint32(ip.SrcIP[2])) << 8 + csum += uint32(ip.SrcIP[1]) + uint32(ip.SrcIP[3]) + csum += (uint32(ip.DstIP[0]) + uint32(ip.DstIP[2])) << 8 + csum += uint32(ip.DstIP[1]) + uint32(ip.DstIP[3]) + return csum, nil +} + +func (ip *IPv6) pseudoheaderChecksum() (csum uint32, err error) { + if err := ip.AddressTo16(); err != nil { + return 0, err + } + for i := 0; i < 16; i += 2 { + csum += uint32(ip.SrcIP[i]) << 8 + csum += uint32(ip.SrcIP[i+1]) + csum += uint32(ip.DstIP[i]) << 8 + csum += uint32(ip.DstIP[i+1]) + } + return csum, nil +} + +// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any +// initial checksum data that's already been computed. +func tcpipChecksum(data []byte, csum uint32) uint16 { + // to handle odd lengths, we loop to length - 1, incrementing by 2, then + // handle the last byte specifically by checking against the original + // length. + length := len(data) - 1 + for i := 0; i < length; i += 2 { + // For our test packet, doing this manually is about 25% faster + // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16. + csum += uint32(data[i]) << 8 + csum += uint32(data[i+1]) + } + if len(data)%2 == 1 { + csum += uint32(data[length]) << 8 + } + for csum > 0xffff { + csum = (csum >> 16) + (csum & 0xffff) + } + return ^uint16(csum) +} + +// computeChecksum computes a TCP or UDP checksum. headerAndPayload is the +// serialized TCP or UDP header plus its payload, with the checksum zero'd +// out. headerProtocol is the IP protocol number of the upper-layer header. +func (c *tcpipchecksum) computeChecksum(headerAndPayload []byte, headerProtocol IPProtocol) (uint16, error) { + if c.pseudoheader == nil { + return 0, errors.New("TCP/IP layer 4 checksum cannot be computed without network layer... call SetNetworkLayerForChecksum to set which layer to use") + } + length := uint32(len(headerAndPayload)) + csum, err := c.pseudoheader.pseudoheaderChecksum() + if err != nil { + return 0, err + } + csum += uint32(headerProtocol) + csum += length & 0xffff + csum += length >> 16 + return tcpipChecksum(headerAndPayload, csum), nil +} + +// SetNetworkLayerForChecksum tells this layer which network layer is wrapping it. +// This is needed for computing the checksum when serializing, since TCP/IP transport +// layer checksums depends on fields in the IPv4 or IPv6 layer that contains it. +// The passed in layer must be an *IPv4 or *IPv6. +func (i *tcpipchecksum) SetNetworkLayerForChecksum(l gopacket.NetworkLayer) error { + switch v := l.(type) { + case *IPv4: + i.pseudoheader = v + case *IPv6: + i.pseudoheader = v + default: + return fmt.Errorf("cannot use layer type %v for tcp checksum network layer", l.LayerType()) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/tcpip_test.go b/vendor/github.com/google/gopacket/layers/tcpip_test.go new file mode 100644 index 00000000..c002e849 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tcpip_test.go @@ -0,0 +1,185 @@ +// Copyright 2014, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" + "net" + "testing" +) + +const ( + ipv4UDPChecksum = uint16(0xbc5f) // Wireshark + ipv6UDPChecksumWithIPv6DstOpts = uint16(0x4d21) // Wireshark + ipv6UDPChecksumJumbogram = uint16(0xcda8) +) + +func createIPv4ChecksumTestLayer() (ip4 *IPv4) { + ip4 = &IPv4{} + ip4.Version = 4 + ip4.TTL = 64 + ip4.SrcIP = net.ParseIP("192.0.2.1") + ip4.DstIP = net.ParseIP("198.51.100.1") + return +} + +func createIPv6ChecksumTestLayer() (ip6 *IPv6) { + ip6 = &IPv6{} + ip6.Version = 6 + ip6.NextHeader = IPProtocolNoNextHeader + ip6.HopLimit = 64 + ip6.SrcIP = net.ParseIP("2001:db8::1") + ip6.DstIP = net.ParseIP("2001:db8::2") + return +} + +func createIPv6DestinationChecksumTestLayer() (dst *IPv6Destination) { + tlv := &IPv6DestinationOption{} + tlv.OptionType = 0x01 //PadN + tlv.OptionData = []byte{0x00, 0x00, 0x00, 0x00} + dst = &IPv6Destination{} + dst.Options = append(dst.Options, tlv) + dst.NextHeader = IPProtocolNoNextHeader + return +} + +func createUDPChecksumTestLayer() (udp *UDP) { + udp = &UDP{} + udp.SrcPort = UDPPort(12345) + udp.DstPort = UDPPort(9999) + return +} + +func TestIPv4UDPChecksum(t *testing.T) { + var serialize []gopacket.SerializableLayer = make([]gopacket.SerializableLayer, 0, 2) + var u *UDP + var err error + + ip4 := createIPv4ChecksumTestLayer() + ip4.Protocol = IPProtocolUDP + serialize = append(serialize, ip4) + + udp := createUDPChecksumTestLayer() + udp.SetNetworkLayerForChecksum(ip4) + serialize = append(serialize, udp) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true} + err = gopacket.SerializeLayers(buf, opts, serialize...) + if err != nil { + t.Fatal(err) + } + + p := gopacket.NewPacket(buf.Bytes(), LinkTypeRaw, gopacket.Default) + if p.ErrorLayer() != nil { + t.Fatal("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeIPv4, LayerTypeUDP}, t) + + if l, ok := p.Layer(LayerTypeUDP).(*UDP); !ok { + t.Fatal("No UDP layer type found in packet") + } else { + u = l + } + got := u.Checksum + want := ipv4UDPChecksum + if got != want { + t.Errorf("Bad checksum:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } +} + +func TestIPv6UDPChecksumWithIPv6DstOpts(t *testing.T) { + var serialize []gopacket.SerializableLayer = make([]gopacket.SerializableLayer, 0, 3) + var u *UDP + var err error + + ip6 := createIPv6ChecksumTestLayer() + ip6.NextHeader = IPProtocolIPv6Destination + serialize = append(serialize, ip6) + + dst := createIPv6DestinationChecksumTestLayer() + dst.NextHeader = IPProtocolUDP + serialize = append(serialize, dst) + + udp := createUDPChecksumTestLayer() + udp.SetNetworkLayerForChecksum(ip6) + serialize = append(serialize, udp) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true} + err = gopacket.SerializeLayers(buf, opts, serialize...) + if err != nil { + t.Fatal(err) + } + + p := gopacket.NewPacket(buf.Bytes(), LinkTypeRaw, gopacket.Default) + if p.ErrorLayer() != nil { + t.Fatal("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeIPv6, LayerTypeIPv6Destination, LayerTypeUDP}, t) + + if l, ok := p.Layer(LayerTypeUDP).(*UDP); !ok { + t.Fatal("No UDP layer type found in packet") + } else { + u = l + } + got := u.Checksum + want := ipv6UDPChecksumWithIPv6DstOpts + if got != want { + t.Errorf("Bad checksum:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } +} + +func TestIPv6JumbogramUDPChecksum(t *testing.T) { + var serialize []gopacket.SerializableLayer = make([]gopacket.SerializableLayer, 0, 4) + var u *UDP + var err error + + ip6 := &IPv6{} + ip6.Version = 6 + ip6.NextHeader = IPProtocolUDP + ip6.HopLimit = 64 + ip6.SrcIP = net.ParseIP("2001:db8::1") + ip6.DstIP = net.ParseIP("2001:db8::2") + serialize = append(serialize, ip6) + + udp := &UDP{} + udp.SrcPort = UDPPort(12345) + udp.DstPort = UDPPort(9999) + udp.SetNetworkLayerForChecksum(ip6) + serialize = append(serialize, udp) + + payload := make([]byte, ipv6MaxPayloadLength+1) + for i := range payload { + payload[i] = 0xfe + } + serialize = append(serialize, gopacket.Payload(payload)) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true} + err = gopacket.SerializeLayers(buf, opts, serialize...) + if err != nil { + t.Fatal(err) + } + + p := gopacket.NewPacket(buf.Bytes(), LinkTypeRaw, gopacket.Default) + if p.ErrorLayer() != nil { + t.Fatal("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeIPv6, LayerTypeIPv6HopByHop, LayerTypeUDP, gopacket.LayerTypePayload}, t) + + if l, ok := p.Layer(LayerTypeUDP).(*UDP); !ok { + t.Fatal("No UDP layer type found in packet") + } else { + u = l + } + got := u.Checksum + want := ipv6UDPChecksumJumbogram + if got != want { + t.Errorf("Bad checksum:\ngot:\n%#v\n\nwant:\n%#v\n\n", got, want) + } +} diff --git a/vendor/github.com/google/gopacket/layers/test_creator.py b/vendor/github.com/google/gopacket/layers/test_creator.py new file mode 100755 index 00000000..c92d2765 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/test_creator.py @@ -0,0 +1,103 @@ +#!/usr/bin/python +# Copyright 2012 Google, Inc. All rights reserved. + +"""TestCreator creates test templates from pcap files.""" + +import argparse +import base64 +import glob +import re +import string +import subprocess +import sys + + +class Packet(object): + """Helper class encapsulating packet from a pcap file.""" + + def __init__(self, packet_lines): + self.packet_lines = packet_lines + self.data = self._DecodeText(packet_lines) + + @classmethod + def _DecodeText(cls, packet_lines): + packet_bytes = [] + # First line is timestamp and stuff, skip it. + # Format: 0x0010: 0000 0020 3aff 3ffe 0000 0000 0000 0000 ....:.?......... + + for line in packet_lines[1:]: + m = re.match(r'\s+0x[a-f\d]+:\s+((?:[\da-f]{2,4}\s)*)', line, re.IGNORECASE) + if m is None: continue + for hexpart in m.group(1).split(): + packet_bytes.append(base64.b16decode(hexpart.upper())) + return ''.join(packet_bytes) + + def Test(self, name, link_type): + """Yields a test using this packet, as a set of lines.""" + yield '// testPacket%s is the packet:' % name + for line in self.packet_lines: + yield '// ' + line + yield 'var testPacket%s = []byte{' % name + data = list(self.data) + while data: + linebytes, data = data[:16], data[16:] + yield ''.join(['\t'] + ['0x%02x, ' % ord(c) for c in linebytes]) + yield '}' + yield 'func TestPacket%s(t *testing.T) {' % name + yield '\tp := gopacket.NewPacket(testPacket%s, LinkType%s, gopacket.Default)' % (name, link_type) + yield '\tif p.ErrorLayer() != nil {' + yield '\t\tt.Error("Failed to decode packet:", p.ErrorLayer().Error())' + yield '\t}' + yield '\tcheckLayers(p, []gopacket.LayerType{LayerType%s, FILL_ME_IN_WITH_ACTUAL_LAYERS}, t)' % link_type + yield '}' + yield 'func BenchmarkDecodePacket%s(b *testing.B) {' % name + yield '\tfor i := 0; i < b.N; i++ {' + yield '\t\tgopacket.NewPacket(testPacket%s, LinkType%s, gopacket.NoCopy)' % (name, link_type) + yield '\t}' + yield '}' + + + +def GetTcpdumpOutput(filename): + """Runs tcpdump on the given file, returning output as string.""" + return subprocess.check_output( + ['tcpdump', '-XX', '-s', '0', '-n', '-r', filename]) + + +def TcpdumpOutputToPackets(output): + """Reads a pcap file with TCPDump, yielding Packet objects.""" + pdata = [] + for line in output.splitlines(): + if line[0] not in string.whitespace and pdata: + yield Packet(pdata) + pdata = [] + pdata.append(line) + if pdata: + yield Packet(pdata) + + +def main(): + class CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter): + def _format_usage(self, usage, actions, groups, prefix=None): + header =('TestCreator creates gopacket tests using a pcap file.\n\n' + 'Tests are written to standard out... they can then be \n' + 'copied into the file of your choice and modified as \n' + 'you see.\n\n') + return header + argparse.ArgumentDefaultsHelpFormatter._format_usage( + self, usage, actions, groups, prefix) + + parser = argparse.ArgumentParser(formatter_class=CustomHelpFormatter) + parser.add_argument('--link_type', default='Ethernet', help='the link type (default: %(default)s)') + parser.add_argument('--name', default='Packet%d', help='the layer type, must have "%d" inside it') + parser.add_argument('files', metavar='file.pcap', type=str, nargs='+', help='the files to process') + + args = parser.parse_args() + + for arg in args.files: + for path in glob.glob(arg): + for i, packet in enumerate(TcpdumpOutputToPackets(GetTcpdumpOutput(path))): + print '\n'.join(packet.Test( + args.name % i, args.link_type)) + +if __name__ == '__main__': + main() diff --git a/vendor/github.com/google/gopacket/layers/udp.go b/vendor/github.com/google/gopacket/layers/udp.go new file mode 100644 index 00000000..20f8c50f --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/udp.go @@ -0,0 +1,120 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +// UDP is the layer for UDP headers. +type UDP struct { + BaseLayer + SrcPort, DstPort UDPPort + Length uint16 + Checksum uint16 + sPort, dPort []byte + tcpipchecksum +} + +// LayerType returns gopacket.LayerTypeUDP +func (u *UDP) LayerType() gopacket.LayerType { return LayerTypeUDP } + +func (udp *UDP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + udp.SrcPort = UDPPort(binary.BigEndian.Uint16(data[0:2])) + udp.sPort = data[0:2] + udp.DstPort = UDPPort(binary.BigEndian.Uint16(data[2:4])) + udp.dPort = data[2:4] + udp.Length = binary.BigEndian.Uint16(data[4:6]) + udp.Checksum = binary.BigEndian.Uint16(data[6:8]) + udp.BaseLayer = BaseLayer{Contents: data[:8]} + switch { + case udp.Length >= 8: + hlen := int(udp.Length) + if hlen > len(data) { + df.SetTruncated() + hlen = len(data) + } + udp.Payload = data[8:hlen] + case udp.Length == 0: // Jumbogram, use entire rest of data + udp.Payload = data[8:] + default: + return fmt.Errorf("UDP packet too small: %d bytes", udp.Length) + } + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (u *UDP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var jumbo bool + + payload := b.Bytes() + if _, ok := u.pseudoheader.(*IPv6); ok { + if len(payload)+8 > 65535 { + jumbo = true + } + } + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, uint16(u.SrcPort)) + binary.BigEndian.PutUint16(bytes[2:], uint16(u.DstPort)) + if opts.FixLengths { + if jumbo { + u.Length = 0 + } else { + u.Length = uint16(len(payload)) + 8 + } + } + binary.BigEndian.PutUint16(bytes[4:], u.Length) + if opts.ComputeChecksums { + // zero out checksum bytes + bytes[6] = 0 + bytes[7] = 0 + csum, err := u.computeChecksum(b.Bytes(), IPProtocolUDP) + if err != nil { + return err + } + u.Checksum = csum + } + binary.BigEndian.PutUint16(bytes[6:], u.Checksum) + return nil +} + +func (u *UDP) CanDecode() gopacket.LayerClass { + return LayerTypeUDP +} + +// NextLayerType use the destination port to select the +// right next decoder. It tries first to decode via the +// destination port, then the source port. +func (u *UDP) NextLayerType() gopacket.LayerType { + if lt := u.DstPort.LayerType(); lt != gopacket.LayerTypePayload { + return lt + } + return u.SrcPort.LayerType() +} + +func decodeUDP(data []byte, p gopacket.PacketBuilder) error { + udp := &UDP{} + err := udp.DecodeFromBytes(data, p) + p.AddLayer(udp) + p.SetTransportLayer(udp) + if err != nil { + return err + } + return p.NextDecoder(udp.NextLayerType()) +} + +func (u *UDP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointUDPPort, u.sPort, u.dPort) +} diff --git a/vendor/github.com/google/gopacket/layers/udp_test.go b/vendor/github.com/google/gopacket/layers/udp_test.go new file mode 100644 index 00000000..e71236cc --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/udp_test.go @@ -0,0 +1,372 @@ +// Copyright 2012, Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "reflect" + "testing" + + "github.com/google/gopacket" +) + +// testUDPPacketDNS is the packet: +// 10:33:07.883637 IP 172.16.255.1.53 > 172.29.20.15.35181: 47320 7/0/0 MX ALT2.ASPMX.L.GOOGLE.com. 20, MX ASPMX2.GOOGLEMAIL.com. 30, MX ASPMX3.GOOGLEMAIL.com. 30, MX ASPMX4.GOOGLEMAIL.com. 30, MX ASPMX5.GOOGLEMAIL.com. 30, MX ASPMX.L.GOOGLE.com. 10, MX ALT1.ASPMX.L.GOOGLE.com. 20 (202) +// 0x0000: 24be 0527 0b17 001f cab3 75c0 0800 4500 $..'......u...E. +// 0x0010: 00e6 68cf 0000 3f11 a6f9 ac10 ff01 ac1d ..h...?......... +// 0x0020: 140f 0035 896d 00d2 754a b8d8 8180 0001 ...5.m..uJ...... +// 0x0030: 0007 0000 0000 0478 6b63 6403 636f 6d00 .......xkcd.com. +// 0x0040: 000f 0001 c00c 000f 0001 0000 0258 0018 .............X.. +// 0x0050: 0014 0441 4c54 3205 4153 504d 5801 4c06 ...ALT2.ASPMX.L. +// 0x0060: 474f 4f47 4c45 c011 c00c 000f 0001 0000 GOOGLE.......... +// 0x0070: 0258 0016 001e 0641 5350 4d58 320a 474f .X.....ASPMX2.GO +// 0x0080: 4f47 4c45 4d41 494c c011 c00c 000f 0001 OGLEMAIL........ +// 0x0090: 0000 0258 000b 001e 0641 5350 4d58 33c0 ...X.....ASPMX3. +// 0x00a0: 53c0 0c00 0f00 0100 0002 5800 0b00 1e06 S.........X..... +// 0x00b0: 4153 504d 5834 c053 c00c 000f 0001 0000 ASPMX4.S........ +// 0x00c0: 0258 000b 001e 0641 5350 4d58 35c0 53c0 .X.....ASPMX5.S. +// 0x00d0: 0c00 0f00 0100 0002 5800 0400 0ac0 2dc0 ........X.....-. +// 0x00e0: 0c00 0f00 0100 0002 5800 0900 1404 414c ........X.....AL +// 0x00f0: 5431 c02d T1.- +// Packet generated by doing DNS query for 'xkcd.com' +var testUDPPacketDNS = []byte{ + 0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x75, 0xc0, 0x08, 0x00, 0x45, 0x00, + 0x00, 0xe6, 0x68, 0xcf, 0x00, 0x00, 0x3f, 0x11, 0xa6, 0xf9, 0xac, 0x10, 0xff, 0x01, 0xac, 0x1d, + 0x14, 0x0f, 0x00, 0x35, 0x89, 0x6d, 0x00, 0xd2, 0x75, 0x4a, 0xb8, 0xd8, 0x81, 0x80, 0x00, 0x01, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x04, 0x78, 0x6b, 0x63, 0x64, 0x03, 0x63, 0x6f, 0x6d, 0x00, + 0x00, 0x0f, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x18, + 0x00, 0x14, 0x04, 0x41, 0x4c, 0x54, 0x32, 0x05, 0x41, 0x53, 0x50, 0x4d, 0x58, 0x01, 0x4c, 0x06, + 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0xc0, 0x11, 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x58, 0x00, 0x16, 0x00, 0x1e, 0x06, 0x41, 0x53, 0x50, 0x4d, 0x58, 0x32, 0x0a, 0x47, 0x4f, + 0x4f, 0x47, 0x4c, 0x45, 0x4d, 0x41, 0x49, 0x4c, 0xc0, 0x11, 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x58, 0x00, 0x0b, 0x00, 0x1e, 0x06, 0x41, 0x53, 0x50, 0x4d, 0x58, 0x33, 0xc0, + 0x53, 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x0b, 0x00, 0x1e, 0x06, + 0x41, 0x53, 0x50, 0x4d, 0x58, 0x34, 0xc0, 0x53, 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x58, 0x00, 0x0b, 0x00, 0x1e, 0x06, 0x41, 0x53, 0x50, 0x4d, 0x58, 0x35, 0xc0, 0x53, 0xc0, + 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x04, 0x00, 0x0a, 0xc0, 0x2d, 0xc0, + 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x02, 0x58, 0x00, 0x09, 0x00, 0x14, 0x04, 0x41, 0x4c, + 0x54, 0x31, 0xc0, 0x2d, +} + +func TestUDPPacketDNS(t *testing.T) { + p := gopacket.NewPacket(testUDPPacketDNS, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeDNS}, t) + if got, ok := p.TransportLayer().(*UDP); ok { + want := &UDP{ + BaseLayer: BaseLayer{ + Contents: []byte{0x0, 0x35, 0x89, 0x6d, 0x0, 0xd2, 0x75, 0x4a}, + Payload: []byte{0xb8, 0xd8, 0x81, 0x80, 0x0, 0x1, 0x0, + 0x7, 0x0, 0x0, 0x0, 0x0, 0x4, 0x78, 0x6b, 0x63, 0x64, 0x3, 0x63, 0x6f, + 0x6d, 0x0, 0x0, 0xf, 0x0, 0x1, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, + 0x2, 0x58, 0x0, 0x18, 0x0, 0x14, 0x4, 0x41, 0x4c, 0x54, 0x32, 0x5, 0x41, + 0x53, 0x50, 0x4d, 0x58, 0x1, 0x4c, 0x6, 0x47, 0x4f, 0x4f, 0x47, 0x4c, + 0x45, 0xc0, 0x11, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x58, 0x0, + 0x16, 0x0, 0x1e, 0x6, 0x41, 0x53, 0x50, 0x4d, 0x58, 0x32, 0xa, 0x47, 0x4f, + 0x4f, 0x47, 0x4c, 0x45, 0x4d, 0x41, 0x49, 0x4c, 0xc0, 0x11, 0xc0, 0xc, + 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x58, 0x0, 0xb, 0x0, 0x1e, 0x6, 0x41, + 0x53, 0x50, 0x4d, 0x58, 0x33, 0xc0, 0x53, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, + 0x0, 0x0, 0x2, 0x58, 0x0, 0xb, 0x0, 0x1e, 0x6, 0x41, 0x53, 0x50, 0x4d, + 0x58, 0x34, 0xc0, 0x53, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, + 0x58, 0x0, 0xb, 0x0, 0x1e, 0x6, 0x41, 0x53, 0x50, 0x4d, 0x58, 0x35, 0xc0, + 0x53, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x58, 0x0, 0x4, 0x0, + 0xa, 0xc0, 0x2d, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x58, 0x0, + 0x9, 0x0, 0x14, 0x4, 0x41, 0x4c, 0x54, 0x31, 0xc0, 0x2d}, + }, + SrcPort: 53, + DstPort: 35181, + Length: 210, + Checksum: 30026, + sPort: []byte{0x0, 0x35}, + dPort: []byte{0x89, 0x6d}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("UDP packet mismatch:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } else { + t.Error("Transport layer packet not UDP") + } +} + +func loadDNS(dnspacket []byte, t *testing.T) *DNS { + p := gopacket.NewPacket(dnspacket, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, + LayerTypeUDP, LayerTypeDNS}, t) + + dnsL := p.Layer(LayerTypeDNS) + if dnsL == nil { + t.Error("No DNS Layer found") + } + + dns, ok := dnsL.(*DNS) + if !ok { + return nil + } + return dns +} + +var testDNSQueryA = []byte{ + 0xfe, 0x54, 0x00, 0x3e, 0x00, 0x96, 0x52, 0x54, /* .T.>..RT */ + 0x00, 0xbd, 0x1c, 0x70, 0x08, 0x00, 0x45, 0x00, /* ...p..E. */ + 0x00, 0x3c, 0x22, 0xe0, 0x00, 0x00, 0x40, 0x11, /* .<"...@. */ + 0xe2, 0x38, 0xc0, 0xa8, 0x7a, 0x46, 0xc0, 0xa8, /* .8..zF.. */ + 0x7a, 0x01, 0xc3, 0x35, 0x00, 0x35, 0x00, 0x28, /* z..5.5.( */ + 0x75, 0xd2, 0x52, 0x41, 0x01, 0x00, 0x00, 0x01, /* u.RA.... */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, /* .......w */ + 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, /* ww.googl */ + 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, /* e.com... */ + 0x00, 0x01, /* .. */ +} + +func TestDNSQueryA(t *testing.T) { + dns := loadDNS(testDNSQueryA, t) + if dns == nil { + t.Fatal("Failed to get a pointer to DNS struct") + } + + if uint16(len(dns.Questions)) != dns.QDCount { + t.Fatal("Invalid query decoding, not the right number of questions") + } + + if string(dns.Questions[0].Name) != "www.google.com" { + t.Errorf("Invalid query decoding, expecting 'www.google.com', got '%s'", + dns.Questions[0].Name) + } + if dns.Questions[0].Class != DNSClassIN { + t.Errorf("Invalid query decoding, expecting Class IN, got '%d'", + dns.Questions[0].Class) + } + + if dns.Questions[0].Type != DNSTypeA { + t.Errorf("Invalid query decoding, expecting Type A, got '%d'", + dns.Questions[0].Type) + } +} + +var testDNSRRA = []byte{ + 0x52, 0x54, 0x00, 0xbd, 0x1c, 0x70, 0xfe, 0x54, /* RT...p.T */ + 0x00, 0x3e, 0x00, 0x96, 0x08, 0x00, 0x45, 0x00, /* .>....E. */ + 0x01, 0x24, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, /* .$..@.@. */ + 0xc4, 0x30, 0xc0, 0xa8, 0x7a, 0x01, 0xc0, 0xa8, /* .0..z... */ + 0x7a, 0x46, 0x00, 0x35, 0xc3, 0x35, 0x01, 0x10, /* zF.5.5.. */ + 0x76, 0xba, 0x52, 0x41, 0x81, 0x80, 0x00, 0x01, /* v.RA.... */ + 0x00, 0x06, 0x00, 0x04, 0x00, 0x04, 0x03, 0x77, /* .......w */ + 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, /* ww.googl */ + 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, /* e.com... */ + 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, /* ........ */ + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x4a, 0x7d, /* ...,..J} */ + 0xc3, 0x67, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, /* .g...... */ + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x4a, 0x7d, /* ...,..J} */ + 0xc3, 0x68, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, /* .h...... */ + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x4a, 0x7d, /* ...,..J} */ + 0xc3, 0x69, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, /* .i...... */ + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x4a, 0x7d, /* ...,..J} */ + 0xc3, 0x6a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, /* .j...... */ + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x4a, 0x7d, /* ...,..J} */ + 0xc3, 0x93, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, /* ........ */ + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x4a, 0x7d, /* ...,..J} */ + 0xc3, 0x63, 0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, /* .c...... */ + 0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, 0x03, 0x6e, /* .......n */ + 0x73, 0x32, 0xc0, 0x10, 0xc0, 0x10, 0x00, 0x02, /* s2...... */ + 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, /* ........ */ + 0x03, 0x6e, 0x73, 0x33, 0xc0, 0x10, 0xc0, 0x10, /* .ns3.... */ + 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */ + 0x00, 0x06, 0x03, 0x6e, 0x73, 0x31, 0xc0, 0x10, /* ...ns1.. */ + 0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, /* ........ */ + 0xa3, 0x00, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x34, /* .....ns4 */ + 0xc0, 0x10, 0xc0, 0xb0, 0x00, 0x01, 0x00, 0x01, /* ........ */ + 0x00, 0x02, 0xa3, 0x00, 0x00, 0x04, 0xd8, 0xef, /* ........ */ + 0x20, 0x0a, 0xc0, 0x8c, 0x00, 0x01, 0x00, 0x01, /* ....... */ + 0x00, 0x02, 0xa3, 0x00, 0x00, 0x04, 0xd8, 0xef, /* ........ */ + 0x22, 0x0a, 0xc0, 0x9e, 0x00, 0x01, 0x00, 0x01, /* "....... */ + 0x00, 0x02, 0xa3, 0x00, 0x00, 0x04, 0xd8, 0xef, /* ........ */ + 0x24, 0x0a, 0xc0, 0xc2, 0x00, 0x01, 0x00, 0x01, /* $....... */ + 0x00, 0x02, 0xa3, 0x00, 0x00, 0x04, 0xd8, 0xef, /* ........ */ + 0x26, 0x0a, /* &. */ +} + +func TestDNSRRA(t *testing.T) { + dns := loadDNS(testDNSRRA, t) + if dns == nil { + t.Fatal("Failed to get a pointer to DNS struct") + return + } + + if uint16(len(dns.Questions)) != dns.QDCount { + t.Fatal("Invalid query decoding, not the right number of questions") + } else if uint16(len(dns.Answers)) != dns.ANCount { + t.Fatal("Invalid query decoding, not the right number of answers") + } else if uint16(len(dns.Authorities)) != dns.NSCount { + t.Fatal("Invalid query decoding, not the right number of authorities") + } else if uint16(len(dns.Additionals)) != dns.ARCount { + t.Fatal("Invalid query decoding, not the right number of additionals info") + } + + if string(dns.Questions[0].Name) != "www.google.com" { + t.Errorf("Invalid query decoding, expecting 'www.google.com', got '%s'", + dns.Questions[0].Name) + } + if string(dns.Answers[0].Name) != "www.google.com" { + t.Errorf("Invalid query decoding, expecting 'www.google.com', got '%d'", + dns.Questions[0].Class) + } + if dns.Answers[0].Class != DNSClassIN { + t.Errorf("Invalid query decoding, expecting Class IN, got '%d'", + dns.Questions[0].Class) + } + if dns.Answers[0].Type != DNSTypeA { + t.Errorf("Invalid query decoding, expecting Type A, got '%d'", + dns.Questions[0].Type) + } + if !dns.Answers[0].IP.Equal([]byte{74, 125, 195, 103}) { + t.Errorf("Invalid query decoding, invalid IP address,"+ + " expecting '74.125.195.103', got '%s'", + dns.Answers[0].IP.String()) + } + if len(dns.Answers) != 6 { + t.Errorf("No correct number of answers, expecting 6, go '%d'", + len(dns.Answers)) + } + if len(dns.Authorities) != 4 { + t.Errorf("No correct number of answers, expecting 4, go '%d'", + len(dns.Answers)) + } + if len(dns.Additionals) != 4 { + t.Errorf("No correct number of answers, expecting 4, go '%d'", + len(dns.Answers)) + } else { + for i, want := range []string{ + "ns1.google.com", + "ns2.google.com", + "ns3.google.com", + "ns4.google.com", + } { + if got := string(dns.Additionals[i].Name); got != want { + t.Errorf("got %q want %q", got, want) + } + } + } +} + +var testDNSAAAA = []byte{ + 0x52, 0x54, 0x00, 0xbd, 0x1c, 0x70, 0xfe, 0x54, /* RT...p.T */ + 0x00, 0x3e, 0x00, 0x96, 0x08, 0x00, 0x45, 0x00, /* .>....E. */ + 0x00, 0xe0, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, /* ....@.@. */ + 0xc4, 0x74, 0xc0, 0xa8, 0x7a, 0x01, 0xc0, 0xa8, /* .t..z... */ + 0x7a, 0x46, 0x00, 0x35, 0xdb, 0x13, 0x00, 0xcc, /* zF.5.... */ + 0x76, 0x76, 0xf3, 0x03, 0x81, 0x80, 0x00, 0x01, /* vv...... */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x04, 0x03, 0x77, /* .......w */ + 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, /* ww.googl */ + 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x1c, /* e.com... */ + 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x1c, 0x00, 0x01, /* ........ */ + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x10, 0x2a, 0x00, /* ...,..*. */ + 0x14, 0x50, 0x40, 0x0c, 0x0c, 0x01, 0x00, 0x00, /* .P@..... */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xc0, 0x10, /* .....i.. */ + 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */ + 0x00, 0x06, 0x03, 0x6e, 0x73, 0x34, 0xc0, 0x10, /* ...ns4.. */ + 0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, /* ........ */ + 0xa3, 0x00, 0x00, 0x06, 0x03, 0x6e, 0x73, 0x32, /* .....ns2 */ + 0xc0, 0x10, 0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, /* ........ */ + 0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, 0x03, 0x6e, /* .......n */ + 0x73, 0x31, 0xc0, 0x10, 0xc0, 0x10, 0x00, 0x02, /* s1...... */ + 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, 0x00, 0x06, /* ........ */ + 0x03, 0x6e, 0x73, 0x33, 0xc0, 0x10, 0xc0, 0x6c, /* .ns3...l */ + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */ + 0x00, 0x04, 0xd8, 0xef, 0x20, 0x0a, 0xc0, 0x5a, /* .... ..Z */ + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */ + 0x00, 0x04, 0xd8, 0xef, 0x22, 0x0a, 0xc0, 0x7e, /* ...."..~ */ + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */ + 0x00, 0x04, 0xd8, 0xef, 0x24, 0x0a, 0xc0, 0x48, /* ....$..H */ + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0xa3, 0x00, /* ........ */ + 0x00, 0x04, 0xd8, 0xef, 0x26, 0x0a, /* ....&. */ +} + +func TestDNSAAAA(t *testing.T) { + dns := loadDNS(testDNSAAAA, t) + if dns == nil { + t.Error("Failed to get a pointer to DNS struct") + return + } + + if len(dns.Questions) != 1 { + t.Error("Invalid number of question") + return + } + if dns.Questions[0].Type != DNSTypeAAAA { + t.Errorf("Invalid question, Type is not AAAA, found %d", + dns.Questions[0].Type) + } + + if len(dns.Answers) != 1 { + t.Error("Invalid number of answers") + } + if !dns.Answers[0].IP.Equal([]byte{0x2a, 0x00, 0x14, 0x50, 0x40, + 0x0c, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69}) { + t.Error("Invalid IP address, found ", + dns.Answers[0].IP.String()) + } +} + +var testDNSMXSOA = []byte{ + 0x52, 0x54, 0x00, 0xbd, 0x1c, 0x70, 0xfe, 0x54, /* RT...p.T */ + 0x00, 0x3e, 0x00, 0x96, 0x08, 0x00, 0x45, 0x00, /* .>....E. */ + 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x40, 0x11, /* .n..@.@. */ + 0xc4, 0xe6, 0xc0, 0xa8, 0x7a, 0x01, 0xc0, 0xa8, /* ....z... */ + 0x7a, 0x46, 0x00, 0x35, 0x9c, 0x60, 0x00, 0x5a, /* zF.5.`.Z */ + 0x76, 0x04, 0xfc, 0x7a, 0x81, 0x80, 0x00, 0x01, /* v..z.... */ + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x77, /* .......w */ + 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, /* ww.googl */ + 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x0f, /* e.com... */ + 0x00, 0x01, 0xc0, 0x10, 0x00, 0x06, 0x00, 0x01, /* ........ */ + 0x00, 0x00, 0x00, 0x3c, 0x00, 0x26, 0x03, 0x6e, /* ...<.&.n */ + 0x73, 0x31, 0xc0, 0x10, 0x09, 0x64, 0x6e, 0x73, /* s1...dns */ + 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0xc0, 0x10, /* -admin.. */ + 0x00, 0x17, 0x9f, 0x64, 0x00, 0x00, 0x1c, 0x20, /* ...d... */ + 0x00, 0x00, 0x07, 0x08, 0x00, 0x12, 0x75, 0x00, /* ......u. */ + 0x00, 0x00, 0x01, 0x2c, /* ..., */ +} + +func TestDNSMXSOA(t *testing.T) { + dns := loadDNS(testDNSMXSOA, t) + if dns == nil { + t.Error("Failed to get a pointer to DNS struct") + return + } + + if len(dns.Authorities) != 1 { + t.Error("Invalid number of authoritative answers, found ", + len(dns.Authorities)) + return + } +} + +func BenchmarkDecodeDNS(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testDNSQueryA, LinkTypeEthernet, gopacket.NoCopy) + } +} +func BenchmarkDecodeDNSLayer(b *testing.B) { + var dns DNS + for i := 0; i < b.N; i++ { + dns.DecodeFromBytes(testDNSAAAA[ /*eth*/ 14+ /*ipv4*/ 20+ /*udp*/ 8:], gopacket.NilDecodeFeedback) + } +} +func TestDNSDoesNotMalloc(t *testing.T) { + var dns DNS + if n := testing.AllocsPerRun(1000, func() { + if err := dns.DecodeFromBytes(testDNSAAAA[ /*eth*/ 14+ /*ipv4*/ 20+ /*udp*/ 8:], gopacket.NilDecodeFeedback); err != nil { + t.Fatal(err) + } + }); n > 0 { + t.Error(n, "mallocs decoding DNS") + } +} diff --git a/vendor/github.com/google/gopacket/layers/udplite.go b/vendor/github.com/google/gopacket/layers/udplite.go new file mode 100644 index 00000000..7d84c514 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/udplite.go @@ -0,0 +1,44 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// UDPLite is the layer for UDP-Lite headers (rfc 3828). +type UDPLite struct { + BaseLayer + SrcPort, DstPort UDPLitePort + ChecksumCoverage uint16 + Checksum uint16 + sPort, dPort []byte +} + +// LayerType returns gopacket.LayerTypeUDPLite +func (u *UDPLite) LayerType() gopacket.LayerType { return LayerTypeUDPLite } + +func decodeUDPLite(data []byte, p gopacket.PacketBuilder) error { + udp := &UDPLite{ + SrcPort: UDPLitePort(binary.BigEndian.Uint16(data[0:2])), + sPort: data[0:2], + DstPort: UDPLitePort(binary.BigEndian.Uint16(data[2:4])), + dPort: data[2:4], + ChecksumCoverage: binary.BigEndian.Uint16(data[4:6]), + Checksum: binary.BigEndian.Uint16(data[6:8]), + BaseLayer: BaseLayer{data[:8], data[8:]}, + } + p.AddLayer(udp) + p.SetTransportLayer(udp) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +func (u *UDPLite) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointUDPLitePort, u.sPort, u.dPort) +} diff --git a/vendor/github.com/google/gopacket/layers/usb.go b/vendor/github.com/google/gopacket/layers/usb.go new file mode 100644 index 00000000..d4f483cf --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/usb.go @@ -0,0 +1,308 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +type USBEventType uint8 + +const ( + USBEventTypeSubmit USBEventType = 'S' + USBEventTypeComplete USBEventType = 'C' + USBEventTypeError USBEventType = 'E' +) + +func (a USBEventType) String() string { + switch a { + case USBEventTypeSubmit: + return "SUBMIT" + case USBEventTypeComplete: + return "COMPLETE" + case USBEventTypeError: + return "ERROR" + default: + return "Unknown event type" + } +} + +type USBRequestBlockSetupRequest uint8 + +const ( + USBRequestBlockSetupRequestGetStatus USBRequestBlockSetupRequest = 0x00 + USBRequestBlockSetupRequestClearFeature USBRequestBlockSetupRequest = 0x01 + USBRequestBlockSetupRequestSetFeature USBRequestBlockSetupRequest = 0x03 + USBRequestBlockSetupRequestSetAddress USBRequestBlockSetupRequest = 0x05 + USBRequestBlockSetupRequestGetDescriptor USBRequestBlockSetupRequest = 0x06 + USBRequestBlockSetupRequestSetDescriptor USBRequestBlockSetupRequest = 0x07 + USBRequestBlockSetupRequestGetConfiguration USBRequestBlockSetupRequest = 0x08 + USBRequestBlockSetupRequestSetConfiguration USBRequestBlockSetupRequest = 0x09 + USBRequestBlockSetupRequestSetIdle USBRequestBlockSetupRequest = 0x0a +) + +func (a USBRequestBlockSetupRequest) String() string { + switch a { + case USBRequestBlockSetupRequestGetStatus: + return "GET_STATUS" + case USBRequestBlockSetupRequestClearFeature: + return "CLEAR_FEATURE" + case USBRequestBlockSetupRequestSetFeature: + return "SET_FEATURE" + case USBRequestBlockSetupRequestSetAddress: + return "SET_ADDRESS" + case USBRequestBlockSetupRequestGetDescriptor: + return "GET_DESCRIPTOR" + case USBRequestBlockSetupRequestSetDescriptor: + return "SET_DESCRIPTOR" + case USBRequestBlockSetupRequestGetConfiguration: + return "GET_CONFIGURATION" + case USBRequestBlockSetupRequestSetConfiguration: + return "SET_CONFIGURATION" + case USBRequestBlockSetupRequestSetIdle: + return "SET_IDLE" + default: + return "UNKNOWN" + } +} + +type USBTransportType uint8 + +const ( + USBTransportTypeTransferIn USBTransportType = 0x80 // Indicates send or receive + USBTransportTypeIsochronous USBTransportType = 0x00 // Isochronous transfers occur continuously and periodically. They typically contain time sensitive information, such as an audio or video stream. + USBTransportTypeInterrupt USBTransportType = 0x01 // Interrupt transfers are typically non-periodic, small device "initiated" communication requiring bounded latency, such as pointing devices or keyboards. + USBTransportTypeControl USBTransportType = 0x02 // Control transfers are typically used for command and status operations. + USBTransportTypeBulk USBTransportType = 0x03 // Bulk transfers can be used for large bursty data, using all remaining available bandwidth, no guarantees on bandwidth or latency, such as file transfers. +) + +func (a USBTransportType) LayerType() gopacket.LayerType { + return USBTypeMetadata[a].LayerType +} + +func (a USBTransportType) String() string { + switch a { + case USBTransportTypeTransferIn: + return "Transfer In" + case USBTransportTypeIsochronous: + return "Isochronous" + case USBTransportTypeInterrupt: + return "Interrupt" + case USBTransportTypeControl: + return "Control" + case USBTransportTypeBulk: + return "Bulk" + default: + return "Unknown transport type" + } +} + +type USBDirectionType uint8 + +const ( + USBDirectionTypeUnknown USBDirectionType = iota + USBDirectionTypeIn + USBDirectionTypeOut +) + +func (a USBDirectionType) String() string { + switch a { + case USBDirectionTypeIn: + return "In" + case USBDirectionTypeOut: + return "Out" + default: + return "Unknown direction type" + } +} + +// The reference at http://www.beyondlogic.org/usbnutshell/usb1.shtml contains more information about the protocol. +type USB struct { + BaseLayer + ID uint64 + EventType USBEventType + TransferType USBTransportType + Direction USBDirectionType + EndpointNumber uint8 + DeviceAddress uint8 + BusID uint16 + TimestampSec int64 + TimestampUsec int32 + Setup bool + Data bool + Status int32 + UrbLength uint32 + UrbDataLength uint32 + + UrbInterval uint32 + UrbStartFrame uint32 + UrbCopyOfTransferFlags uint32 + IsoNumDesc uint32 +} + +func (u *USB) LayerType() gopacket.LayerType { return LayerTypeUSB } + +func (m *USB) NextLayerType() gopacket.LayerType { + if m.Setup { + return LayerTypeUSBRequestBlockSetup + } else if m.Data { + } + + return m.TransferType.LayerType() +} + +func decodeUSB(data []byte, p gopacket.PacketBuilder) error { + d := &USB{} + + return decodingLayerDecoder(d, data, p) +} + +func (m *USB) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.ID = binary.LittleEndian.Uint64(data[0:8]) + m.EventType = USBEventType(data[8]) + m.TransferType = USBTransportType(data[9]) + + m.EndpointNumber = data[10] & 0x7f + if data[10]&uint8(USBTransportTypeTransferIn) > 0 { + m.Direction = USBDirectionTypeIn + } else { + m.Direction = USBDirectionTypeOut + } + + m.DeviceAddress = data[11] + m.BusID = binary.LittleEndian.Uint16(data[12:14]) + + if uint(data[14]) == 0 { + m.Setup = true + } + + if uint(data[15]) == 0 { + m.Data = true + } + + m.TimestampSec = int64(binary.LittleEndian.Uint64(data[16:24])) + m.TimestampUsec = int32(binary.LittleEndian.Uint32(data[24:28])) + m.Status = int32(binary.LittleEndian.Uint32(data[28:32])) + m.UrbLength = binary.LittleEndian.Uint32(data[32:36]) + m.UrbDataLength = binary.LittleEndian.Uint32(data[36:40]) + + m.Contents = data[:40] + m.Payload = data[40:] + + if m.Setup { + m.Payload = data[40:] + } else if m.Data { + m.Payload = data[uint32(len(data))-m.UrbDataLength:] + } + + // if 64 bit, dissect_linux_usb_pseudo_header_ext + if false { + m.UrbInterval = binary.LittleEndian.Uint32(data[40:44]) + m.UrbStartFrame = binary.LittleEndian.Uint32(data[44:48]) + m.UrbDataLength = binary.LittleEndian.Uint32(data[48:52]) + m.IsoNumDesc = binary.LittleEndian.Uint32(data[52:56]) + m.Contents = data[:56] + m.Payload = data[56:] + } + + // crc5 or crc16 + // eop (end of packet) + + return nil +} + +type USBRequestBlockSetup struct { + BaseLayer + RequestType uint8 + Request USBRequestBlockSetupRequest + Value uint16 + Index uint16 + Length uint16 +} + +func (u *USBRequestBlockSetup) LayerType() gopacket.LayerType { return LayerTypeUSBRequestBlockSetup } + +func (m *USBRequestBlockSetup) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBRequestBlockSetup) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.RequestType = data[0] + m.Request = USBRequestBlockSetupRequest(data[1]) + m.Value = binary.LittleEndian.Uint16(data[2:4]) + m.Index = binary.LittleEndian.Uint16(data[4:6]) + m.Length = binary.LittleEndian.Uint16(data[6:8]) + m.Contents = data[:8] + m.Payload = data[8:] + return nil +} + +func decodeUSBRequestBlockSetup(data []byte, p gopacket.PacketBuilder) error { + d := &USBRequestBlockSetup{} + return decodingLayerDecoder(d, data, p) +} + +type USBControl struct { + BaseLayer +} + +func (u *USBControl) LayerType() gopacket.LayerType { return LayerTypeUSBControl } + +func (m *USBControl) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBControl) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeUSBControl(data []byte, p gopacket.PacketBuilder) error { + d := &USBControl{} + return decodingLayerDecoder(d, data, p) +} + +type USBInterrupt struct { + BaseLayer +} + +func (u *USBInterrupt) LayerType() gopacket.LayerType { return LayerTypeUSBInterrupt } + +func (m *USBInterrupt) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBInterrupt) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeUSBInterrupt(data []byte, p gopacket.PacketBuilder) error { + d := &USBInterrupt{} + return decodingLayerDecoder(d, data, p) +} + +type USBBulk struct { + BaseLayer +} + +func (u *USBBulk) LayerType() gopacket.LayerType { return LayerTypeUSBBulk } + +func (m *USBBulk) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBBulk) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeUSBBulk(data []byte, p gopacket.PacketBuilder) error { + d := &USBBulk{} + return decodingLayerDecoder(d, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/usb_test.go b/vendor/github.com/google/gopacket/layers/usb_test.go new file mode 100644 index 00000000..35e9c707 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/usb_test.go @@ -0,0 +1,73 @@ +// Copyright 2014, Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + _ "fmt" + "github.com/google/gopacket" + "reflect" + "testing" +) + +// Generator python layers/test_creator.py --link_type USB --name USB dongle.pcap +// http://wiki.wireshark.org/SampleCaptures#Sample_Captures + +// testPacketUSB0 is the packet: +// 02:41:04.689546 INTERRUPT COMPLETE to 2:1:1 +// 0x0000: 0038 4a3b 0088 ffff 4301 8101 0200 2d00 .8J;....C.....-. +// 0x0010: c0d3 5b50 0000 0000 8a85 0a00 0000 0000 ..[P............ +// 0x0020: 0100 0000 0100 0000 0000 0000 0000 0000 ................ +// 0x0030: 8000 0000 0000 0000 0002 0000 0000 0000 ................ +// 0x0040: 04 . +var testPacketUSB0 = []byte{ + 0x00, 0x38, 0x4a, 0x3b, 0x00, 0x88, 0xff, 0xff, 0x43, 0x01, 0x81, 0x01, 0x02, 0x00, 0x2d, 0x00, + 0xc0, 0xd3, 0x5b, 0x50, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x85, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, +} + +func TestPacketUSB0(t *testing.T) { + p := gopacket.NewPacket(testPacketUSB0, LinkTypeLinuxUSB, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeUSB, LayerTypeUSBInterrupt}, t) + + if got, ok := p.Layer(LayerTypeUSB).(*USB); ok { + want := &USB{ + BaseLayer: BaseLayer{ + Contents: []uint8{0x0, 0x38, 0x4a, 0x3b, 0x0, 0x88, 0xff, 0xff, 0x43, 0x1, 0x81, 0x1, 0x2, 0x0, 0x2d, 0x0, 0xc0, 0xd3, 0x5b, 0x50, 0x0, 0x0, 0x0, 0x0, 0x8a, 0x85, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0}, + Payload: []uint8{0x4}, + }, + ID: 0xffff88003b4a3800, + EventType: USBEventTypeComplete, + TransferType: USBTransportTypeInterrupt, + Direction: 0x1, + EndpointNumber: 0x1, + DeviceAddress: 0x1, + BusID: 0x2, + TimestampSec: 1348195264, + TimestampUsec: 689546, + Setup: false, + Data: true, + Status: 0, + UrbLength: 0x1, + UrbDataLength: 0x1, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("USB packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want) + } + } + +} +func BenchmarkDecodePacketUSB0(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketUSB0, LinkTypeLinuxUSB, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/vrrp.go b/vendor/github.com/google/gopacket/layers/vrrp.go new file mode 100644 index 00000000..ffaafe6a --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/vrrp.go @@ -0,0 +1,156 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "net" + + "github.com/google/gopacket" +) + +/* + This layer provides decoding for Virtual Router Redundancy Protocol (VRRP) v2. + https://tools.ietf.org/html/rfc3768#section-5 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Version| Type | Virtual Rtr ID| Priority | Count IP Addrs| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Auth Type | Adver Int | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address (1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | . | + | . | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address (n) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Authentication Data (1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Authentication Data (2) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +type VRRPv2Type uint8 +type VRRPv2AuthType uint8 + +const ( + VRRPv2Advertisement VRRPv2Type = 0x01 // router advertisement +) + +// String conversions for VRRP message types +func (v VRRPv2Type) String() string { + switch v { + case VRRPv2Advertisement: + return "VRRPv2 Advertisement" + default: + return "" + } +} + +const ( + VRRPv2AuthNoAuth VRRPv2AuthType = 0x00 // No Authentication + VRRPv2AuthReserved1 VRRPv2AuthType = 0x01 // Reserved field 1 + VRRPv2AuthReserved2 VRRPv2AuthType = 0x02 // Reserved field 2 +) + +func (v VRRPv2AuthType) String() string { + switch v { + case VRRPv2AuthNoAuth: + return "No Authentication" + case VRRPv2AuthReserved1: + return "Reserved" + case VRRPv2AuthReserved2: + return "Reserved" + default: + return "" + } +} + +// VRRPv2 represents an VRRP v2 message. +type VRRPv2 struct { + BaseLayer + Version uint8 // The version field specifies the VRRP protocol version of this packet (v2) + Type VRRPv2Type // The type field specifies the type of this VRRP packet. The only type defined in v2 is ADVERTISEMENT + VirtualRtrID uint8 // identifies the virtual router this packet is reporting status for + Priority uint8 // specifies the sending VRRP router's priority for the virtual router (100 = default) + CountIPAddr uint8 // The number of IP addresses contained in this VRRP advertisement. + AuthType VRRPv2AuthType // identifies the authentication method being utilized + AdverInt uint8 // The Advertisement interval indicates the time interval (in seconds) between ADVERTISEMENTS. The default is 1 second + Checksum uint16 // used to detect data corruption in the VRRP message. + IPAddress []net.IP // one or more IP addresses associated with the virtual router. Specified in the CountIPAddr field. +} + +// LayerType returns LayerTypeVRRP for VRRP v2 message. +func (v *VRRPv2) LayerType() gopacket.LayerType { return LayerTypeVRRP } + +func (v *VRRPv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + + v.BaseLayer = BaseLayer{Contents: data[:len(data)]} + v.Version = data[0] >> 4 // high nibble == VRRP version. We're expecting v2 + + v.Type = VRRPv2Type(data[0] & 0x0F) // low nibble == VRRP type. Expecting 1 (advertisement) + if v.Type != 1 { + // rfc3768: A packet with unknown type MUST be discarded. + return errors.New("Unrecognized VRRPv2 type field.") + } + + v.VirtualRtrID = data[1] + v.Priority = data[2] + + v.CountIPAddr = data[3] + if v.CountIPAddr < 1 { + return errors.New("VRRPv2 number of IP addresses is not valid.") + } + + v.AuthType = VRRPv2AuthType(data[4]) + v.AdverInt = uint8(data[5]) + v.Checksum = binary.BigEndian.Uint16(data[6:8]) + + // populate the IPAddress field. The number of addresses is specified in the v.CountIPAddr field + // offset references the starting byte containing the list of ip addresses + offset := 8 + for i := uint8(0); i < v.CountIPAddr; i++ { + v.IPAddress = append(v.IPAddress, data[offset:offset+4]) + offset += 4 + } + + // any trailing packets here may be authentication data and *should* be ignored in v2 as per RFC + // + // 5.3.10. Authentication Data + // + // The authentication string is currently only used to maintain + // backwards compatibility with RFC 2338. It SHOULD be set to zero on + // transmission and ignored on reception. + return nil +} + +// CanDecode specifies the layer type in which we are attempting to unwrap. +func (v *VRRPv2) CanDecode() gopacket.LayerClass { + return LayerTypeVRRP +} + +// NextLayerType specifies the next layer that should be decoded. VRRP does not contain any further payload, so we set to 0 +func (v *VRRPv2) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// The VRRP packet does not include payload data. Setting byte slice to nil +func (v *VRRPv2) Payload() []byte { + return nil +} + +// decodeVRRP will parse VRRP v2 +func decodeVRRP(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 8 { + return errors.New("Not a valid VRRP packet. Packet length is too small.") + } + v := &VRRPv2{} + return decodingLayerDecoder(v, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/vrrp_test.go b/vendor/github.com/google/gopacket/layers/vrrp_test.go new file mode 100644 index 00000000..bc6b7e47 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/vrrp_test.go @@ -0,0 +1,55 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +package layers + +import ( + "github.com/google/gopacket" + "testing" +) + +// vrrpPacketPriority100 is the packet: +// 06:12:21.813317 IP 192.168.0.30 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 100, authtype none, intvl 1s, length 20 +// 0x0000: 0100 5e00 0012 0000 5e00 0101 0800 45c0 ..^.....^.....E. +// 0x0010: 0028 0000 0000 ff70 19cd c0a8 001e e000 .(.....p........ +// 0x0020: 0012 2101 6401 0001 ba52 c0a8 0001 0000 ..!.d....R...... +// 0x0030: 0000 0000 0000 0000 0000 0000 ............ +var vrrpPacketPriority100 = []byte{ + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x12, 0x00, 0x00, 0x5e, 0x00, 0x01, 0x01, 0x08, 0x00, 0x45, 0xc0, + 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0xff, 0x70, 0x19, 0xcd, 0xc0, 0xa8, 0x00, 0x1e, 0xe0, 0x00, + 0x00, 0x12, 0x21, 0x01, 0x64, 0x01, 0x00, 0x01, 0xba, 0x52, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +} + +func TestVRRPPacketPacket0(t *testing.T) { + p := gopacket.NewPacket(vrrpPacketPriority100, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeVRRP}, t) + + // Version=2 Type=VRRPv2 Advertisement VirtualRtrID=1 Priority=100 + vrrp := p.Layer(LayerTypeVRRP).(*VRRPv2) + if vrrp.Version != 2 { + t.Fatalf("Unable to decode VRRPv2 version. Received %d, expected %d", vrrp.Version, 2) + } + + if vrrp.Type != 1 { + t.Fatalf("Unable to decode VRRPv2 type. Received %d, expected %d", vrrp.Type, 1) + } + + if vrrp.Priority != 100 { + t.Fatalf("Unable to decode VRRPv2 priority. Received %d, expected %d", vrrp.Priority, 100) + } + + if vrrp.Checksum != 47698 { + t.Fatalf("Unable to decode VRRPv2 checksum. Received %d, expected %d", vrrp.Checksum, 47698) + } +} +func BenchmarkDecodeVRRPPacket0(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(vrrpPacketPriority100, LayerTypeEthernet, gopacket.NoCopy) + } +} diff --git a/vendor/github.com/google/gopacket/layers/vxlan.go b/vendor/github.com/google/gopacket/layers/vxlan.go new file mode 100644 index 00000000..08f1ecb4 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/vxlan.go @@ -0,0 +1,94 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +// VXLAN is specifed in RFC 7348 https://tools.ietf.org/html/rfc7348 +// G, D, A, Group Policy ID from https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// 0 8 16 24 32 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |G|R|R|R|I|R|R|R|R|D|R|R|A|R|R|R| Group Policy ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | 24 bit VXLAN Network Identifier | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// VXLAN is a VXLAN packet header +type VXLAN struct { + BaseLayer + ValidIDFlag bool // 'I' bit per RFC 7348 + VNI uint32 // 'VXLAN Network Identifier' 24 bits per RFC 7348 + GBPExtension bool // 'G' bit per Group Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 + GBPDontLearn bool // 'D' bit per Group Policy + GBPApplied bool // 'A' bit per Group Policy + GBPGroupPolicyID uint16 // 'Group Policy ID' 16 bits per Group Policy +} + +// LayerType returns LayerTypeVXLAN +func (vx *VXLAN) LayerType() gopacket.LayerType { return LayerTypeVXLAN } + +func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error { + vx := &VXLAN{} + + // VNI is a 24bit number, Uint32 requires 32 bits + var buf [4]byte + copy(buf[1:], data[4:7]) + + // RFC 7348 https://tools.ietf.org/html/rfc7348 + vx.ValidIDFlag = data[0]&0x08 > 0 // 'I' bit per RFC7348 + vx.VNI = binary.BigEndian.Uint32(buf[:]) // VXLAN Network Identifier per RFC7348 + + // Group Based Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 + vx.GBPExtension = data[0]&0x80 > 0 // 'G' bit per the group policy draft + vx.GBPDontLearn = data[1]&0x40 > 0 // 'D' bit - the egress VTEP MUST NOT learn the source address of the encapsulated frame. + vx.GBPApplied = data[1]&0x80 > 0 // 'A' bit - indicates that the group policy has already been applied to this packet. + vx.GBPGroupPolicyID = binary.BigEndian.Uint16(data[2:4]) // Policy ID as per the group policy draft + + // Layer information + const vxlanLength = 8 + vx.Contents = data[:vxlanLength] + vx.Payload = data[vxlanLength:] + + p.AddLayer(vx) + return p.NextDecoder(LinkTypeEthernet) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (vx *VXLAN) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + + if vx.ValidIDFlag { + bytes[0] |= 0x08 + } + if vx.GBPExtension { + bytes[0] |= 0x80 + } + if vx.GBPDontLearn { + bytes[1] |= 0x40 + } + if vx.GBPApplied { + bytes[1] |= 0x80 + } + + binary.BigEndian.PutUint16(bytes[2:4], vx.GBPGroupPolicyID) + if vx.VNI >= 1<<24 { + return fmt.Errorf("Virtual Network Identifier = %x exceeds max for 24-bit uint", vx.VNI) + } + binary.BigEndian.PutUint32(bytes[4:8], vx.VNI<<8) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/vxlan_test.go b/vendor/github.com/google/gopacket/layers/vxlan_test.go new file mode 100644 index 00000000..a02d9342 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/vxlan_test.go @@ -0,0 +1,108 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" + "reflect" + "testing" +) + +// VXLAN is specifed in RFC 7348 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// 0 8 16 24 32 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |G|R|R|R|I|R|R|R|R|D|R|R|A|R|R|R| Group Policy ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | 24 bit VXLAN Network Identifier | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Ethernet[IP[UDP[VXLAN[Ethernet[IP[ICMP]]]]]] + +// testPacketVXLAN +// 0000 00 16 3e 08 71 cf 36 dc 85 1e b3 40 08 00 45 00 ..>.q.6....@..E. +// 0010 00 86 d2 c0 40 00 40 11 51 52 c0 a8 cb 01 c0 a8 ....@.@.QR...... +// 0020 ca 01 b0 5d 12 b5 00 72 00 00 08 00 00 00 00 00 ...]...r........ +// 0030 00 00 00 30 88 01 00 02 00 16 3e 37 f6 04 08 00 ...0......>7.... +// 0040 45 00 00 54 00 00 40 00 40 01 23 4f c0 a8 cb 03 E..T..@.@.#O.... +// 0050 c0 a8 cb 05 08 00 f6 f2 05 0c 00 01 fc e2 97 51 ...............Q +// 0060 00 00 00 00 a6 f8 02 00 00 00 00 00 10 11 12 13 ................ +// 0070 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 ............ !"# +// 0080 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 $%&'()*+,-./0123 +// 0090 34 35 36 37 4567 ./01234567 +var testPacketVXLAN = []byte{ + 0x00, 0x16, 0x3e, 0x08, 0x71, 0xcf, 0x36, 0xdc, 0x85, 0x1e, 0xb3, 0x40, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x86, 0xd2, 0xc0, 0x40, 0x00, 0x40, 0x11, 0x51, 0x52, 0xc0, 0xa8, 0xcb, 0x01, 0xc0, 0xa8, + 0xca, 0x01, 0xb0, 0x5d, 0x12, 0xb5, 0x00, 0x72, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x30, 0x88, 0x01, 0x00, 0x02, 0x00, 0x16, 0x3e, 0x37, 0xf6, 0x04, 0x08, 0x00, + 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0x23, 0x4f, 0xc0, 0xa8, 0xcb, 0x03, + 0xc0, 0xa8, 0xcb, 0x05, 0x08, 0x00, 0xf6, 0xf2, 0x05, 0x0c, 0x00, 0x01, 0xfc, 0xe2, 0x97, 0x51, + 0x00, 0x00, 0x00, 0x00, 0xa6, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, +} + +func TestPacketVXLAN(t *testing.T) { + p := gopacket.NewPacket(testPacketVXLAN, LinkTypeEthernet, gopacket.Default) + if p.ErrorLayer() != nil { + t.Error("Failed to decode packet:", p.ErrorLayer().Error()) + } + checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeUDP, LayerTypeVXLAN, LayerTypeEthernet, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t) + if got, ok := p.Layer(LayerTypeVXLAN).(*VXLAN); ok { + want := &VXLAN{ + BaseLayer: BaseLayer{ + Contents: []byte{0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00}, + Payload: []byte{0x00, 0x30, 0x88, 0x01, 0x00, 0x02, 0x00, 0x16, 0x3e, 0x37, 0xf6, 0x04, 0x08, 0x00, + 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0x23, 0x4f, 0xc0, 0xa8, 0xcb, 0x03, + 0xc0, 0xa8, 0xcb, 0x05, 0x08, 0x00, 0xf6, 0xf2, 0x05, 0x0c, 0x00, 0x01, 0xfc, 0xe2, 0x97, 0x51, + 0x00, 0x00, 0x00, 0x00, 0xa6, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37}, + }, + ValidIDFlag: true, + VNI: 255, + GBPExtension: false, + GBPApplied: false, + GBPDontLearn: false, + GBPGroupPolicyID: 0, + } + if !reflect.DeepEqual(want, got) { + t.Errorf("VXLAN layer mismatch, \nwant %#v\ngot %#v\n", want, got) + } + } +} + +func BenchmarkDecodePacketVXLAN(b *testing.B) { + for i := 0; i < b.N; i++ { + gopacket.NewPacket(testPacketVXLAN, LinkTypeEthernet, gopacket.NoCopy) + } +} + +func TestIsomorphicPacketVXLAN(t *testing.T) { + vx := &VXLAN{ + ValidIDFlag: true, + VNI: 255, + GBPExtension: true, + GBPApplied: true, + GBPDontLearn: true, + GBPGroupPolicyID: 777, + } + + b := gopacket.NewSerializeBuffer() + vx.SerializeTo(b, gopacket.SerializeOptions{}) + + p := gopacket.NewPacket(b.Bytes(), gopacket.DecodeFunc(decodeVXLAN), gopacket.Default) + vxTranslated := p.Layer(LayerTypeVXLAN).(*VXLAN) + vxTranslated.BaseLayer = BaseLayer{} + + if !reflect.DeepEqual(vx, vxTranslated) { + t.Errorf("VXLAN isomorph mismatch, \nwant %#v\ngot %#v\n", vx, vxTranslated) + } +} diff --git a/vendor/github.com/google/gopacket/layertype.go b/vendor/github.com/google/gopacket/layertype.go new file mode 100644 index 00000000..3abfee1e --- /dev/null +++ b/vendor/github.com/google/gopacket/layertype.go @@ -0,0 +1,111 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" + "strconv" +) + +// LayerType is a unique identifier for each type of layer. This enumeration +// does not match with any externally available numbering scheme... it's solely +// usable/useful within this library as a means for requesting layer types +// (see Packet.Layer) and determining which types of layers have been decoded. +// +// New LayerTypes may be created by calling gopacket.RegisterLayerType. +type LayerType int64 + +// LayerTypeMetadata contains metadata associated with each LayerType. +type LayerTypeMetadata struct { + // Name is the string returned by each layer type's String method. + Name string + // Decoder is the decoder to use when the layer type is passed in as a + // Decoder. + Decoder Decoder +} + +type layerTypeMetadata struct { + inUse bool + LayerTypeMetadata +} + +// DecodersByLayerName maps layer names to decoders for those layers. +// This allows users to specify decoders by name to a program and have that +// program pick the correct decoder accordingly. +var DecodersByLayerName = map[string]Decoder{} + +const maxLayerType = 2000 + +var ltMeta [maxLayerType]layerTypeMetadata +var ltMetaMap = map[LayerType]layerTypeMetadata{} + +// RegisterLayerType creates a new layer type and registers it globally. +// The number passed in must be unique, or a runtime panic will occur. Numbers +// 0-999 are reserved for the gopacket library. Numbers 1000-1999 should be +// used for common application-specific types, and are very fast. Any other +// number (negative or >= 2000) may be used for uncommon application-specific +// types, and are somewhat slower (they require a map lookup over an array +// index). +func RegisterLayerType(num int, meta LayerTypeMetadata) LayerType { + if 0 <= num && num < maxLayerType { + if ltMeta[num].inUse { + panic("Layer type already exists") + } + } else { + if ltMetaMap[LayerType(num)].inUse { + panic("Layer type already exists") + } + } + return OverrideLayerType(num, meta) +} + +// OverrideLayerType acts like RegisterLayerType, except that if the layer type +// has already been registered, it overrides the metadata with the passed-in +// metadata intead of panicing. +func OverrideLayerType(num int, meta LayerTypeMetadata) LayerType { + if 0 <= num && num < maxLayerType { + ltMeta[num] = layerTypeMetadata{ + inUse: true, + LayerTypeMetadata: meta, + } + } else { + ltMetaMap[LayerType(num)] = layerTypeMetadata{ + inUse: true, + LayerTypeMetadata: meta, + } + } + DecodersByLayerName[meta.Name] = meta.Decoder + return LayerType(num) +} + +// Decode decodes the given data using the decoder registered with the layer +// type. +func (t LayerType) Decode(data []byte, c PacketBuilder) error { + var d Decoder + if 0 <= int(t) && int(t) < maxLayerType { + d = ltMeta[int(t)].Decoder + } else { + d = ltMetaMap[t].Decoder + } + if d != nil { + return d.Decode(data, c) + } + return fmt.Errorf("Layer type %v has no associated decoder", t) +} + +// String returns the string associated with this layer type. +func (t LayerType) String() (s string) { + if 0 <= int(t) && int(t) < maxLayerType { + s = ltMeta[int(t)].Name + } else { + s = ltMetaMap[t].Name + } + if s == "" { + s = strconv.Itoa(int(t)) + } + return +} diff --git a/vendor/github.com/google/gopacket/macs/benchmark_test.go b/vendor/github.com/google/gopacket/macs/benchmark_test.go new file mode 100644 index 00000000..33af5c26 --- /dev/null +++ b/vendor/github.com/google/gopacket/macs/benchmark_test.go @@ -0,0 +1,18 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package macs + +import ( + "testing" +) + +func BenchmarkCheckEthernetPrefix(b *testing.B) { + key := [3]byte{5, 5, 5} + for i := 0; i < b.N; i++ { + _ = ValidMACPrefixMap[key] + } +} diff --git a/vendor/github.com/google/gopacket/macs/doc.go b/vendor/github.com/google/gopacket/macs/doc.go new file mode 100644 index 00000000..c0d32a8c --- /dev/null +++ b/vendor/github.com/google/gopacket/macs/doc.go @@ -0,0 +1,12 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package macs provides an in-memory mapping of all valid Ethernet MAC address +// prefixes to their associated organization. +// +// The ValidMACPrefixMap map maps 3-byte prefixes to organization strings. It +// can be updated using 'go run gen.go' in this directory. +package macs diff --git a/vendor/github.com/google/gopacket/macs/gen.go b/vendor/github.com/google/gopacket/macs/gen.go new file mode 100644 index 00000000..d19e0774 --- /dev/null +++ b/vendor/github.com/google/gopacket/macs/gen.go @@ -0,0 +1,98 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build ignore + +// This binary pulls the list of known MAC +// prefixes from IEEE and writes them out to a go file which is compiled +// into gopacket. It should be run as follows: +// +// go run gen.go | gofmt > valid_mac_prefixes.go +package main + +import ( + "bufio" + "bytes" + "encoding/hex" + "flag" + "fmt" + "io" + "net/http" + "os" + "regexp" + "sort" + "time" +) + +const header = `// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package macs + +// Created by gen.go, don't edit manually +// Generated at %s +// Fetched from %q + +// ValidMACPrefixMap maps a valid MAC address prefix to the name of the +// organization that owns the rights to use it. We map it to a hidden +// variable so it won't show up in godoc, since it's a very large map. +var ValidMACPrefixMap = validMACPrefixMap +var validMACPrefixMap = map[[3]byte]string{ +` + +var url = flag.String("url", "http://standards.ieee.org/develop/regauth/oui/oui.txt", "URL to fetch MACs from") + +type mac struct { + prefix [3]byte + company string +} + +type macs []mac + +func (m macs) Len() int { return len(m) } +func (m macs) Less(i, j int) bool { return bytes.Compare(m[i].prefix[:], m[j].prefix[:]) < 0 } +func (m macs) Swap(i, j int) { m[i], m[j] = m[j], m[i] } + +func main() { + fmt.Fprintf(os.Stderr, "Fetching MACs from %q\n", *url) + resp, err := http.Get(*url) + if err != nil { + panic(err) + } + defer resp.Body.Close() + buffered := bufio.NewReader(resp.Body) + finder := regexp.MustCompile(`^\s*([0-9A-F]{6})\s+\(base 16\)\s+(.*)`) + got := macs{} + for { + line, err := buffered.ReadString('\n') + if err == io.EOF { + break + } else if err != nil { + panic(err) + } + if matches := finder.FindStringSubmatch(line); matches != nil { + var prefix [3]byte + hex.Decode(prefix[:], []byte(matches[1])) + company := matches[2] + if company == "" { + company = "PRIVATE" + } + fmt.Fprint(os.Stderr, "*") + got = append(got, mac{prefix: prefix, company: company}) + } + } + fmt.Fprintln(os.Stderr, "\nSorting macs") + sort.Sort(got) + fmt.Fprintln(os.Stderr, "Starting write to standard output") + fmt.Printf(header, time.Now(), *url) + for _, m := range got { + fmt.Printf("\t[3]byte{%d, %d, %d}: %q,\n", m.prefix[0], m.prefix[1], m.prefix[2], m.company) + } + fmt.Println("}") +} diff --git a/vendor/github.com/google/gopacket/macs/valid_mac_prefixes.go b/vendor/github.com/google/gopacket/macs/valid_mac_prefixes.go new file mode 100644 index 00000000..87ce3b5b --- /dev/null +++ b/vendor/github.com/google/gopacket/macs/valid_mac_prefixes.go @@ -0,0 +1,23000 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package macs + +// Created by gen.go, don't edit manually +// Generated at 2017-01-13 10:27:48.357683702 -0700 MST +// Fetched from "http://standards.ieee.org/develop/regauth/oui/oui.txt" + +// ValidMACPrefixMap maps a valid MAC address prefix to the name of the +// organization that owns the rights to use it. We map it to a hidden +// variable so it won't show up in godoc, since it's a very large map. +var ValidMACPrefixMap = validMACPrefixMap +var validMACPrefixMap = map[[3]byte]string{ + [3]byte{0, 0, 0}: "XEROX CORPORATION", + [3]byte{0, 0, 1}: "XEROX CORPORATION", + [3]byte{0, 0, 2}: "XEROX CORPORATION", + [3]byte{0, 0, 3}: "XEROX CORPORATION", + [3]byte{0, 0, 4}: "XEROX CORPORATION", + [3]byte{0, 0, 5}: "XEROX CORPORATION", + [3]byte{0, 0, 6}: "XEROX CORPORATION", + [3]byte{0, 0, 7}: "XEROX CORPORATION", + [3]byte{0, 0, 8}: "XEROX CORPORATION", + [3]byte{0, 0, 9}: "XEROX CORPORATION", + [3]byte{0, 0, 10}: "OMRON TATEISI ELECTRONICS CO.", + [3]byte{0, 0, 11}: "MATRIX CORPORATION", + [3]byte{0, 0, 12}: "Cisco Systems, Inc", + [3]byte{0, 0, 13}: "FIBRONICS LTD.", + [3]byte{0, 0, 14}: "FUJITSU LIMITED", + [3]byte{0, 0, 15}: "NEXT, INC.", + [3]byte{0, 0, 16}: "SYTEK INC.", + [3]byte{0, 0, 17}: "NORMEREL SYSTEMES", + [3]byte{0, 0, 18}: "INFORMATION TECHNOLOGY LIMITED", + [3]byte{0, 0, 19}: "CAMEX", + [3]byte{0, 0, 20}: "NETRONIX", + [3]byte{0, 0, 21}: "DATAPOINT CORPORATION", + [3]byte{0, 0, 22}: "DU PONT PIXEL SYSTEMS .", + [3]byte{0, 0, 23}: "Oracle", + [3]byte{0, 0, 24}: "WEBSTER COMPUTER CORPORATION", + [3]byte{0, 0, 25}: "APPLIED DYNAMICS INTERNATIONAL", + [3]byte{0, 0, 26}: "ADVANCED MICRO DEVICES", + [3]byte{0, 0, 27}: "Novell, Inc.", + [3]byte{0, 0, 28}: "BELL TECHNOLOGIES", + [3]byte{0, 0, 29}: "Cabletron Systems, Inc.", + [3]byte{0, 0, 30}: "TELSIST INDUSTRIA ELECTRONICA", + [3]byte{0, 0, 31}: "Telco Systems, Inc. ", + [3]byte{0, 0, 32}: "DATAINDUSTRIER DIAB AB", + [3]byte{0, 0, 33}: "SUREMAN COMP. & COMMUN. CORP.", + [3]byte{0, 0, 34}: "VISUAL TECHNOLOGY INC.", + [3]byte{0, 0, 35}: "ABB INDUSTRIAL SYSTEMS AB", + [3]byte{0, 0, 36}: "CONNECT AS", + [3]byte{0, 0, 37}: "RAMTEK CORP.", + [3]byte{0, 0, 38}: "SHA-KEN CO., LTD.", + [3]byte{0, 0, 39}: "JAPAN RADIO COMPANY", + [3]byte{0, 0, 40}: "PRODIGY SYSTEMS CORPORATION", + [3]byte{0, 0, 41}: "IMC NETWORKS CORP.", + [3]byte{0, 0, 42}: "TRW - SEDD/INP", + [3]byte{0, 0, 43}: "CRISP AUTOMATION, INC", + [3]byte{0, 0, 44}: "AUTOTOTE LIMITED", + [3]byte{0, 0, 45}: "CHROMATICS INC", + [3]byte{0, 0, 46}: "SOCIETE EVIRA", + [3]byte{0, 0, 47}: "TIMEPLEX INC.", + [3]byte{0, 0, 48}: "VG LABORATORY SYSTEMS LTD", + [3]byte{0, 0, 49}: "QPSX COMMUNICATIONS, LTD.", + [3]byte{0, 0, 50}: "Marconi plc", + [3]byte{0, 0, 51}: "EGAN MACHINERY COMPANY", + [3]byte{0, 0, 52}: "NETWORK RESOURCES CORPORATION", + [3]byte{0, 0, 53}: "SPECTRAGRAPHICS CORPORATION", + [3]byte{0, 0, 54}: "ATARI CORPORATION", + [3]byte{0, 0, 55}: "OXFORD METRICS LIMITED", + [3]byte{0, 0, 56}: "CSS LABS", + [3]byte{0, 0, 57}: "TOSHIBA CORPORATION", + [3]byte{0, 0, 58}: "CHYRON CORPORATION", + [3]byte{0, 0, 59}: "i Controls, Inc.", + [3]byte{0, 0, 60}: "AUSPEX SYSTEMS INC.", + [3]byte{0, 0, 61}: "UNISYS", + [3]byte{0, 0, 62}: "SIMPACT", + [3]byte{0, 0, 63}: "SYNTREX, INC.", + [3]byte{0, 0, 64}: "APPLICON, INC.", + [3]byte{0, 0, 65}: "ICE CORPORATION", + [3]byte{0, 0, 66}: "METIER MANAGEMENT SYSTEMS LTD.", + [3]byte{0, 0, 67}: "MICRO TECHNOLOGY", + [3]byte{0, 0, 68}: "CASTELLE CORPORATION", + [3]byte{0, 0, 69}: "FORD AEROSPACE & COMM. CORP.", + [3]byte{0, 0, 70}: "OLIVETTI NORTH AMERICA", + [3]byte{0, 0, 71}: "NICOLET INSTRUMENTS CORP.", + [3]byte{0, 0, 72}: "Seiko Epson Corporation", + [3]byte{0, 0, 73}: "APRICOT COMPUTERS, LTD", + [3]byte{0, 0, 74}: "ADC CODENOLL TECHNOLOGY CORP.", + [3]byte{0, 0, 75}: "ICL DATA OY", + [3]byte{0, 0, 76}: "NEC CORPORATION", + [3]byte{0, 0, 77}: "DCI CORPORATION", + [3]byte{0, 0, 78}: "AMPEX CORPORATION", + [3]byte{0, 0, 79}: "LOGICRAFT, INC.", + [3]byte{0, 0, 80}: "RADISYS CORPORATION", + [3]byte{0, 0, 81}: "HOB ELECTRONIC GMBH & CO. KG", + [3]byte{0, 0, 82}: "Intrusion.com, Inc.", + [3]byte{0, 0, 83}: "COMPUCORP", + [3]byte{0, 0, 84}: "Schneider Electric", + [3]byte{0, 0, 85}: "COMMISSARIAT A L`ENERGIE ATOM.", + [3]byte{0, 0, 86}: "DR. B. STRUCK", + [3]byte{0, 0, 87}: "SCITEX CORPORATION LTD.", + [3]byte{0, 0, 88}: "RACORE COMPUTER PRODUCTS INC.", + [3]byte{0, 0, 89}: "Hellige GMBH", + [3]byte{0, 0, 90}: "SysKonnect GmbH", + [3]byte{0, 0, 91}: "ELTEC ELEKTRONIK AG", + [3]byte{0, 0, 92}: "TELEMATICS INTERNATIONAL INC.", + [3]byte{0, 0, 93}: "CS TELECOM", + [3]byte{0, 0, 94}: "ICANN, IANA Department", + [3]byte{0, 0, 95}: "Sumitomo Electric Industries,Ltd", + [3]byte{0, 0, 96}: "KONTRON ELEKTRONIK GMBH", + [3]byte{0, 0, 97}: "GATEWAY COMMUNICATIONS", + [3]byte{0, 0, 98}: "BULL HN INFORMATION SYSTEMS", + [3]byte{0, 0, 99}: "BARCO CONTROL ROOMS GMBH", + [3]byte{0, 0, 100}: "Yokogawa Digital Computer Corporation", + [3]byte{0, 0, 101}: "Network General Corporation", + [3]byte{0, 0, 102}: "TALARIS SYSTEMS, INC.", + [3]byte{0, 0, 103}: "SOFT * RITE, INC.", + [3]byte{0, 0, 104}: "ROSEMOUNT CONTROLS", + [3]byte{0, 0, 105}: "CONCORD COMMUNICATIONS INC", + [3]byte{0, 0, 106}: "COMPUTER CONSOLES INC.", + [3]byte{0, 0, 107}: "SILICON GRAPHICS INC./MIPS", + [3]byte{0, 0, 108}: "Private", + [3]byte{0, 0, 109}: "CRAY COMMUNICATIONS, LTD.", + [3]byte{0, 0, 110}: "Artisoft Inc.", + [3]byte{0, 0, 111}: "Madge Ltd.", + [3]byte{0, 0, 112}: "HCL LIMITED", + [3]byte{0, 0, 113}: "ADRA SYSTEMS INC.", + [3]byte{0, 0, 114}: "MINIWARE TECHNOLOGY", + [3]byte{0, 0, 115}: "SIECOR CORPORATION", + [3]byte{0, 0, 116}: "RICOH COMPANY LTD.", + [3]byte{0, 0, 117}: "Nortel Networks", + [3]byte{0, 0, 118}: "ABEKAS VIDEO SYSTEM", + [3]byte{0, 0, 119}: "INTERPHASE CORPORATION", + [3]byte{0, 0, 120}: "LABTAM LIMITED", + [3]byte{0, 0, 121}: "NETWORTH INCORPORATED", + [3]byte{0, 0, 122}: "DANA COMPUTER INC.", + [3]byte{0, 0, 123}: "RESEARCH MACHINES", + [3]byte{0, 0, 124}: "AMPERE INCORPORATED", + [3]byte{0, 0, 125}: "Oracle Corporation", + [3]byte{0, 0, 126}: "CLUSTRIX CORPORATION", + [3]byte{0, 0, 127}: "LINOTYPE-HELL AG", + [3]byte{0, 0, 128}: "CRAY COMMUNICATIONS A/S", + [3]byte{0, 0, 129}: "Bay Networks", + [3]byte{0, 0, 130}: "LECTRA SYSTEMES SA", + [3]byte{0, 0, 131}: "TADPOLE TECHNOLOGY PLC", + [3]byte{0, 0, 132}: "SUPERNET", + [3]byte{0, 0, 133}: "CANON INC.", + [3]byte{0, 0, 134}: "MEGAHERTZ CORPORATION", + [3]byte{0, 0, 135}: "HITACHI, LTD.", + [3]byte{0, 0, 136}: "Brocade Communications Systems, Inc.", + [3]byte{0, 0, 137}: "CAYMAN SYSTEMS INC.", + [3]byte{0, 0, 138}: "DATAHOUSE INFORMATION SYSTEMS", + [3]byte{0, 0, 139}: "INFOTRON", + [3]byte{0, 0, 140}: "Alloy Computer Products (Australia) Pty Ltd", + [3]byte{0, 0, 141}: "Cryptek Inc.", + [3]byte{0, 0, 142}: "SOLBOURNE COMPUTER, INC.", + [3]byte{0, 0, 143}: "Raytheon", + [3]byte{0, 0, 144}: "MICROCOM", + [3]byte{0, 0, 145}: "ANRITSU CORPORATION", + [3]byte{0, 0, 146}: "COGENT DATA TECHNOLOGIES", + [3]byte{0, 0, 147}: "PROTEON INC.", + [3]byte{0, 0, 148}: "ASANTE TECHNOLOGIES", + [3]byte{0, 0, 149}: "SONY TEKTRONIX CORP.", + [3]byte{0, 0, 150}: "MARCONI ELECTRONICS LTD.", + [3]byte{0, 0, 151}: "EMC Corporation", + [3]byte{0, 0, 152}: "CROSSCOMM CORPORATION", + [3]byte{0, 0, 153}: "MTX, INC.", + [3]byte{0, 0, 154}: "RC COMPUTER A/S", + [3]byte{0, 0, 155}: "INFORMATION INTERNATIONAL, INC", + [3]byte{0, 0, 156}: "ROLM MIL-SPEC COMPUTERS", + [3]byte{0, 0, 157}: "LOCUS COMPUTING CORPORATION", + [3]byte{0, 0, 158}: "MARLI S.A.", + [3]byte{0, 0, 159}: "AMERISTAR TECHNOLOGIES INC.", + [3]byte{0, 0, 160}: "SANYO Electric Co., Ltd.", + [3]byte{0, 0, 161}: "MARQUETTE ELECTRIC CO.", + [3]byte{0, 0, 162}: "Bay Networks", + [3]byte{0, 0, 163}: "NETWORK APPLICATION TECHNOLOGY", + [3]byte{0, 0, 164}: "ACORN COMPUTERS LIMITED", + [3]byte{0, 0, 165}: "Tattile SRL ", + [3]byte{0, 0, 166}: "NETWORK GENERAL CORPORATION", + [3]byte{0, 0, 167}: "NETWORK COMPUTING DEVICES INC.", + [3]byte{0, 0, 168}: "STRATUS COMPUTER INC.", + [3]byte{0, 0, 169}: "NETWORK SYSTEMS CORP.", + [3]byte{0, 0, 170}: "XEROX CORPORATION", + [3]byte{0, 0, 171}: "LOGIC MODELING CORPORATION", + [3]byte{0, 0, 172}: "CONWARE COMPUTER CONSULTING", + [3]byte{0, 0, 173}: "BRUKER INSTRUMENTS INC.", + [3]byte{0, 0, 174}: "DASSAULT ELECTRONIQUE", + [3]byte{0, 0, 175}: "Canberra Industries, Inc.", + [3]byte{0, 0, 176}: "RND-RAD NETWORK DEVICES", + [3]byte{0, 0, 177}: "Alpha Micro", + [3]byte{0, 0, 178}: "TELEVIDEO SYSTEMS, INC.", + [3]byte{0, 0, 179}: "CIMLINC INCORPORATED", + [3]byte{0, 0, 180}: "Edimax Technology Co. Ltd.", + [3]byte{0, 0, 181}: "DATABILITY SOFTWARE SYS. INC.", + [3]byte{0, 0, 182}: "MICRO-MATIC RESEARCH", + [3]byte{0, 0, 183}: "DOVE COMPUTER CORPORATION", + [3]byte{0, 0, 184}: "SEIKOSHA CO., LTD.", + [3]byte{0, 0, 185}: "MCDONNELL DOUGLAS COMPUTER SYS", + [3]byte{0, 0, 186}: "SIIG, INC.", + [3]byte{0, 0, 187}: "TRI-DATA", + [3]byte{0, 0, 188}: "Rockwell Automation", + [3]byte{0, 0, 189}: "MITSUBISHI CABLE COMPANY", + [3]byte{0, 0, 190}: "THE NTI GROUP", + [3]byte{0, 0, 191}: "SYMMETRIC COMPUTER SYSTEMS", + [3]byte{0, 0, 192}: "WESTERN DIGITAL CORPORATION", + [3]byte{0, 0, 193}: "Madge Ltd.", + [3]byte{0, 0, 194}: "INFORMATION PRESENTATION TECH.", + [3]byte{0, 0, 195}: "HARRIS CORP COMPUTER SYS DIV", + [3]byte{0, 0, 196}: "WATERS DIV. OF MILLIPORE", + [3]byte{0, 0, 197}: "ARRIS Group, Inc.", + [3]byte{0, 0, 198}: "EON SYSTEMS", + [3]byte{0, 0, 199}: "ARIX CORPORATION", + [3]byte{0, 0, 200}: "ALTOS COMPUTER SYSTEMS", + [3]byte{0, 0, 201}: "Emulex Corporation", + [3]byte{0, 0, 202}: "ARRIS Group, Inc.", + [3]byte{0, 0, 203}: "COMPU-SHACK ELECTRONIC GMBH", + [3]byte{0, 0, 204}: "DENSAN CO., LTD.", + [3]byte{0, 0, 205}: "Allied Telesis Labs Ltd", + [3]byte{0, 0, 206}: "MEGADATA CORP.", + [3]byte{0, 0, 207}: "HAYES MICROCOMPUTER PRODUCTS", + [3]byte{0, 0, 208}: "DEVELCON ELECTRONICS LTD.", + [3]byte{0, 0, 209}: "ADAPTEC INCORPORATED", + [3]byte{0, 0, 210}: "SBE, INC.", + [3]byte{0, 0, 211}: "WANG LABORATORIES INC.", + [3]byte{0, 0, 212}: "PURE DATA LTD.", + [3]byte{0, 0, 213}: "MICROGNOSIS INTERNATIONAL", + [3]byte{0, 0, 214}: "PUNCH LINE HOLDING", + [3]byte{0, 0, 215}: "DARTMOUTH COLLEGE", + [3]byte{0, 0, 216}: "Novell, Inc.", + [3]byte{0, 0, 217}: "NIPPON TELEGRAPH & TELEPHONE", + [3]byte{0, 0, 218}: "ATEX", + [3]byte{0, 0, 219}: "British Telecommunications plc", + [3]byte{0, 0, 220}: "HAYES MICROCOMPUTER PRODUCTS", + [3]byte{0, 0, 221}: "TCL INCORPORATED", + [3]byte{0, 0, 222}: "CETIA", + [3]byte{0, 0, 223}: "BELL & HOWELL PUB SYS DIV", + [3]byte{0, 0, 224}: "QUADRAM CORP.", + [3]byte{0, 0, 225}: "GRID SYSTEMS", + [3]byte{0, 0, 226}: "ACER TECHNOLOGIES CORP.", + [3]byte{0, 0, 227}: "INTEGRATED MICRO PRODUCTS LTD", + [3]byte{0, 0, 228}: "IN2 GROUPE INTERTECHNIQUE", + [3]byte{0, 0, 229}: "SIGMEX LTD.", + [3]byte{0, 0, 230}: "APTOR PRODUITS DE COMM INDUST", + [3]byte{0, 0, 231}: "Star Gate Technologies", + [3]byte{0, 0, 232}: "ACCTON TECHNOLOGY CORP.", + [3]byte{0, 0, 233}: "ISICAD, INC.", + [3]byte{0, 0, 234}: "UPNOD AB", + [3]byte{0, 0, 235}: "MATSUSHITA COMM. IND. CO. LTD.", + [3]byte{0, 0, 236}: "MICROPROCESS", + [3]byte{0, 0, 237}: "APRIL", + [3]byte{0, 0, 238}: "NETWORK DESIGNERS, LTD.", + [3]byte{0, 0, 239}: "KTI", + [3]byte{0, 0, 240}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 0, 241}: "MAGNA COMPUTER CORPORATION", + [3]byte{0, 0, 242}: "SPIDER COMMUNICATIONS", + [3]byte{0, 0, 243}: "GANDALF DATA LIMITED", + [3]byte{0, 0, 244}: "Allied Telesis, Inc.", + [3]byte{0, 0, 245}: "DIAMOND SALES LIMITED", + [3]byte{0, 0, 246}: "APPLIED MICROSYSTEMS CORP.", + [3]byte{0, 0, 247}: "YOUTH KEEP ENTERPRISE CO LTD", + [3]byte{0, 0, 248}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{0, 0, 249}: "QUOTRON SYSTEMS INC.", + [3]byte{0, 0, 250}: "MICROSAGE COMPUTER SYSTEMS INC", + [3]byte{0, 0, 251}: "RECHNER ZUR KOMMUNIKATION", + [3]byte{0, 0, 252}: "MEIKO", + [3]byte{0, 0, 253}: "HIGH LEVEL HARDWARE", + [3]byte{0, 0, 254}: "ANNAPOLIS MICRO SYSTEMS", + [3]byte{0, 0, 255}: "CAMTEC ELECTRONICS LTD.", + [3]byte{0, 1, 0}: "EQUIP'TRANS", + [3]byte{0, 1, 1}: "Private", + [3]byte{0, 1, 2}: "3COM CORPORATION", + [3]byte{0, 1, 3}: "3COM CORPORATION", + [3]byte{0, 1, 4}: "DVICO Co., Ltd.", + [3]byte{0, 1, 5}: "Beckhoff Automation GmbH", + [3]byte{0, 1, 6}: "Tews Datentechnik GmbH", + [3]byte{0, 1, 7}: "Leiser GmbH", + [3]byte{0, 1, 8}: "AVLAB Technology, Inc.", + [3]byte{0, 1, 9}: "Nagano Japan Radio Co., Ltd.", + [3]byte{0, 1, 10}: "CIS TECHNOLOGY INC.", + [3]byte{0, 1, 11}: "Space CyberLink, Inc.", + [3]byte{0, 1, 12}: "System Talks Inc.", + [3]byte{0, 1, 13}: "Teledyne DALSA Inc.", + [3]byte{0, 1, 14}: "Bri-Link Technologies Co., Ltd", + [3]byte{0, 1, 15}: "Brocade Communications Systems, Inc.", + [3]byte{0, 1, 16}: "Gotham Networks", + [3]byte{0, 1, 17}: "iDigm Inc.", + [3]byte{0, 1, 18}: "Shark Multimedia Inc.", + [3]byte{0, 1, 19}: "OLYMPUS CORPORATION", + [3]byte{0, 1, 20}: "KANDA TSUSHIN KOGYO CO., LTD.", + [3]byte{0, 1, 21}: "EXTRATECH CORPORATION", + [3]byte{0, 1, 22}: "Netspect Technologies, Inc.", + [3]byte{0, 1, 23}: "Canal +", + [3]byte{0, 1, 24}: "EZ Digital Co., Ltd.", + [3]byte{0, 1, 25}: "RTUnet (Australia)", + [3]byte{0, 1, 26}: "Hoffmann und Burmeister GbR", + [3]byte{0, 1, 27}: "Unizone Technologies, Inc.", + [3]byte{0, 1, 28}: "Universal Talkware Corporation", + [3]byte{0, 1, 29}: "Centillium Communications", + [3]byte{0, 1, 30}: "Precidia Technologies, Inc.", + [3]byte{0, 1, 31}: "RC Networks, Inc.", + [3]byte{0, 1, 32}: "OSCILLOQUARTZ S.A.", + [3]byte{0, 1, 33}: "Watchguard Technologies, Inc.", + [3]byte{0, 1, 34}: "Trend Communications, Ltd.", + [3]byte{0, 1, 35}: "DIGITAL ELECTRONICS CORP.", + [3]byte{0, 1, 36}: "Acer Incorporated", + [3]byte{0, 1, 37}: "YAESU MUSEN CO., LTD.", + [3]byte{0, 1, 38}: "PAC Labs", + [3]byte{0, 1, 39}: "OPEN Networks Pty Ltd", + [3]byte{0, 1, 40}: "EnjoyWeb, Inc.", + [3]byte{0, 1, 41}: "DFI Inc.", + [3]byte{0, 1, 42}: "Telematica Sistems Inteligente", + [3]byte{0, 1, 43}: "TELENET Co., Ltd.", + [3]byte{0, 1, 44}: "Aravox Technologies, Inc.", + [3]byte{0, 1, 45}: "Komodo Technology", + [3]byte{0, 1, 46}: "PC Partner Ltd.", + [3]byte{0, 1, 47}: "Twinhead International Corp", + [3]byte{0, 1, 48}: "Extreme Networks", + [3]byte{0, 1, 49}: "Bosch Security Systems, Inc.", + [3]byte{0, 1, 50}: "Dranetz - BMI", + [3]byte{0, 1, 51}: "KYOWA Electronic Instruments C", + [3]byte{0, 1, 52}: "Selectron Systems AG", + [3]byte{0, 1, 53}: "KDC Corp.", + [3]byte{0, 1, 54}: "CyberTAN Technology Inc.", + [3]byte{0, 1, 55}: "IT Farm Corporation", + [3]byte{0, 1, 56}: "XAVi Technologies Corp.", + [3]byte{0, 1, 57}: "Point Multimedia Systems", + [3]byte{0, 1, 58}: "SHELCAD COMMUNICATIONS, LTD.", + [3]byte{0, 1, 59}: "BNA SYSTEMS", + [3]byte{0, 1, 60}: "TIW SYSTEMS", + [3]byte{0, 1, 61}: "RiscStation Ltd.", + [3]byte{0, 1, 62}: "Ascom Tateco AB", + [3]byte{0, 1, 63}: "Neighbor World Co., Ltd.", + [3]byte{0, 1, 64}: "Sendtek Corporation", + [3]byte{0, 1, 65}: "CABLE PRINT", + [3]byte{0, 1, 66}: "Cisco Systems, Inc", + [3]byte{0, 1, 67}: "Cisco Systems, Inc", + [3]byte{0, 1, 68}: "EMC Corporation", + [3]byte{0, 1, 69}: "WINSYSTEMS, INC.", + [3]byte{0, 1, 70}: "Tesco Controls, Inc.", + [3]byte{0, 1, 71}: "Zhone Technologies", + [3]byte{0, 1, 72}: "X-traWeb Inc.", + [3]byte{0, 1, 73}: "T.D.T. Transfer Data Test GmbH", + [3]byte{0, 1, 74}: "Sony Corporation", + [3]byte{0, 1, 75}: "Ennovate Networks, Inc.", + [3]byte{0, 1, 76}: "Berkeley Process Control", + [3]byte{0, 1, 77}: "Shin Kin Enterprises Co., Ltd", + [3]byte{0, 1, 78}: "WIN Enterprises, Inc.", + [3]byte{0, 1, 79}: "Adtran Inc", + [3]byte{0, 1, 80}: "GILAT COMMUNICATIONS, LTD.", + [3]byte{0, 1, 81}: "Ensemble Communications", + [3]byte{0, 1, 82}: "CHROMATEK INC.", + [3]byte{0, 1, 83}: "ARCHTEK TELECOM CORPORATION", + [3]byte{0, 1, 84}: "G3M Corporation", + [3]byte{0, 1, 85}: "Promise Technology, Inc.", + [3]byte{0, 1, 86}: "FIREWIREDIRECT.COM, INC.", + [3]byte{0, 1, 87}: "SYSWAVE CO., LTD", + [3]byte{0, 1, 88}: "Electro Industries/Gauge Tech", + [3]byte{0, 1, 89}: "S1 Corporation", + [3]byte{0, 1, 90}: "Digital Video Broadcasting", + [3]byte{0, 1, 91}: "ITALTEL S.p.A/RF-UP-I", + [3]byte{0, 1, 92}: "CADANT INC.", + [3]byte{0, 1, 93}: "Oracle Corporation ", + [3]byte{0, 1, 94}: "BEST TECHNOLOGY CO., LTD.", + [3]byte{0, 1, 95}: "DIGITAL DESIGN GmbH", + [3]byte{0, 1, 96}: "ELMEX Co., LTD.", + [3]byte{0, 1, 97}: "Meta Machine Technology", + [3]byte{0, 1, 98}: "Cygnet Technologies, Inc.", + [3]byte{0, 1, 99}: "Cisco Systems, Inc", + [3]byte{0, 1, 100}: "Cisco Systems, Inc", + [3]byte{0, 1, 101}: "AirSwitch Corporation", + [3]byte{0, 1, 102}: "TC GROUP A/S", + [3]byte{0, 1, 103}: "HIOKI E.E. CORPORATION", + [3]byte{0, 1, 104}: "VITANA CORPORATION", + [3]byte{0, 1, 105}: "Celestix Networks Pte Ltd.", + [3]byte{0, 1, 106}: "ALITEC", + [3]byte{0, 1, 107}: "LightChip, Inc.", + [3]byte{0, 1, 108}: "FOXCONN", + [3]byte{0, 1, 109}: "CarrierComm Inc.", + [3]byte{0, 1, 110}: "Conklin Corporation", + [3]byte{0, 1, 111}: "Inkel Corp.", + [3]byte{0, 1, 112}: "ESE Embedded System Engineer'g", + [3]byte{0, 1, 113}: "Allied Data Technologies", + [3]byte{0, 1, 114}: "TechnoLand Co., LTD.", + [3]byte{0, 1, 115}: "AMCC", + [3]byte{0, 1, 116}: "CyberOptics Corporation", + [3]byte{0, 1, 117}: "Radiant Communications Corp.", + [3]byte{0, 1, 118}: "Orient Silver Enterprises", + [3]byte{0, 1, 119}: "EDSL", + [3]byte{0, 1, 120}: "MARGI Systems, Inc.", + [3]byte{0, 1, 121}: "WIRELESS TECHNOLOGY, INC.", + [3]byte{0, 1, 122}: "Chengdu Maipu Electric Industrial Co., Ltd.", + [3]byte{0, 1, 123}: "Heidelberger Druckmaschinen AG", + [3]byte{0, 1, 124}: "AG-E GmbH", + [3]byte{0, 1, 125}: "ThermoQuest", + [3]byte{0, 1, 126}: "ADTEK System Science Co., Ltd.", + [3]byte{0, 1, 127}: "Experience Music Project", + [3]byte{0, 1, 128}: "AOpen, Inc.", + [3]byte{0, 1, 129}: "Nortel Networks", + [3]byte{0, 1, 130}: "DICA TECHNOLOGIES AG", + [3]byte{0, 1, 131}: "ANITE TELECOMS", + [3]byte{0, 1, 132}: "SIEB & MEYER AG", + [3]byte{0, 1, 133}: "Hitachi Aloka Medical, Ltd.", + [3]byte{0, 1, 134}: "Uwe Disch", + [3]byte{0, 1, 135}: "I2SE GmbH", + [3]byte{0, 1, 136}: "LXCO Technologies ag", + [3]byte{0, 1, 137}: "Refraction Technology, Inc.", + [3]byte{0, 1, 138}: "ROI COMPUTER AG", + [3]byte{0, 1, 139}: "NetLinks Co., Ltd.", + [3]byte{0, 1, 140}: "Mega Vision", + [3]byte{0, 1, 141}: "AudeSi Technologies", + [3]byte{0, 1, 142}: "Logitec Corporation", + [3]byte{0, 1, 143}: "Kenetec, Inc.", + [3]byte{0, 1, 144}: "SMK-M", + [3]byte{0, 1, 145}: "SYRED Data Systems", + [3]byte{0, 1, 146}: "Texas Digital Systems", + [3]byte{0, 1, 147}: "Hanbyul Telecom Co., Ltd.", + [3]byte{0, 1, 148}: "Capital Equipment Corporation", + [3]byte{0, 1, 149}: "Sena Technologies, Inc.", + [3]byte{0, 1, 150}: "Cisco Systems, Inc", + [3]byte{0, 1, 151}: "Cisco Systems, Inc", + [3]byte{0, 1, 152}: "Darim Vision", + [3]byte{0, 1, 153}: "HeiSei Electronics", + [3]byte{0, 1, 154}: "LEUNIG GmbH", + [3]byte{0, 1, 155}: "Kyoto Microcomputer Co., Ltd.", + [3]byte{0, 1, 156}: "JDS Uniphase Inc.", + [3]byte{0, 1, 157}: "E-Control Systems, Inc.", + [3]byte{0, 1, 158}: "ESS Technology, Inc.", + [3]byte{0, 1, 159}: "ReadyNet", + [3]byte{0, 1, 160}: "Infinilink Corporation", + [3]byte{0, 1, 161}: "Mag-Tek, Inc.", + [3]byte{0, 1, 162}: "Logical Co., Ltd.", + [3]byte{0, 1, 163}: "GENESYS LOGIC, INC.", + [3]byte{0, 1, 164}: "Microlink Corporation", + [3]byte{0, 1, 165}: "Nextcomm, Inc.", + [3]byte{0, 1, 166}: "Scientific-Atlanta Arcodan A/S", + [3]byte{0, 1, 167}: "UNEX TECHNOLOGY CORPORATION", + [3]byte{0, 1, 168}: "Welltech Computer Co., Ltd.", + [3]byte{0, 1, 169}: "BMW AG", + [3]byte{0, 1, 170}: "Airspan Communications, Ltd.", + [3]byte{0, 1, 171}: "Main Street Networks", + [3]byte{0, 1, 172}: "Sitara Networks, Inc.", + [3]byte{0, 1, 173}: "Coach Master International d.b.a. CMI Worldwide, Inc.", + [3]byte{0, 1, 174}: "Trex Enterprises", + [3]byte{0, 1, 175}: "Artesyn Embedded Technologies", + [3]byte{0, 1, 176}: "Fulltek Technology Co., Ltd.", + [3]byte{0, 1, 177}: "General Bandwidth", + [3]byte{0, 1, 178}: "Digital Processing Systems, Inc.", + [3]byte{0, 1, 179}: "Precision Electronic Manufacturing", + [3]byte{0, 1, 180}: "Wayport, Inc.", + [3]byte{0, 1, 181}: "Turin Networks, Inc.", + [3]byte{0, 1, 182}: "SAEJIN T&M Co., Ltd.", + [3]byte{0, 1, 183}: "Centos, Inc.", + [3]byte{0, 1, 184}: "Netsensity, Inc.", + [3]byte{0, 1, 185}: "SKF Condition Monitoring", + [3]byte{0, 1, 186}: "IC-Net, Inc.", + [3]byte{0, 1, 187}: "Frequentis", + [3]byte{0, 1, 188}: "Brains Corporation", + [3]byte{0, 1, 189}: "Peterson Electro-Musical Products, Inc.", + [3]byte{0, 1, 190}: "Gigalink Co., Ltd.", + [3]byte{0, 1, 191}: "Teleforce Co., Ltd.", + [3]byte{0, 1, 192}: "CompuLab, Ltd.", + [3]byte{0, 1, 193}: "Vitesse Semiconductor Corporation", + [3]byte{0, 1, 194}: "ARK Research Corp.", + [3]byte{0, 1, 195}: "Acromag, Inc.", + [3]byte{0, 1, 196}: "NeoWave, Inc.", + [3]byte{0, 1, 197}: "Simpler Networks", + [3]byte{0, 1, 198}: "Quarry Technologies", + [3]byte{0, 1, 199}: "Cisco Systems, Inc", + [3]byte{0, 1, 200}: "CONRAD CORP.", + [3]byte{0, 1, 200}: "THOMAS CONRAD CORP.", + [3]byte{0, 1, 201}: "Cisco Systems, Inc", + [3]byte{0, 1, 202}: "Geocast Network Systems, Inc.", + [3]byte{0, 1, 203}: "EVR", + [3]byte{0, 1, 204}: "Japan Total Design Communication Co., Ltd.", + [3]byte{0, 1, 205}: "ARtem", + [3]byte{0, 1, 206}: "Custom Micro Products, Ltd.", + [3]byte{0, 1, 207}: "Alpha Data Parallel Systems, Ltd.", + [3]byte{0, 1, 208}: "VitalPoint, Inc.", + [3]byte{0, 1, 209}: "CoNet Communications, Inc.", + [3]byte{0, 1, 210}: "inXtron, Inc. ", + [3]byte{0, 1, 211}: "PAXCOMM, Inc.", + [3]byte{0, 1, 212}: "Leisure Time, Inc.", + [3]byte{0, 1, 213}: "HAEDONG INFO & COMM CO., LTD", + [3]byte{0, 1, 214}: "manroland AG", + [3]byte{0, 1, 215}: "F5 Networks, Inc.", + [3]byte{0, 1, 216}: "Teltronics, Inc.", + [3]byte{0, 1, 217}: "Sigma, Inc.", + [3]byte{0, 1, 218}: "WINCOMM Corporation", + [3]byte{0, 1, 219}: "Freecom Technologies GmbH", + [3]byte{0, 1, 220}: "Activetelco", + [3]byte{0, 1, 221}: "Avail Networks", + [3]byte{0, 1, 222}: "Trango Systems, Inc.", + [3]byte{0, 1, 223}: "ISDN Communications, Ltd.", + [3]byte{0, 1, 224}: "Fast Systems, Inc.", + [3]byte{0, 1, 225}: "Kinpo Electronics, Inc.", + [3]byte{0, 1, 226}: "Ando Electric Corporation", + [3]byte{0, 1, 227}: "Siemens AG", + [3]byte{0, 1, 228}: "Sitera, Inc.", + [3]byte{0, 1, 229}: "Supernet, Inc.", + [3]byte{0, 1, 230}: "Hewlett Packard", + [3]byte{0, 1, 231}: "Hewlett Packard", + [3]byte{0, 1, 232}: "Force10 Networks, Inc.", + [3]byte{0, 1, 233}: "Litton Marine Systems B.V.", + [3]byte{0, 1, 234}: "Cirilium Corp.", + [3]byte{0, 1, 235}: "C-COM Corporation", + [3]byte{0, 1, 236}: "Ericsson Group", + [3]byte{0, 1, 237}: "SETA Corp.", + [3]byte{0, 1, 238}: "Comtrol Europe, Ltd.", + [3]byte{0, 1, 239}: "Camtel Technology Corp.", + [3]byte{0, 1, 240}: "Tridium, Inc.", + [3]byte{0, 1, 241}: "Innovative Concepts, Inc.", + [3]byte{0, 1, 242}: "Mark of the Unicorn, Inc.", + [3]byte{0, 1, 243}: "QPS, Inc.", + [3]byte{0, 1, 244}: "Enterasys", + [3]byte{0, 1, 245}: "ERIM S.A.", + [3]byte{0, 1, 246}: "Association of Musical Electronics Industry", + [3]byte{0, 1, 247}: "Image Display Systems, Inc.", + [3]byte{0, 1, 248}: "TEXIO TECHNOLOGY CORPORATION", + [3]byte{0, 1, 249}: "TeraGlobal Communications Corp.", + [3]byte{0, 1, 250}: "HOROSCAS", + [3]byte{0, 1, 251}: "DoTop Technology, Inc.", + [3]byte{0, 1, 252}: "Keyence Corporation", + [3]byte{0, 1, 253}: "Digital Voice Systems, Inc.", + [3]byte{0, 1, 254}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{0, 1, 255}: "Data Direct Networks, Inc.", + [3]byte{0, 2, 0}: "Net & Sys Co., Ltd.", + [3]byte{0, 2, 1}: "IFM Electronic gmbh", + [3]byte{0, 2, 2}: "Amino Communications, Ltd.", + [3]byte{0, 2, 3}: "Woonsang Telecom, Inc.", + [3]byte{0, 2, 4}: "Bodmann Industries Elektronik GmbH", + [3]byte{0, 2, 5}: "Hitachi Denshi, Ltd.", + [3]byte{0, 2, 6}: "Telital R&D Denmark A/S", + [3]byte{0, 2, 7}: "VisionGlobal Network Corp.", + [3]byte{0, 2, 8}: "Unify Networks, Inc.", + [3]byte{0, 2, 9}: "Shenzhen SED Information Technology Co., Ltd.", + [3]byte{0, 2, 10}: "Gefran Spa", + [3]byte{0, 2, 11}: "Native Networks, Inc.", + [3]byte{0, 2, 12}: "Metro-Optix", + [3]byte{0, 2, 13}: "Micronpc.com", + [3]byte{0, 2, 14}: "ECI Telecom Ltd.", + [3]byte{0, 2, 15}: "AATR", + [3]byte{0, 2, 16}: "Fenecom", + [3]byte{0, 2, 17}: "Nature Worldwide Technology Corp.", + [3]byte{0, 2, 18}: "SierraCom", + [3]byte{0, 2, 19}: "S.D.E.L.", + [3]byte{0, 2, 20}: "DTVRO", + [3]byte{0, 2, 21}: "Cotas Computer Technology A/B", + [3]byte{0, 2, 22}: "Cisco Systems, Inc", + [3]byte{0, 2, 23}: "Cisco Systems, Inc", + [3]byte{0, 2, 24}: "Advanced Scientific Corp", + [3]byte{0, 2, 25}: "Paralon Technologies", + [3]byte{0, 2, 26}: "Zuma Networks", + [3]byte{0, 2, 27}: "Kollmorgen-Servotronix", + [3]byte{0, 2, 28}: "Network Elements, Inc.", + [3]byte{0, 2, 29}: "Data General Communication Ltd.", + [3]byte{0, 2, 30}: "SIMTEL S.R.L.", + [3]byte{0, 2, 31}: "Aculab PLC", + [3]byte{0, 2, 32}: "CANON FINETECH INC.", + [3]byte{0, 2, 33}: "DSP Application, Ltd.", + [3]byte{0, 2, 34}: "Chromisys, Inc.", + [3]byte{0, 2, 35}: "ClickTV", + [3]byte{0, 2, 36}: "C-COR", + [3]byte{0, 2, 37}: "One Stop Systems", + [3]byte{0, 2, 38}: "XESystems, Inc.", + [3]byte{0, 2, 39}: "ESD Electronic System Design GmbH", + [3]byte{0, 2, 40}: "Necsom, Ltd.", + [3]byte{0, 2, 41}: "Adtec Corporation", + [3]byte{0, 2, 42}: "Asound Electronic", + [3]byte{0, 2, 43}: "SAXA, Inc.", + [3]byte{0, 2, 44}: "ABB Bomem, Inc.", + [3]byte{0, 2, 45}: "Agere Systems", + [3]byte{0, 2, 46}: "TEAC Corp. R& D", + [3]byte{0, 2, 47}: "P-Cube, Ltd.", + [3]byte{0, 2, 48}: "Intersoft Electronics", + [3]byte{0, 2, 49}: "Ingersoll-Rand", + [3]byte{0, 2, 50}: "Avision, Inc.", + [3]byte{0, 2, 51}: "Mantra Communications, Inc.", + [3]byte{0, 2, 52}: "Imperial Technology, Inc.", + [3]byte{0, 2, 53}: "Paragon Networks International", + [3]byte{0, 2, 54}: "INIT GmbH", + [3]byte{0, 2, 55}: "Cosmo Research Corp.", + [3]byte{0, 2, 56}: "Serome Technology, Inc.", + [3]byte{0, 2, 57}: "Visicom", + [3]byte{0, 2, 58}: "ZSK Stickmaschinen GmbH", + [3]byte{0, 2, 59}: "Ericsson", + [3]byte{0, 2, 60}: "Creative Technology, Ltd.", + [3]byte{0, 2, 61}: "Cisco Systems, Inc", + [3]byte{0, 2, 62}: "Selta Telematica S.p.a", + [3]byte{0, 2, 63}: "COMPAL ELECTRONICS, INC.", + [3]byte{0, 2, 64}: "Seedek Co., Ltd.", + [3]byte{0, 2, 65}: "Amer.com", + [3]byte{0, 2, 66}: "Videoframe Systems", + [3]byte{0, 2, 67}: "Raysis Co., Ltd.", + [3]byte{0, 2, 68}: "SURECOM Technology Co.", + [3]byte{0, 2, 69}: "Lampus Co, Ltd.", + [3]byte{0, 2, 70}: "All-Win Tech Co., Ltd.", + [3]byte{0, 2, 71}: "Great Dragon Information Technology (Group) Co., Ltd.", + [3]byte{0, 2, 72}: "Pilz GmbH & Co.", + [3]byte{0, 2, 73}: "Aviv Infocom Co, Ltd.", + [3]byte{0, 2, 74}: "Cisco Systems, Inc", + [3]byte{0, 2, 75}: "Cisco Systems, Inc", + [3]byte{0, 2, 76}: "SiByte, Inc.", + [3]byte{0, 2, 77}: "Mannesman Dematic Colby Pty. Ltd.", + [3]byte{0, 2, 78}: "Datacard Group", + [3]byte{0, 2, 79}: "IPM Datacom S.R.L.", + [3]byte{0, 2, 80}: "Geyser Networks, Inc.", + [3]byte{0, 2, 81}: "Soma Networks, Inc.", + [3]byte{0, 2, 82}: "Carrier Corporation", + [3]byte{0, 2, 83}: "Televideo, Inc.", + [3]byte{0, 2, 84}: "WorldGate", + [3]byte{0, 2, 85}: "IBM Corp", + [3]byte{0, 2, 86}: "Alpha Processor, Inc.", + [3]byte{0, 2, 87}: "Microcom Corp.", + [3]byte{0, 2, 88}: "Flying Packets Communications", + [3]byte{0, 2, 89}: "Tsann Kuen China (Shanghai)Enterprise Co., Ltd. IT Group", + [3]byte{0, 2, 90}: "Catena Networks", + [3]byte{0, 2, 91}: "Cambridge Silicon Radio", + [3]byte{0, 2, 92}: "SCI Systems (Kunshan) Co., Ltd.", + [3]byte{0, 2, 93}: "Calix Networks", + [3]byte{0, 2, 94}: "High Technology Ltd", + [3]byte{0, 2, 95}: "Nortel Networks", + [3]byte{0, 2, 96}: "Accordion Networks, Inc.", + [3]byte{0, 2, 97}: "Tilgin AB", + [3]byte{0, 2, 98}: "Soyo Group Soyo Com Tech Co., Ltd", + [3]byte{0, 2, 99}: "UPS Manufacturing SRL", + [3]byte{0, 2, 100}: "AudioRamp.com", + [3]byte{0, 2, 101}: "Virditech Co. Ltd.", + [3]byte{0, 2, 102}: "Thermalogic Corporation", + [3]byte{0, 2, 103}: "NODE RUNNER, INC.", + [3]byte{0, 2, 104}: "Harris Government Communications", + [3]byte{0, 2, 105}: "Nadatel Co., Ltd", + [3]byte{0, 2, 106}: "Cocess Telecom Co., Ltd.", + [3]byte{0, 2, 107}: "BCM Computers Co., Ltd.", + [3]byte{0, 2, 108}: "Philips CFT", + [3]byte{0, 2, 109}: "Adept Telecom", + [3]byte{0, 2, 110}: "NeGeN Access, Inc.", + [3]byte{0, 2, 111}: "Senao International Co., Ltd.", + [3]byte{0, 2, 112}: "Crewave Co., Ltd.", + [3]byte{0, 2, 113}: "Zhone Technologies", + [3]byte{0, 2, 114}: "CC&C Technologies, Inc.", + [3]byte{0, 2, 115}: "Coriolis Networks", + [3]byte{0, 2, 116}: "Tommy Technologies Corp.", + [3]byte{0, 2, 117}: "SMART Technologies, Inc.", + [3]byte{0, 2, 118}: "Primax Electronics Ltd.", + [3]byte{0, 2, 119}: "Cash Systemes Industrie", + [3]byte{0, 2, 120}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{0, 2, 121}: "Control Applications, Ltd.", + [3]byte{0, 2, 122}: "IOI Technology Corporation", + [3]byte{0, 2, 123}: "Amplify Net, Inc.", + [3]byte{0, 2, 124}: "Trilithic, Inc.", + [3]byte{0, 2, 125}: "Cisco Systems, Inc", + [3]byte{0, 2, 126}: "Cisco Systems, Inc", + [3]byte{0, 2, 127}: "ask-technologies.com", + [3]byte{0, 2, 128}: "Mu Net, Inc.", + [3]byte{0, 2, 129}: "Madge Ltd.", + [3]byte{0, 2, 130}: "ViaClix, Inc.", + [3]byte{0, 2, 131}: "Spectrum Controls, Inc.", + [3]byte{0, 2, 132}: "AREVA T&D", + [3]byte{0, 2, 133}: "Riverstone Networks", + [3]byte{0, 2, 134}: "Occam Networks", + [3]byte{0, 2, 135}: "Adapcom", + [3]byte{0, 2, 136}: "GLOBAL VILLAGE COMMUNICATION", + [3]byte{0, 2, 137}: "DNE Technologies", + [3]byte{0, 2, 138}: "Ambit Microsystems Corporation", + [3]byte{0, 2, 139}: "VDSL Systems OY", + [3]byte{0, 2, 140}: "Micrel-Synergy Semiconductor", + [3]byte{0, 2, 141}: "Movita Technologies, Inc.", + [3]byte{0, 2, 142}: "Rapid 5 Networks, Inc.", + [3]byte{0, 2, 143}: "Globetek, Inc.", + [3]byte{0, 2, 144}: "Woorigisool, Inc.", + [3]byte{0, 2, 145}: "Open Network Co., Ltd.", + [3]byte{0, 2, 146}: "Logic Innovations, Inc.", + [3]byte{0, 2, 147}: "Solid Data Systems", + [3]byte{0, 2, 148}: "Tokyo Sokushin Co., Ltd.", + [3]byte{0, 2, 149}: "IP.Access Limited", + [3]byte{0, 2, 150}: "Lectron Co,. Ltd.", + [3]byte{0, 2, 151}: "C-COR.net", + [3]byte{0, 2, 152}: "Broadframe Corporation", + [3]byte{0, 2, 153}: "Apex, Inc.", + [3]byte{0, 2, 154}: "Storage Apps", + [3]byte{0, 2, 155}: "Kreatel Communications AB", + [3]byte{0, 2, 156}: "3COM", + [3]byte{0, 2, 157}: "Merix Corp.", + [3]byte{0, 2, 158}: "Information Equipment Co., Ltd.", + [3]byte{0, 2, 159}: "L-3 Communication Aviation Recorders", + [3]byte{0, 2, 160}: "Flatstack Ltd.", + [3]byte{0, 2, 161}: "World Wide Packets", + [3]byte{0, 2, 162}: "Hilscher GmbH", + [3]byte{0, 2, 163}: "ABB Switzerland Ltd, Power Systems", + [3]byte{0, 2, 164}: "AddPac Technology Co., Ltd.", + [3]byte{0, 2, 165}: "Hewlett Packard", + [3]byte{0, 2, 166}: "Effinet Systems Co., Ltd.", + [3]byte{0, 2, 167}: "Vivace Networks", + [3]byte{0, 2, 168}: "Air Link Technology", + [3]byte{0, 2, 169}: "RACOM, s.r.o.", + [3]byte{0, 2, 170}: "PLcom Co., Ltd.", + [3]byte{0, 2, 171}: "CTC Union Technologies Co., Ltd.", + [3]byte{0, 2, 172}: "3PAR data", + [3]byte{0, 2, 173}: "HOYA Corporation", + [3]byte{0, 2, 174}: "Scannex Electronics Ltd.", + [3]byte{0, 2, 175}: "TeleCruz Technology, Inc.", + [3]byte{0, 2, 176}: "Hokubu Communication & Industrial Co., Ltd.", + [3]byte{0, 2, 177}: "Anritsu, Ltd.", + [3]byte{0, 2, 178}: "Cablevision", + [3]byte{0, 2, 179}: "Intel Corporation", + [3]byte{0, 2, 180}: "DAPHNE", + [3]byte{0, 2, 181}: "Avnet, Inc.", + [3]byte{0, 2, 182}: "Acrosser Technology Co., Ltd.", + [3]byte{0, 2, 183}: "Watanabe Electric Industry Co., Ltd.", + [3]byte{0, 2, 184}: "WHI KONSULT AB", + [3]byte{0, 2, 185}: "Cisco Systems, Inc", + [3]byte{0, 2, 186}: "Cisco Systems, Inc", + [3]byte{0, 2, 187}: "Continuous Computing Corp", + [3]byte{0, 2, 188}: "LVL 7 Systems, Inc.", + [3]byte{0, 2, 189}: "Bionet Co., Ltd.", + [3]byte{0, 2, 190}: "Totsu Engineering, Inc.", + [3]byte{0, 2, 191}: "dotRocket, Inc.", + [3]byte{0, 2, 192}: "Bencent Tzeng Industry Co., Ltd.", + [3]byte{0, 2, 193}: "Innovative Electronic Designs, Inc.", + [3]byte{0, 2, 194}: "Net Vision Telecom", + [3]byte{0, 2, 195}: "Arelnet Ltd.", + [3]byte{0, 2, 196}: "Vector International BVBA", + [3]byte{0, 2, 197}: "Evertz Microsystems Ltd.", + [3]byte{0, 2, 198}: "Data Track Technology PLC", + [3]byte{0, 2, 199}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 2, 200}: "Technocom Communications Technology (pte) Ltd", + [3]byte{0, 2, 201}: "Mellanox Technologies, Inc.", + [3]byte{0, 2, 202}: "EndPoints, Inc.", + [3]byte{0, 2, 203}: "TriState Ltd.", + [3]byte{0, 2, 204}: "M.C.C.I", + [3]byte{0, 2, 205}: "TeleDream, Inc.", + [3]byte{0, 2, 206}: "FoxJet, Inc.", + [3]byte{0, 2, 207}: "ZyGate Communications, Inc.", + [3]byte{0, 2, 208}: "Comdial Corporation", + [3]byte{0, 2, 209}: "Vivotek, Inc.", + [3]byte{0, 2, 210}: "Workstation AG", + [3]byte{0, 2, 211}: "NetBotz, Inc.", + [3]byte{0, 2, 212}: "PDA Peripherals, Inc.", + [3]byte{0, 2, 213}: "ACR", + [3]byte{0, 2, 214}: "NICE Systems", + [3]byte{0, 2, 215}: "EMPEG Ltd", + [3]byte{0, 2, 216}: "BRECIS Communications Corporation", + [3]byte{0, 2, 217}: "Reliable Controls", + [3]byte{0, 2, 218}: "ExiO Communications, Inc.", + [3]byte{0, 2, 219}: "NETSEC", + [3]byte{0, 2, 220}: "Fujitsu General Limited", + [3]byte{0, 2, 221}: "Bromax Communications, Ltd.", + [3]byte{0, 2, 222}: "Astrodesign, Inc.", + [3]byte{0, 2, 223}: "Net Com Systems, Inc.", + [3]byte{0, 2, 224}: "ETAS GmbH", + [3]byte{0, 2, 225}: "Integrated Network Corporation", + [3]byte{0, 2, 226}: "NDC Infared Engineering", + [3]byte{0, 2, 227}: "LITE-ON Communications, Inc.", + [3]byte{0, 2, 228}: "JC HYUN Systems, Inc.", + [3]byte{0, 2, 229}: "Timeware Ltd.", + [3]byte{0, 2, 230}: "Gould Instrument Systems, Inc.", + [3]byte{0, 2, 231}: "CAB GmbH & Co KG", + [3]byte{0, 2, 232}: "E.D.&A.", + [3]byte{0, 2, 233}: "CS Systemes De Securite - C3S", + [3]byte{0, 2, 234}: "Focus Enhancements", + [3]byte{0, 2, 235}: "Pico Communications", + [3]byte{0, 2, 236}: "Maschoff Design Engineering", + [3]byte{0, 2, 237}: "DXO Telecom Co., Ltd.", + [3]byte{0, 2, 238}: "Nokia Danmark A/S", + [3]byte{0, 2, 239}: "CCC Network Systems Group Ltd.", + [3]byte{0, 2, 240}: "AME Optimedia Technology Co., Ltd.", + [3]byte{0, 2, 241}: "Pinetron Co., Ltd.", + [3]byte{0, 2, 242}: "eDevice, Inc.", + [3]byte{0, 2, 243}: "Media Serve Co., Ltd.", + [3]byte{0, 2, 244}: "PCTEL, Inc.", + [3]byte{0, 2, 245}: "VIVE Synergies, Inc.", + [3]byte{0, 2, 246}: "Equipe Communications", + [3]byte{0, 2, 247}: "ARM", + [3]byte{0, 2, 248}: "SEAKR Engineering, Inc.", + [3]byte{0, 2, 249}: "MIMOS Berhad", + [3]byte{0, 2, 250}: "DX Antenna Co., Ltd.", + [3]byte{0, 2, 251}: "Baumuller Aulugen-Systemtechnik GmbH", + [3]byte{0, 2, 252}: "Cisco Systems, Inc", + [3]byte{0, 2, 253}: "Cisco Systems, Inc", + [3]byte{0, 2, 254}: "Viditec, Inc.", + [3]byte{0, 2, 255}: "Handan BroadInfoCom", + [3]byte{0, 3, 0}: "Barracuda Networks, Inc.", + [3]byte{0, 3, 1}: "EXFO", + [3]byte{0, 3, 2}: "Charles Industries, Ltd.", + [3]byte{0, 3, 3}: "JAMA Electronics Co., Ltd.", + [3]byte{0, 3, 4}: "Pacific Broadband Communications", + [3]byte{0, 3, 5}: "MSC Vertriebs GmbH", + [3]byte{0, 3, 6}: "Fusion In Tech Co., Ltd.", + [3]byte{0, 3, 7}: "Secure Works, Inc.", + [3]byte{0, 3, 8}: "AM Communications, Inc.", + [3]byte{0, 3, 9}: "Texcel Technology PLC", + [3]byte{0, 3, 10}: "Argus Technologies", + [3]byte{0, 3, 11}: "Hunter Technology, Inc.", + [3]byte{0, 3, 12}: "Telesoft Technologies Ltd.", + [3]byte{0, 3, 13}: "Uniwill Computer Corp.", + [3]byte{0, 3, 14}: "Core Communications Co., Ltd.", + [3]byte{0, 3, 15}: "Digital China (Shanghai) Networks Ltd.", + [3]byte{0, 3, 16}: "E-Globaledge Corporation", + [3]byte{0, 3, 17}: "Micro Technology Co., Ltd.", + [3]byte{0, 3, 18}: "TR-Systemtechnik GmbH", + [3]byte{0, 3, 19}: "Access Media SPA", + [3]byte{0, 3, 20}: "Teleware Network Systems", + [3]byte{0, 3, 21}: "Cidco Incorporated", + [3]byte{0, 3, 22}: "Nobell Communications, Inc.", + [3]byte{0, 3, 23}: "Merlin Systems, Inc.", + [3]byte{0, 3, 24}: "Cyras Systems, Inc.", + [3]byte{0, 3, 25}: "Infineon AG", + [3]byte{0, 3, 26}: "Beijing Broad Telecom Ltd., China", + [3]byte{0, 3, 27}: "Cellvision Systems, Inc.", + [3]byte{0, 3, 28}: "Svenska Hardvarufabriken AB", + [3]byte{0, 3, 29}: "Taiwan Commate Computer, Inc.", + [3]byte{0, 3, 30}: "Optranet, Inc.", + [3]byte{0, 3, 31}: "Condev Ltd.", + [3]byte{0, 3, 32}: "Xpeed, Inc.", + [3]byte{0, 3, 33}: "Reco Research Co., Ltd.", + [3]byte{0, 3, 34}: "IDIS Co., Ltd.", + [3]byte{0, 3, 35}: "Cornet Technology, Inc.", + [3]byte{0, 3, 36}: "SANYO Consumer Electronics Co., Ltd.", + [3]byte{0, 3, 37}: "Arima Computer Corp.", + [3]byte{0, 3, 38}: "Iwasaki Information Systems Co., Ltd.", + [3]byte{0, 3, 39}: "ACT'L", + [3]byte{0, 3, 40}: "Mace Group, Inc.", + [3]byte{0, 3, 41}: "F3, Inc.", + [3]byte{0, 3, 42}: "UniData Communication Systems, Inc.", + [3]byte{0, 3, 43}: "GAI Datenfunksysteme GmbH", + [3]byte{0, 3, 44}: "ABB Switzerland Ltd", + [3]byte{0, 3, 45}: "IBASE Technology, Inc.", + [3]byte{0, 3, 46}: "Scope Information Management, Ltd.", + [3]byte{0, 3, 47}: "Global Sun Technology, Inc.", + [3]byte{0, 3, 48}: "Imagenics, Co., Ltd.", + [3]byte{0, 3, 49}: "Cisco Systems, Inc", + [3]byte{0, 3, 50}: "Cisco Systems, Inc", + [3]byte{0, 3, 51}: "Digitel Co., Ltd.", + [3]byte{0, 3, 52}: "Newport Electronics", + [3]byte{0, 3, 53}: "Mirae Technology", + [3]byte{0, 3, 54}: "Zetes Technologies", + [3]byte{0, 3, 55}: "Vaone, Inc.", + [3]byte{0, 3, 56}: "Oak Technology", + [3]byte{0, 3, 57}: "Eurologic Systems, Ltd.", + [3]byte{0, 3, 58}: "Silicon Wave, Inc.", + [3]byte{0, 3, 59}: "TAMI Tech Co., Ltd.", + [3]byte{0, 3, 60}: "Daiden Co., Ltd.", + [3]byte{0, 3, 61}: "ILSHin Lab", + [3]byte{0, 3, 62}: "Tateyama System Laboratory Co., Ltd.", + [3]byte{0, 3, 63}: "BigBand Networks, Ltd.", + [3]byte{0, 3, 64}: "Floware Wireless Systems, Ltd.", + [3]byte{0, 3, 65}: "Axon Digital Design", + [3]byte{0, 3, 66}: "Nortel Networks", + [3]byte{0, 3, 67}: "Martin Professional A/S", + [3]byte{0, 3, 68}: "Tietech.Co., Ltd.", + [3]byte{0, 3, 69}: "Routrek Networks Corporation", + [3]byte{0, 3, 70}: "Hitachi Kokusai Electric, Inc.", + [3]byte{0, 3, 71}: "Intel Corporation", + [3]byte{0, 3, 72}: "Norscan Instruments, Ltd.", + [3]byte{0, 3, 73}: "Vidicode Datacommunicatie B.V.", + [3]byte{0, 3, 74}: "RIAS Corporation", + [3]byte{0, 3, 75}: "Nortel Networks", + [3]byte{0, 3, 76}: "Shanghai DigiVision Technology Co., Ltd.", + [3]byte{0, 3, 77}: "Chiaro Networks, Ltd.", + [3]byte{0, 3, 78}: "Pos Data Company, Ltd.", + [3]byte{0, 3, 79}: "Sur-Gard Security", + [3]byte{0, 3, 80}: "BTICINO SPA", + [3]byte{0, 3, 81}: "Diebold, Inc.", + [3]byte{0, 3, 82}: "Colubris Networks", + [3]byte{0, 3, 83}: "Mitac, Inc.", + [3]byte{0, 3, 84}: "Fiber Logic Communications", + [3]byte{0, 3, 85}: "TeraBeam Internet Systems", + [3]byte{0, 3, 86}: "Wincor Nixdorf International GmbH", + [3]byte{0, 3, 87}: "Intervoice-Brite, Inc.", + [3]byte{0, 3, 88}: "Hanyang Digitech Co.Ltd", + [3]byte{0, 3, 89}: "DigitalSis", + [3]byte{0, 3, 90}: "Photron Limited", + [3]byte{0, 3, 91}: "BridgeWave Communications", + [3]byte{0, 3, 92}: "Saint Song Corp.", + [3]byte{0, 3, 93}: "Bosung Hi-Net Co., Ltd.", + [3]byte{0, 3, 94}: "Metropolitan Area Networks, Inc.", + [3]byte{0, 3, 95}: "Prüftechnik Condition Monitoring GmbH & Co. KG", + [3]byte{0, 3, 96}: "PAC Interactive Technology, Inc.", + [3]byte{0, 3, 97}: "Widcomm, Inc.", + [3]byte{0, 3, 98}: "Vodtel Communications, Inc.", + [3]byte{0, 3, 99}: "Miraesys Co., Ltd.", + [3]byte{0, 3, 100}: "Scenix Semiconductor, Inc.", + [3]byte{0, 3, 101}: "Kira Information & Communications, Ltd.", + [3]byte{0, 3, 102}: "ASM Pacific Technology", + [3]byte{0, 3, 103}: "Jasmine Networks, Inc.", + [3]byte{0, 3, 104}: "Embedone Co., Ltd.", + [3]byte{0, 3, 105}: "Nippon Antenna Co., Ltd.", + [3]byte{0, 3, 106}: "Mainnet, Ltd.", + [3]byte{0, 3, 107}: "Cisco Systems, Inc", + [3]byte{0, 3, 108}: "Cisco Systems, Inc", + [3]byte{0, 3, 109}: "Runtop, Inc.", + [3]byte{0, 3, 110}: "Nicon Systems (Pty) Limited", + [3]byte{0, 3, 111}: "Telsey SPA", + [3]byte{0, 3, 112}: "NXTV, Inc.", + [3]byte{0, 3, 113}: "Acomz Networks Corp.", + [3]byte{0, 3, 114}: "ULAN", + [3]byte{0, 3, 115}: "Aselsan A.S", + [3]byte{0, 3, 116}: "Control Microsystems", + [3]byte{0, 3, 117}: "NetMedia, Inc.", + [3]byte{0, 3, 118}: "Graphtec Technology, Inc.", + [3]byte{0, 3, 119}: "Gigabit Wireless", + [3]byte{0, 3, 120}: "HUMAX Co., Ltd.", + [3]byte{0, 3, 121}: "Proscend Communications, Inc.", + [3]byte{0, 3, 122}: "Taiyo Yuden Co., Ltd.", + [3]byte{0, 3, 123}: "IDEC IZUMI Corporation", + [3]byte{0, 3, 124}: "Coax Media", + [3]byte{0, 3, 125}: "Stellcom", + [3]byte{0, 3, 126}: "PORTech Communications, Inc.", + [3]byte{0, 3, 127}: "Atheros Communications, Inc.", + [3]byte{0, 3, 128}: "SSH Communications Security Corp.", + [3]byte{0, 3, 129}: "Ingenico International", + [3]byte{0, 3, 130}: "A-One Co., Ltd.", + [3]byte{0, 3, 131}: "Metera Networks, Inc.", + [3]byte{0, 3, 132}: "AETA", + [3]byte{0, 3, 133}: "Actelis Networks, Inc.", + [3]byte{0, 3, 134}: "Ho Net, Inc.", + [3]byte{0, 3, 135}: "Blaze Network Products", + [3]byte{0, 3, 136}: "Fastfame Technology Co., Ltd.", + [3]byte{0, 3, 137}: "PLANTRONICS, INC.", + [3]byte{0, 3, 138}: "America Online, Inc.", + [3]byte{0, 3, 139}: "PLUS-ONE I&T, Inc.", + [3]byte{0, 3, 140}: "Total Impact", + [3]byte{0, 3, 141}: "PCS Revenue Control Systems, Inc.", + [3]byte{0, 3, 142}: "Atoga Systems, Inc.", + [3]byte{0, 3, 143}: "Weinschel Corporation", + [3]byte{0, 3, 144}: "Digital Video Communications, Inc.", + [3]byte{0, 3, 145}: "Advanced Digital Broadcast, Ltd.", + [3]byte{0, 3, 146}: "Hyundai Teletek Co., Ltd.", + [3]byte{0, 3, 147}: "Apple, Inc.", + [3]byte{0, 3, 148}: "Connect One", + [3]byte{0, 3, 149}: "California Amplifier", + [3]byte{0, 3, 150}: "EZ Cast Co., Ltd.", + [3]byte{0, 3, 151}: "Watchfront Limited", + [3]byte{0, 3, 152}: "WISI", + [3]byte{0, 3, 153}: "Dongju Informations & Communications Co., Ltd.", + [3]byte{0, 3, 154}: "SiConnect", + [3]byte{0, 3, 155}: "NetChip Technology, Inc.", + [3]byte{0, 3, 156}: "OptiMight Communications, Inc.", + [3]byte{0, 3, 157}: "Qisda Corporation", + [3]byte{0, 3, 158}: "Tera System Co., Ltd.", + [3]byte{0, 3, 159}: "Cisco Systems, Inc", + [3]byte{0, 3, 160}: "Cisco Systems, Inc", + [3]byte{0, 3, 161}: "HIPER Information & Communication, Inc.", + [3]byte{0, 3, 162}: "Catapult Communications", + [3]byte{0, 3, 163}: "MAVIX, Ltd.", + [3]byte{0, 3, 164}: "Imation Corp.", + [3]byte{0, 3, 165}: "Medea Corporation", + [3]byte{0, 3, 166}: "Traxit Technology, Inc.", + [3]byte{0, 3, 167}: "Unixtar Technology, Inc.", + [3]byte{0, 3, 168}: "IDOT Computers, Inc.", + [3]byte{0, 3, 169}: "AXCENT Media AG", + [3]byte{0, 3, 170}: "Watlow", + [3]byte{0, 3, 171}: "Bridge Information Systems", + [3]byte{0, 3, 172}: "Fronius Schweissmaschinen", + [3]byte{0, 3, 173}: "Emerson Energy Systems AB", + [3]byte{0, 3, 174}: "Allied Advanced Manufacturing Pte, Ltd.", + [3]byte{0, 3, 175}: "Paragea Communications", + [3]byte{0, 3, 176}: "Xsense Technology Corp.", + [3]byte{0, 3, 177}: "Hospira Inc.", + [3]byte{0, 3, 178}: "Radware", + [3]byte{0, 3, 179}: "IA Link Systems Co., Ltd.", + [3]byte{0, 3, 180}: "Macrotek International Corp.", + [3]byte{0, 3, 181}: "Entra Technology Co.", + [3]byte{0, 3, 182}: "QSI Corporation", + [3]byte{0, 3, 183}: "ZACCESS Systems", + [3]byte{0, 3, 184}: "NetKit Solutions, LLC", + [3]byte{0, 3, 185}: "Hualong Telecom Co., Ltd.", + [3]byte{0, 3, 186}: "Oracle Corporation", + [3]byte{0, 3, 187}: "Signal Communications Limited", + [3]byte{0, 3, 188}: "COT GmbH", + [3]byte{0, 3, 189}: "OmniCluster Technologies, Inc.", + [3]byte{0, 3, 190}: "Netility", + [3]byte{0, 3, 191}: "Centerpoint Broadband Technologies, Inc.", + [3]byte{0, 3, 192}: "RFTNC Co., Ltd.", + [3]byte{0, 3, 193}: "Packet Dynamics Ltd", + [3]byte{0, 3, 194}: "Solphone K.K.", + [3]byte{0, 3, 195}: "Micronik Multimedia", + [3]byte{0, 3, 196}: "Tomra Systems ASA", + [3]byte{0, 3, 197}: "Mobotix AG", + [3]byte{0, 3, 198}: "ICUE Systems, Inc.", + [3]byte{0, 3, 199}: "hopf Elektronik GmbH", + [3]byte{0, 3, 200}: "CML Emergency Services", + [3]byte{0, 3, 201}: "TECOM Co., Ltd.", + [3]byte{0, 3, 202}: "MTS Systems Corp.", + [3]byte{0, 3, 203}: "Nippon Systems Development Co., Ltd.", + [3]byte{0, 3, 204}: "Momentum Computer, Inc.", + [3]byte{0, 3, 205}: "Clovertech, Inc.", + [3]byte{0, 3, 206}: "ETEN Technologies, Inc.", + [3]byte{0, 3, 207}: "Muxcom, Inc.", + [3]byte{0, 3, 208}: "KOANKEISO Co., Ltd.", + [3]byte{0, 3, 209}: "Takaya Corporation", + [3]byte{0, 3, 210}: "Crossbeam Systems, Inc.", + [3]byte{0, 3, 211}: "Internet Energy Systems, Inc.", + [3]byte{0, 3, 212}: "Alloptic, Inc.", + [3]byte{0, 3, 213}: "Advanced Communications Co., Ltd.", + [3]byte{0, 3, 214}: "RADVision, Ltd.", + [3]byte{0, 3, 215}: "NextNet Wireless, Inc.", + [3]byte{0, 3, 216}: "iMPath Networks, Inc.", + [3]byte{0, 3, 217}: "Secheron SA", + [3]byte{0, 3, 218}: "Takamisawa Cybernetics Co., Ltd.", + [3]byte{0, 3, 219}: "Apogee Electronics Corp.", + [3]byte{0, 3, 220}: "Lexar Media, Inc.", + [3]byte{0, 3, 221}: "Comark Interactive Solutions", + [3]byte{0, 3, 222}: "OTC Wireless", + [3]byte{0, 3, 223}: "Desana Systems", + [3]byte{0, 3, 224}: "ARRIS Group, Inc.", + [3]byte{0, 3, 225}: "Winmate Communication, Inc.", + [3]byte{0, 3, 226}: "Comspace Corporation", + [3]byte{0, 3, 227}: "Cisco Systems, Inc", + [3]byte{0, 3, 228}: "Cisco Systems, Inc", + [3]byte{0, 3, 229}: "Hermstedt SG", + [3]byte{0, 3, 230}: "Entone, Inc.", + [3]byte{0, 3, 231}: "Logostek Co. Ltd.", + [3]byte{0, 3, 232}: "Wavelength Digital Limited", + [3]byte{0, 3, 233}: "Akara Canada, Inc.", + [3]byte{0, 3, 234}: "Mega System Technologies, Inc.", + [3]byte{0, 3, 235}: "Atrica", + [3]byte{0, 3, 236}: "ICG Research, Inc.", + [3]byte{0, 3, 237}: "Shinkawa Electric Co., Ltd.", + [3]byte{0, 3, 238}: "MKNet Corporation", + [3]byte{0, 3, 239}: "Oneline AG", + [3]byte{0, 3, 240}: "Redfern Broadband Networks", + [3]byte{0, 3, 241}: "Cicada Semiconductor, Inc.", + [3]byte{0, 3, 242}: "Seneca Networks", + [3]byte{0, 3, 243}: "Dazzle Multimedia, Inc.", + [3]byte{0, 3, 244}: "NetBurner", + [3]byte{0, 3, 245}: "Chip2Chip", + [3]byte{0, 3, 246}: "Allegro Networks, Inc.", + [3]byte{0, 3, 247}: "Plast-Control GmbH", + [3]byte{0, 3, 248}: "SanCastle Technologies, Inc.", + [3]byte{0, 3, 249}: "Pleiades Communications, Inc.", + [3]byte{0, 3, 250}: "TiMetra Networks", + [3]byte{0, 3, 251}: "ENEGATE Co.,Ltd.", + [3]byte{0, 3, 252}: "Intertex Data AB", + [3]byte{0, 3, 253}: "Cisco Systems, Inc", + [3]byte{0, 3, 254}: "Cisco Systems, Inc", + [3]byte{0, 3, 255}: "Microsoft Corporation", + [3]byte{0, 4, 0}: "LEXMARK INTERNATIONAL, INC.", + [3]byte{0, 4, 1}: "Osaki Electric Co., Ltd.", + [3]byte{0, 4, 2}: "Nexsan Technologies, Ltd.", + [3]byte{0, 4, 3}: "Nexsi Corporation", + [3]byte{0, 4, 4}: "Makino Milling Machine Co., Ltd.", + [3]byte{0, 4, 5}: "ACN Technologies", + [3]byte{0, 4, 6}: "Fa. Metabox AG", + [3]byte{0, 4, 7}: "Topcon Positioning Systems, Inc.", + [3]byte{0, 4, 8}: "Sanko Electronics Co., Ltd.", + [3]byte{0, 4, 9}: "Cratos Networks", + [3]byte{0, 4, 10}: "Sage Systems", + [3]byte{0, 4, 11}: "3COM EUROPE LTD.", + [3]byte{0, 4, 12}: "Kanno Works, Ltd.", + [3]byte{0, 4, 13}: "Avaya Inc", + [3]byte{0, 4, 14}: "AVM GmbH", + [3]byte{0, 4, 15}: "Asus Network Technologies, Inc.", + [3]byte{0, 4, 16}: "Spinnaker Networks, Inc.", + [3]byte{0, 4, 17}: "Inkra Networks, Inc.", + [3]byte{0, 4, 18}: "WaveSmith Networks, Inc.", + [3]byte{0, 4, 19}: "SNOM Technology AG", + [3]byte{0, 4, 20}: "Umezawa Musen Denki Co., Ltd.", + [3]byte{0, 4, 21}: "Rasteme Systems Co., Ltd.", + [3]byte{0, 4, 22}: "Parks S/A Comunicacoes Digitais", + [3]byte{0, 4, 23}: "ELAU AG", + [3]byte{0, 4, 24}: "Teltronic S.A.U.", + [3]byte{0, 4, 25}: "Fibercycle Networks, Inc.", + [3]byte{0, 4, 26}: "Ines Test and Measurement GmbH & CoKG", + [3]byte{0, 4, 27}: "Bridgeworks Ltd.", + [3]byte{0, 4, 28}: "ipDialog, Inc.", + [3]byte{0, 4, 29}: "Corega of America", + [3]byte{0, 4, 30}: "Shikoku Instrumentation Co., Ltd.", + [3]byte{0, 4, 31}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 4, 32}: "Slim Devices, Inc.", + [3]byte{0, 4, 33}: "Ocular Networks", + [3]byte{0, 4, 34}: "Studio Technologies, Inc", + [3]byte{0, 4, 35}: "Intel Corporation", + [3]byte{0, 4, 36}: "TMC s.r.l.", + [3]byte{0, 4, 37}: "Atmel Corporation", + [3]byte{0, 4, 38}: "Autosys", + [3]byte{0, 4, 39}: "Cisco Systems, Inc", + [3]byte{0, 4, 40}: "Cisco Systems, Inc", + [3]byte{0, 4, 41}: "Pixord Corporation", + [3]byte{0, 4, 42}: "Wireless Networks, Inc.", + [3]byte{0, 4, 43}: "IT Access Co., Ltd.", + [3]byte{0, 4, 44}: "Minet, Inc.", + [3]byte{0, 4, 45}: "Sarian Systems, Ltd.", + [3]byte{0, 4, 46}: "Netous Technologies, Ltd.", + [3]byte{0, 4, 47}: "International Communications Products, Inc.", + [3]byte{0, 4, 48}: "Netgem", + [3]byte{0, 4, 49}: "GlobalStreams, Inc.", + [3]byte{0, 4, 50}: "Voyetra Turtle Beach, Inc.", + [3]byte{0, 4, 51}: "Cyberboard A/S", + [3]byte{0, 4, 52}: "Accelent Systems, Inc.", + [3]byte{0, 4, 53}: "InfiNet LLC", + [3]byte{0, 4, 54}: "ELANsat Technologies, Inc.", + [3]byte{0, 4, 55}: "Powin Information Technology, Inc.", + [3]byte{0, 4, 56}: "Nortel Networks", + [3]byte{0, 4, 57}: "Rosco Entertainment Technology, Inc.", + [3]byte{0, 4, 58}: "Intelligent Telecommunications, Inc.", + [3]byte{0, 4, 59}: "Lava Computer Mfg., Inc.", + [3]byte{0, 4, 60}: "SONOS Co., Ltd.", + [3]byte{0, 4, 61}: "INDEL AG", + [3]byte{0, 4, 62}: "Telencomm", + [3]byte{0, 4, 63}: "ESTeem Wireless Modems, Inc", + [3]byte{0, 4, 64}: "cyberPIXIE, Inc.", + [3]byte{0, 4, 65}: "Half Dome Systems, Inc.", + [3]byte{0, 4, 66}: "NACT", + [3]byte{0, 4, 67}: "Agilent Technologies, Inc.", + [3]byte{0, 4, 68}: "Western Multiplex Corporation", + [3]byte{0, 4, 69}: "LMS Skalar Instruments GmbH", + [3]byte{0, 4, 70}: "CYZENTECH Co., Ltd.", + [3]byte{0, 4, 71}: "Acrowave Systems Co., Ltd.", + [3]byte{0, 4, 72}: "Polaroid Corporation", + [3]byte{0, 4, 73}: "Mapletree Networks", + [3]byte{0, 4, 74}: "iPolicy Networks, Inc.", + [3]byte{0, 4, 75}: "NVIDIA", + [3]byte{0, 4, 76}: "JENOPTIK", + [3]byte{0, 4, 77}: "Cisco Systems, Inc", + [3]byte{0, 4, 78}: "Cisco Systems, Inc", + [3]byte{0, 4, 79}: "Schubert System Elektronik Gmbh", + [3]byte{0, 4, 80}: "DMD Computers SRL", + [3]byte{0, 4, 81}: "Medrad, Inc.", + [3]byte{0, 4, 82}: "RocketLogix, Inc.", + [3]byte{0, 4, 83}: "YottaYotta, Inc.", + [3]byte{0, 4, 84}: "Quadriga UK", + [3]byte{0, 4, 85}: "ANTARA.net", + [3]byte{0, 4, 86}: "Cambium Networks Limited", + [3]byte{0, 4, 87}: "Universal Access Technology, Inc.", + [3]byte{0, 4, 88}: "Fusion X Co., Ltd.", + [3]byte{0, 4, 89}: "Veristar Corporation", + [3]byte{0, 4, 90}: "The Linksys Group, Inc.", + [3]byte{0, 4, 91}: "Techsan Electronics Co., Ltd.", + [3]byte{0, 4, 92}: "Mobiwave Pte Ltd", + [3]byte{0, 4, 93}: "BEKA Elektronik", + [3]byte{0, 4, 94}: "PolyTrax Information Technology AG", + [3]byte{0, 4, 95}: "Avalue Technology, Inc.", + [3]byte{0, 4, 96}: "Knilink Technology, Inc.", + [3]byte{0, 4, 97}: "EPOX Computer Co., Ltd.", + [3]byte{0, 4, 98}: "DAKOS Data & Communication Co., Ltd.", + [3]byte{0, 4, 99}: "Bosch Security Systems", + [3]byte{0, 4, 100}: "Pulse-Link Inc", + [3]byte{0, 4, 101}: "i.s.t isdn-support technik GmbH", + [3]byte{0, 4, 102}: "ARMITEL Co.", + [3]byte{0, 4, 103}: "Wuhan Research Institute of MII", + [3]byte{0, 4, 104}: "Vivity, Inc.", + [3]byte{0, 4, 105}: "Innocom, Inc.", + [3]byte{0, 4, 106}: "Navini Networks", + [3]byte{0, 4, 107}: "Palm Wireless, Inc.", + [3]byte{0, 4, 108}: "Cyber Technology Co., Ltd.", + [3]byte{0, 4, 109}: "Cisco Systems, Inc", + [3]byte{0, 4, 110}: "Cisco Systems, Inc", + [3]byte{0, 4, 111}: "Digitel S/A Industria Eletronica", + [3]byte{0, 4, 112}: "ipUnplugged AB", + [3]byte{0, 4, 113}: "IPrad", + [3]byte{0, 4, 114}: "Telelynx, Inc.", + [3]byte{0, 4, 115}: "Photonex Corporation", + [3]byte{0, 4, 116}: "LEGRAND", + [3]byte{0, 4, 117}: "3 Com Corporation", + [3]byte{0, 4, 118}: "3 Com Corporation", + [3]byte{0, 4, 119}: "Scalant Systems, Inc.", + [3]byte{0, 4, 120}: "G. Star Technology Corporation", + [3]byte{0, 4, 121}: "Radius Co., Ltd.", + [3]byte{0, 4, 122}: "AXXESSIT ASA", + [3]byte{0, 4, 123}: "Schlumberger", + [3]byte{0, 4, 124}: "Skidata AG", + [3]byte{0, 4, 125}: "Pelco", + [3]byte{0, 4, 126}: "Siqura B.V.", + [3]byte{0, 4, 127}: "Chr. Mayr GmbH & Co. KG", + [3]byte{0, 4, 128}: "Brocade Communications Systems, Inc.", + [3]byte{0, 4, 129}: "Econolite Control Products, Inc.", + [3]byte{0, 4, 130}: "Medialogic Corp.", + [3]byte{0, 4, 131}: "Deltron Technology, Inc.", + [3]byte{0, 4, 132}: "Amann GmbH", + [3]byte{0, 4, 133}: "PicoLight", + [3]byte{0, 4, 134}: "ITTC, University of Kansas", + [3]byte{0, 4, 135}: "Cogency Semiconductor, Inc.", + [3]byte{0, 4, 136}: "Eurotherm Controls", + [3]byte{0, 4, 137}: "YAFO Networks, Inc.", + [3]byte{0, 4, 138}: "Temia Vertriebs GmbH", + [3]byte{0, 4, 139}: "Poscon Corporation", + [3]byte{0, 4, 140}: "Nayna Networks, Inc.", + [3]byte{0, 4, 141}: "Teo Technologies, Inc", + [3]byte{0, 4, 142}: "Ohm Tech Labs, Inc.", + [3]byte{0, 4, 143}: "TD Systems Corporation", + [3]byte{0, 4, 144}: "Optical Access", + [3]byte{0, 4, 145}: "Technovision, Inc.", + [3]byte{0, 4, 146}: "Hive Internet, Ltd.", + [3]byte{0, 4, 147}: "Tsinghua Unisplendour Co., Ltd.", + [3]byte{0, 4, 148}: "Breezecom, Ltd.", + [3]byte{0, 4, 149}: "Tejas Networks India Limited", + [3]byte{0, 4, 150}: "Extreme Networks", + [3]byte{0, 4, 151}: "MacroSystem Digital Video AG", + [3]byte{0, 4, 152}: "Mahi Networks", + [3]byte{0, 4, 153}: "Chino Corporation", + [3]byte{0, 4, 154}: "Cisco Systems, Inc", + [3]byte{0, 4, 155}: "Cisco Systems, Inc", + [3]byte{0, 4, 156}: "Surgient Networks, Inc.", + [3]byte{0, 4, 157}: "Ipanema Technologies", + [3]byte{0, 4, 158}: "Wirelink Co., Ltd.", + [3]byte{0, 4, 159}: "Freescale Semiconductor", + [3]byte{0, 4, 160}: "Verity Instruments, Inc.", + [3]byte{0, 4, 161}: "Pathway Connectivity", + [3]byte{0, 4, 162}: "L.S.I. Japan Co., Ltd.", + [3]byte{0, 4, 163}: "Microchip Technology Inc.", + [3]byte{0, 4, 164}: "NetEnabled, Inc.", + [3]byte{0, 4, 165}: "Barco Projection Systems NV", + [3]byte{0, 4, 166}: "SAF Tehnika Ltd.", + [3]byte{0, 4, 167}: "FabiaTech Corporation", + [3]byte{0, 4, 168}: "Broadmax Technologies, Inc.", + [3]byte{0, 4, 169}: "SandStream Technologies, Inc.", + [3]byte{0, 4, 170}: "Jetstream Communications", + [3]byte{0, 4, 171}: "Comverse Network Systems, Inc.", + [3]byte{0, 4, 172}: "IBM Corp", + [3]byte{0, 4, 173}: "Malibu Networks", + [3]byte{0, 4, 174}: "Sullair Corporation", + [3]byte{0, 4, 175}: "Digital Fountain, Inc.", + [3]byte{0, 4, 176}: "ELESIGN Co., Ltd.", + [3]byte{0, 4, 177}: "Signal Technology, Inc.", + [3]byte{0, 4, 178}: "ESSEGI SRL", + [3]byte{0, 4, 179}: "Videotek, Inc.", + [3]byte{0, 4, 180}: "CIAC", + [3]byte{0, 4, 181}: "Equitrac Corporation", + [3]byte{0, 4, 182}: "Stratex Networks, Inc.", + [3]byte{0, 4, 183}: "AMB i.t. Holding", + [3]byte{0, 4, 184}: "Kumahira Co., Ltd.", + [3]byte{0, 4, 185}: "S.I. Soubou, Inc.", + [3]byte{0, 4, 186}: "KDD Media Will Corporation", + [3]byte{0, 4, 187}: "Bardac Corporation", + [3]byte{0, 4, 188}: "Giantec, Inc.", + [3]byte{0, 4, 189}: "ARRIS Group, Inc.", + [3]byte{0, 4, 190}: "OptXCon, Inc.", + [3]byte{0, 4, 191}: "VersaLogic Corp.", + [3]byte{0, 4, 192}: "Cisco Systems, Inc", + [3]byte{0, 4, 193}: "Cisco Systems, Inc", + [3]byte{0, 4, 194}: "Magnipix, Inc.", + [3]byte{0, 4, 195}: "CASTOR Informatique", + [3]byte{0, 4, 196}: "Allen & Heath Limited", + [3]byte{0, 4, 197}: "ASE Technologies, USA", + [3]byte{0, 4, 198}: "YAMAHA MOTOR CO.,LTD", + [3]byte{0, 4, 199}: "NetMount", + [3]byte{0, 4, 200}: "LIBA Maschinenfabrik GmbH", + [3]byte{0, 4, 201}: "Micro Electron Co., Ltd.", + [3]byte{0, 4, 202}: "FreeMs Corp.", + [3]byte{0, 4, 203}: "Tdsoft Communication, Ltd.", + [3]byte{0, 4, 204}: "Peek Traffic B.V.", + [3]byte{0, 4, 205}: "Extenway Solutions Inc", + [3]byte{0, 4, 206}: "Patria Ailon", + [3]byte{0, 4, 207}: "Seagate Technology", + [3]byte{0, 4, 208}: "Softlink s.r.o.", + [3]byte{0, 4, 209}: "Drew Technologies, Inc.", + [3]byte{0, 4, 210}: "Adcon Telemetry GmbH", + [3]byte{0, 4, 211}: "Toyokeiki Co., Ltd.", + [3]byte{0, 4, 212}: "Proview Electronics Co., Ltd.", + [3]byte{0, 4, 213}: "Hitachi Information & Communication Engineering, Ltd.", + [3]byte{0, 4, 214}: "Takagi Industrial Co., Ltd.", + [3]byte{0, 4, 215}: "Omitec Instrumentation Ltd.", + [3]byte{0, 4, 216}: "IPWireless, Inc.", + [3]byte{0, 4, 217}: "Titan Electronics, Inc.", + [3]byte{0, 4, 218}: "Relax Technology, Inc.", + [3]byte{0, 4, 219}: "Tellus Group Corp.", + [3]byte{0, 4, 220}: "Nortel Networks", + [3]byte{0, 4, 221}: "Cisco Systems, Inc", + [3]byte{0, 4, 222}: "Cisco Systems, Inc", + [3]byte{0, 4, 223}: "Teracom Telematica Ltda.", + [3]byte{0, 4, 224}: "Procket Networks", + [3]byte{0, 4, 225}: "Infinior Microsystems", + [3]byte{0, 4, 226}: "SMC Networks, Inc.", + [3]byte{0, 4, 227}: "Accton Technology Corp", + [3]byte{0, 4, 228}: "Daeryung Ind., Inc.", + [3]byte{0, 4, 229}: "Glonet Systems, Inc.", + [3]byte{0, 4, 230}: "Banyan Network Private Limited", + [3]byte{0, 4, 231}: "Lightpointe Communications, Inc", + [3]byte{0, 4, 232}: "IER, Inc.", + [3]byte{0, 4, 233}: "Infiniswitch Corporation", + [3]byte{0, 4, 234}: "Hewlett Packard", + [3]byte{0, 4, 235}: "Paxonet Communications, Inc.", + [3]byte{0, 4, 236}: "Memobox SA", + [3]byte{0, 4, 237}: "Billion Electric Co., Ltd.", + [3]byte{0, 4, 238}: "Lincoln Electric Company", + [3]byte{0, 4, 239}: "Polestar Corp.", + [3]byte{0, 4, 240}: "International Computers, Ltd", + [3]byte{0, 4, 241}: "WhereNet", + [3]byte{0, 4, 242}: "Polycom", + [3]byte{0, 4, 243}: "FS FORTH-SYSTEME GmbH", + [3]byte{0, 4, 244}: "Infinite Electronics Inc.", + [3]byte{0, 4, 245}: "SnowShore Networks, Inc.", + [3]byte{0, 4, 246}: "Amphus", + [3]byte{0, 4, 247}: "Omega Band, Inc.", + [3]byte{0, 4, 248}: "QUALICABLE TV Industria E Com., Ltda", + [3]byte{0, 4, 249}: "Xtera Communications, Inc.", + [3]byte{0, 4, 250}: "NBS Technologies Inc.", + [3]byte{0, 4, 251}: "Commtech, Inc.", + [3]byte{0, 4, 252}: "Stratus Computer (DE), Inc.", + [3]byte{0, 4, 253}: "Japan Control Engineering Co., Ltd.", + [3]byte{0, 4, 254}: "Pelago Networks", + [3]byte{0, 4, 255}: "Acronet Co., Ltd.", + [3]byte{0, 5, 0}: "Cisco Systems, Inc", + [3]byte{0, 5, 1}: "Cisco Systems, Inc", + [3]byte{0, 5, 2}: "Apple, Inc.", + [3]byte{0, 5, 3}: "ICONAG", + [3]byte{0, 5, 4}: "Naray Information & Communication Enterprise", + [3]byte{0, 5, 5}: "Systems Integration Solutions, Inc.", + [3]byte{0, 5, 6}: "Reddo Networks AB", + [3]byte{0, 5, 7}: "Fine Appliance Corp.", + [3]byte{0, 5, 8}: "Inetcam, Inc.", + [3]byte{0, 5, 9}: "AVOC Nishimura Ltd.", + [3]byte{0, 5, 10}: "ICS Spa", + [3]byte{0, 5, 11}: "SICOM Systems, Inc.", + [3]byte{0, 5, 12}: "Network Photonics, Inc.", + [3]byte{0, 5, 13}: "Midstream Technologies, Inc.", + [3]byte{0, 5, 14}: "3ware, Inc.", + [3]byte{0, 5, 15}: "Tanaka S/S Ltd.", + [3]byte{0, 5, 16}: "Infinite Shanghai Communication Terminals Ltd.", + [3]byte{0, 5, 17}: "Complementary Technologies Ltd", + [3]byte{0, 5, 18}: "Zebra Technologies Inc", + [3]byte{0, 5, 19}: "VTLinx Multimedia Systems, Inc.", + [3]byte{0, 5, 20}: "KDT Systems Co., Ltd.", + [3]byte{0, 5, 21}: "Nuark Co., Ltd.", + [3]byte{0, 5, 22}: "SMART Modular Technologies", + [3]byte{0, 5, 23}: "Shellcomm, Inc.", + [3]byte{0, 5, 24}: "Jupiters Technology", + [3]byte{0, 5, 25}: "Siemens Building Technologies AG,", + [3]byte{0, 5, 26}: "3COM EUROPE LTD.", + [3]byte{0, 5, 27}: "Magic Control Technology Corporation", + [3]byte{0, 5, 28}: "Xnet Technology Corp.", + [3]byte{0, 5, 29}: "Airocon, Inc.", + [3]byte{0, 5, 30}: "Brocade Communications Systems, Inc.", + [3]byte{0, 5, 31}: "Taijin Media Co., Ltd.", + [3]byte{0, 5, 32}: "Smartronix, Inc.", + [3]byte{0, 5, 33}: "Control Microsystems", + [3]byte{0, 5, 34}: "LEA*D Corporation, Inc.", + [3]byte{0, 5, 35}: "AVL List GmbH", + [3]byte{0, 5, 36}: "BTL System (HK) Limited", + [3]byte{0, 5, 37}: "Puretek Industrial Co., Ltd.", + [3]byte{0, 5, 38}: "IPAS GmbH", + [3]byte{0, 5, 39}: "SJ Tek Co. Ltd", + [3]byte{0, 5, 40}: "New Focus, Inc.", + [3]byte{0, 5, 41}: "Shanghai Broadan Communication Technology Co., Ltd", + [3]byte{0, 5, 42}: "Ikegami Tsushinki Co., Ltd.", + [3]byte{0, 5, 43}: "HORIBA, Ltd.", + [3]byte{0, 5, 44}: "Supreme Magic Corporation", + [3]byte{0, 5, 45}: "Zoltrix International Limited", + [3]byte{0, 5, 46}: "Cinta Networks", + [3]byte{0, 5, 47}: "Leviton Network Solutions", + [3]byte{0, 5, 48}: "Andiamo Systems, Inc.", + [3]byte{0, 5, 49}: "Cisco Systems, Inc", + [3]byte{0, 5, 50}: "Cisco Systems, Inc", + [3]byte{0, 5, 51}: "Brocade Communications Systems, Inc.", + [3]byte{0, 5, 52}: "Northstar Engineering Ltd.", + [3]byte{0, 5, 53}: "Chip PC Ltd.", + [3]byte{0, 5, 54}: "Danam Communications, Inc.", + [3]byte{0, 5, 55}: "Nets Technology Co., Ltd.", + [3]byte{0, 5, 56}: "Merilus, Inc.", + [3]byte{0, 5, 57}: "A Brand New World in Sweden AB", + [3]byte{0, 5, 58}: "Willowglen Services Pte Ltd", + [3]byte{0, 5, 59}: "Harbour Networks Ltd., Co. Beijing", + [3]byte{0, 5, 60}: "XIRCOM", + [3]byte{0, 5, 61}: "Agere Systems", + [3]byte{0, 5, 62}: "KID Systeme GmbH", + [3]byte{0, 5, 63}: "VisionTek, Inc.", + [3]byte{0, 5, 64}: "FAST Corporation", + [3]byte{0, 5, 65}: "Advanced Systems Co., Ltd.", + [3]byte{0, 5, 66}: "Otari, Inc.", + [3]byte{0, 5, 67}: "IQ Wireless GmbH", + [3]byte{0, 5, 68}: "Valley Technologies, Inc.", + [3]byte{0, 5, 69}: "Internet Photonics", + [3]byte{0, 5, 70}: "KDDI Network & Solultions Inc.", + [3]byte{0, 5, 71}: "Starent Networks", + [3]byte{0, 5, 72}: "Disco Corporation", + [3]byte{0, 5, 73}: "Salira Optical Network Systems", + [3]byte{0, 5, 74}: "Ario Data Networks, Inc.", + [3]byte{0, 5, 75}: "Eaton Automation AG", + [3]byte{0, 5, 76}: "RF Innovations Pty Ltd", + [3]byte{0, 5, 77}: "Brans Technologies, Inc.", + [3]byte{0, 5, 78}: "Philips", + [3]byte{0, 5, 79}: "Private", + [3]byte{0, 5, 80}: "Vcomms Connect Limited", + [3]byte{0, 5, 81}: "F & S Elektronik Systeme GmbH", + [3]byte{0, 5, 82}: "Xycotec Computer GmbH", + [3]byte{0, 5, 83}: "DVC Company, Inc.", + [3]byte{0, 5, 84}: "Rangestar Wireless", + [3]byte{0, 5, 85}: "Japan Cash Machine Co., Ltd.", + [3]byte{0, 5, 86}: "360 Systems", + [3]byte{0, 5, 87}: "Agile TV Corporation", + [3]byte{0, 5, 88}: "Synchronous, Inc.", + [3]byte{0, 5, 89}: "Intracom S.A.", + [3]byte{0, 5, 90}: "Power Dsine Ltd.", + [3]byte{0, 5, 91}: "Charles Industries, Ltd.", + [3]byte{0, 5, 92}: "Kowa Company, Ltd.", + [3]byte{0, 5, 93}: "D-LINK SYSTEMS, INC.", + [3]byte{0, 5, 94}: "Cisco Systems, Inc", + [3]byte{0, 5, 95}: "Cisco Systems, Inc", + [3]byte{0, 5, 96}: "LEADER COMM.CO., LTD", + [3]byte{0, 5, 97}: "nac Image Technology, Inc.", + [3]byte{0, 5, 98}: "Digital View Limited", + [3]byte{0, 5, 99}: "J-Works, Inc.", + [3]byte{0, 5, 100}: "Tsinghua Bitway Co., Ltd.", + [3]byte{0, 5, 101}: "Tailyn Communication Company Ltd.", + [3]byte{0, 5, 102}: "Secui.com Corporation", + [3]byte{0, 5, 103}: "Etymonic Design, Inc.", + [3]byte{0, 5, 104}: "Piltofish Networks AB", + [3]byte{0, 5, 105}: "VMware, Inc.", + [3]byte{0, 5, 106}: "Heuft Systemtechnik GmbH", + [3]byte{0, 5, 107}: "C.P. Technology Co., Ltd.", + [3]byte{0, 5, 108}: "Hung Chang Co., Ltd.", + [3]byte{0, 5, 109}: "Pacific Corporation", + [3]byte{0, 5, 110}: "National Enhance Technology, Inc.", + [3]byte{0, 5, 111}: "Innomedia Technologies Pvt. Ltd.", + [3]byte{0, 5, 112}: "Baydel Ltd.", + [3]byte{0, 5, 113}: "Seiwa Electronics Co.", + [3]byte{0, 5, 114}: "Deonet Co., Ltd.", + [3]byte{0, 5, 115}: "Cisco Systems, Inc", + [3]byte{0, 5, 116}: "Cisco Systems, Inc", + [3]byte{0, 5, 117}: "CDS-Electronics BV", + [3]byte{0, 5, 118}: "NSM Technology Ltd.", + [3]byte{0, 5, 119}: "SM Information & Communication", + [3]byte{0, 5, 120}: "Private", + [3]byte{0, 5, 121}: "Universal Control Solution Corp.", + [3]byte{0, 5, 122}: "Overture Networks", + [3]byte{0, 5, 123}: "Chung Nam Electronic Co., Ltd.", + [3]byte{0, 5, 124}: "RCO Security AB", + [3]byte{0, 5, 125}: "Sun Communications, Inc.", + [3]byte{0, 5, 126}: "Eckelmann Steuerungstechnik GmbH", + [3]byte{0, 5, 127}: "Acqis Technology", + [3]byte{0, 5, 128}: "FibroLAN Ltd.", + [3]byte{0, 5, 129}: "Snell", + [3]byte{0, 5, 130}: "ClearCube Technology", + [3]byte{0, 5, 131}: "ImageCom Limited", + [3]byte{0, 5, 132}: "AbsoluteValue Systems, Inc.", + [3]byte{0, 5, 133}: "Juniper Networks", + [3]byte{0, 5, 134}: "Lucent Technologies", + [3]byte{0, 5, 135}: "Locus, Incorporated", + [3]byte{0, 5, 136}: "Sensoria Corp.", + [3]byte{0, 5, 137}: "National Datacomputer", + [3]byte{0, 5, 138}: "Netcom Co., Ltd.", + [3]byte{0, 5, 139}: "IPmental, Inc.", + [3]byte{0, 5, 140}: "Opentech Inc.", + [3]byte{0, 5, 141}: "Lynx Photonic Networks, Inc.", + [3]byte{0, 5, 142}: "Flextronics International GmbH & Co. Nfg. KG", + [3]byte{0, 5, 143}: "CLCsoft co.", + [3]byte{0, 5, 144}: "Swissvoice Ltd.", + [3]byte{0, 5, 145}: "Active Silicon Ltd", + [3]byte{0, 5, 146}: "Pultek Corp.", + [3]byte{0, 5, 147}: "Grammar Engine Inc.", + [3]byte{0, 5, 148}: "HMS Industrial Networks", + [3]byte{0, 5, 149}: "Alesis Corporation", + [3]byte{0, 5, 150}: "Genotech Co., Ltd.", + [3]byte{0, 5, 151}: "Eagle Traffic Control Systems", + [3]byte{0, 5, 152}: "CRONOS S.r.l.", + [3]byte{0, 5, 153}: "DRS Test and Energy Management or DRS-TEM", + [3]byte{0, 5, 154}: "Cisco Systems, Inc", + [3]byte{0, 5, 155}: "Cisco Systems, Inc", + [3]byte{0, 5, 156}: "Kleinknecht GmbH, Ing. Büro", + [3]byte{0, 5, 157}: "Daniel Computing Systems, Inc.", + [3]byte{0, 5, 158}: "Zinwell Corporation", + [3]byte{0, 5, 159}: "Yotta Networks, Inc.", + [3]byte{0, 5, 160}: "MOBILINE Kft.", + [3]byte{0, 5, 161}: "Zenocom", + [3]byte{0, 5, 162}: "CELOX Networks", + [3]byte{0, 5, 163}: "QEI, Inc.", + [3]byte{0, 5, 164}: "Lucid Voice Ltd.", + [3]byte{0, 5, 165}: "KOTT", + [3]byte{0, 5, 166}: "Extron Electronics", + [3]byte{0, 5, 167}: "Hyperchip, Inc.", + [3]byte{0, 5, 168}: "WYLE ELECTRONICS", + [3]byte{0, 5, 169}: "Princeton Networks, Inc.", + [3]byte{0, 5, 170}: "Moore Industries International Inc.", + [3]byte{0, 5, 171}: "Cyber Fone, Inc.", + [3]byte{0, 5, 172}: "Northern Digital, Inc.", + [3]byte{0, 5, 173}: "Topspin Communications, Inc.", + [3]byte{0, 5, 174}: "Mediaport USA", + [3]byte{0, 5, 175}: "InnoScan Computing A/S", + [3]byte{0, 5, 176}: "Korea Computer Technology Co., Ltd.", + [3]byte{0, 5, 177}: "ASB Technology BV", + [3]byte{0, 5, 178}: "Medison Co., Ltd.", + [3]byte{0, 5, 179}: "Asahi-Engineering Co., Ltd.", + [3]byte{0, 5, 180}: "Aceex Corporation", + [3]byte{0, 5, 181}: "Broadcom Technologies", + [3]byte{0, 5, 182}: "INSYS Microelectronics GmbH", + [3]byte{0, 5, 183}: "Arbor Technology Corp.", + [3]byte{0, 5, 184}: "Electronic Design Associates, Inc.", + [3]byte{0, 5, 185}: "Airvana, Inc.", + [3]byte{0, 5, 186}: "Area Netwoeks, Inc.", + [3]byte{0, 5, 187}: "Myspace AB", + [3]byte{0, 5, 188}: "Resource Data Management Ltd", + [3]byte{0, 5, 189}: "ROAX BV", + [3]byte{0, 5, 190}: "Kongsberg Seatex AS", + [3]byte{0, 5, 191}: "JustEzy Technology, Inc.", + [3]byte{0, 5, 192}: "Digital Network Alacarte Co., Ltd.", + [3]byte{0, 5, 193}: "A-Kyung Motion, Inc.", + [3]byte{0, 5, 194}: "Soronti, Inc.", + [3]byte{0, 5, 195}: "Pacific Instruments, Inc.", + [3]byte{0, 5, 196}: "Telect, Inc.", + [3]byte{0, 5, 197}: "Flaga HF", + [3]byte{0, 5, 198}: "Triz Communications", + [3]byte{0, 5, 199}: "I/F-COM A/S", + [3]byte{0, 5, 200}: "VERYTECH", + [3]byte{0, 5, 201}: "LG Innotek Co., Ltd.", + [3]byte{0, 5, 202}: "Hitron Technology, Inc.", + [3]byte{0, 5, 203}: "ROIS Technologies, Inc.", + [3]byte{0, 5, 204}: "Sumtel Communications, Inc.", + [3]byte{0, 5, 205}: "D&M Holdings Inc.", + [3]byte{0, 5, 206}: "Prolink Microsystems Corporation", + [3]byte{0, 5, 207}: "Thunder River Technologies, Inc.", + [3]byte{0, 5, 208}: "Solinet Systems", + [3]byte{0, 5, 209}: "Metavector Technologies", + [3]byte{0, 5, 210}: "DAP Technologies", + [3]byte{0, 5, 211}: "eProduction Solutions, Inc.", + [3]byte{0, 5, 212}: "FutureSmart Networks, Inc.", + [3]byte{0, 5, 213}: "Speedcom Wireless", + [3]byte{0, 5, 214}: "L-3 Linkabit", + [3]byte{0, 5, 215}: "Vista Imaging, Inc.", + [3]byte{0, 5, 216}: "Arescom, Inc.", + [3]byte{0, 5, 217}: "Techno Valley, Inc.", + [3]byte{0, 5, 218}: "Apex Automationstechnik", + [3]byte{0, 5, 219}: "PSI Nentec GmbH", + [3]byte{0, 5, 220}: "Cisco Systems, Inc", + [3]byte{0, 5, 221}: "Cisco Systems, Inc", + [3]byte{0, 5, 222}: "Gi Fone Korea, Inc.", + [3]byte{0, 5, 223}: "Electronic Innovation, Inc.", + [3]byte{0, 5, 224}: "Empirix Corp.", + [3]byte{0, 5, 225}: "Trellis Photonics, Ltd.", + [3]byte{0, 5, 226}: "Creativ Network Technologies", + [3]byte{0, 5, 227}: "LightSand Communications, Inc.", + [3]byte{0, 5, 228}: "Red Lion Controls Inc.", + [3]byte{0, 5, 229}: "Renishaw PLC", + [3]byte{0, 5, 230}: "Egenera, Inc.", + [3]byte{0, 5, 231}: "Netrake an AudioCodes Company", + [3]byte{0, 5, 232}: "TurboWave, Inc.", + [3]byte{0, 5, 233}: "Unicess Network, Inc.", + [3]byte{0, 5, 234}: "Rednix", + [3]byte{0, 5, 235}: "Blue Ridge Networks, Inc.", + [3]byte{0, 5, 236}: "Mosaic Systems Inc.", + [3]byte{0, 5, 237}: "Technikum Joanneum GmbH", + [3]byte{0, 5, 238}: "Vanderbilt International (SWE) AB ", + [3]byte{0, 5, 239}: "ADOIR Digital Technology", + [3]byte{0, 5, 240}: "SATEC", + [3]byte{0, 5, 241}: "Vrcom, Inc.", + [3]byte{0, 5, 242}: "Power R, Inc.", + [3]byte{0, 5, 243}: "Webyn", + [3]byte{0, 5, 244}: "System Base Co., Ltd.", + [3]byte{0, 5, 245}: "Geospace Technologies", + [3]byte{0, 5, 246}: "Young Chang Co. Ltd.", + [3]byte{0, 5, 247}: "Analog Devices, Inc.", + [3]byte{0, 5, 248}: "Real Time Access, Inc.", + [3]byte{0, 5, 249}: "TOA Corporation", + [3]byte{0, 5, 250}: "IPOptical, Inc.", + [3]byte{0, 5, 251}: "ShareGate, Inc.", + [3]byte{0, 5, 252}: "Schenck Pegasus Corp.", + [3]byte{0, 5, 253}: "PacketLight Networks Ltd.", + [3]byte{0, 5, 254}: "Traficon N.V.", + [3]byte{0, 5, 255}: "SNS Solutions, Inc.", + [3]byte{0, 6, 0}: "Toshiba Teli Corporation", + [3]byte{0, 6, 1}: "Otanikeiki Co., Ltd.", + [3]byte{0, 6, 2}: "Cirkitech Electronics Co.", + [3]byte{0, 6, 3}: "Baker Hughes Inc.", + [3]byte{0, 6, 4}: "@Track Communications, Inc.", + [3]byte{0, 6, 5}: "Inncom International, Inc.", + [3]byte{0, 6, 6}: "RapidWAN, Inc.", + [3]byte{0, 6, 7}: "Omni Directional Control Technology Inc.", + [3]byte{0, 6, 8}: "At-Sky SAS", + [3]byte{0, 6, 9}: "Crossport Systems", + [3]byte{0, 6, 10}: "Blue2space", + [3]byte{0, 6, 11}: "Artesyn Embedded Technologies", + [3]byte{0, 6, 12}: "Melco Industries, Inc.", + [3]byte{0, 6, 13}: "Wave7 Optics", + [3]byte{0, 6, 14}: "IGYS Systems, Inc.", + [3]byte{0, 6, 15}: "Narad Networks Inc", + [3]byte{0, 6, 16}: "Abeona Networks Inc", + [3]byte{0, 6, 17}: "Zeus Wireless, Inc.", + [3]byte{0, 6, 18}: "Accusys, Inc.", + [3]byte{0, 6, 19}: "Kawasaki Microelectronics Incorporated", + [3]byte{0, 6, 20}: "Prism Holdings", + [3]byte{0, 6, 21}: "Kimoto Electric Co., Ltd.", + [3]byte{0, 6, 22}: "Tel Net Co., Ltd.", + [3]byte{0, 6, 23}: "Redswitch Inc.", + [3]byte{0, 6, 24}: "DigiPower Manufacturing Inc.", + [3]byte{0, 6, 25}: "Connection Technology Systems", + [3]byte{0, 6, 26}: "Zetari Inc.", + [3]byte{0, 6, 27}: "Notebook Development Lab. Lenovo Japan Ltd.", + [3]byte{0, 6, 28}: "Hoshino Metal Industries, Ltd.", + [3]byte{0, 6, 29}: "MIP Telecom, Inc.", + [3]byte{0, 6, 30}: "Maxan Systems", + [3]byte{0, 6, 31}: "Vision Components GmbH", + [3]byte{0, 6, 32}: "Serial System Ltd.", + [3]byte{0, 6, 33}: "Hinox, Co., Ltd.", + [3]byte{0, 6, 34}: "Chung Fu Chen Yeh Enterprise Corp.", + [3]byte{0, 6, 35}: "MGE UPS Systems France", + [3]byte{0, 6, 36}: "Gentner Communications Corp.", + [3]byte{0, 6, 37}: "The Linksys Group, Inc.", + [3]byte{0, 6, 38}: "MWE GmbH", + [3]byte{0, 6, 39}: "Uniwide Technologies, Inc.", + [3]byte{0, 6, 40}: "Cisco Systems, Inc", + [3]byte{0, 6, 41}: "IBM Corp", + [3]byte{0, 6, 42}: "Cisco Systems, Inc", + [3]byte{0, 6, 43}: "INTRASERVER TECHNOLOGY", + [3]byte{0, 6, 44}: "Bivio Networks", + [3]byte{0, 6, 45}: "TouchStar Technologies, L.L.C.", + [3]byte{0, 6, 46}: "Aristos Logic Corp.", + [3]byte{0, 6, 47}: "Pivotech Systems Inc.", + [3]byte{0, 6, 48}: "Adtranz Sweden", + [3]byte{0, 6, 49}: "Calix Inc.", + [3]byte{0, 6, 50}: "Mesco Engineering GmbH", + [3]byte{0, 6, 51}: "Cross Match Technologies GmbH", + [3]byte{0, 6, 52}: "GTE Airfone Inc.", + [3]byte{0, 6, 53}: "PacketAir Networks, Inc.", + [3]byte{0, 6, 54}: "Jedai Broadband Networks", + [3]byte{0, 6, 55}: "Toptrend-Meta Information (ShenZhen) Inc.", + [3]byte{0, 6, 56}: "Sungjin C&C Co., Ltd.", + [3]byte{0, 6, 57}: "Newtec", + [3]byte{0, 6, 58}: "Dura Micro, Inc.", + [3]byte{0, 6, 59}: "Arcturus Networks Inc.", + [3]byte{0, 6, 60}: "Intrinsyc Software International Inc.", + [3]byte{0, 6, 61}: "Microwave Data Systems Inc.", + [3]byte{0, 6, 62}: "Opthos Inc.", + [3]byte{0, 6, 63}: "Everex Communications Inc.", + [3]byte{0, 6, 64}: "White Rock Networks", + [3]byte{0, 6, 65}: "ITCN", + [3]byte{0, 6, 66}: "Genetel Systems Inc.", + [3]byte{0, 6, 67}: "SONO Computer Co., Ltd.", + [3]byte{0, 6, 68}: "neix,Inc", + [3]byte{0, 6, 69}: "Meisei Electric Co. Ltd.", + [3]byte{0, 6, 70}: "ShenZhen XunBao Network Technology Co Ltd", + [3]byte{0, 6, 71}: "Etrali S.A.", + [3]byte{0, 6, 72}: "Seedsware, Inc.", + [3]byte{0, 6, 73}: "3M Deutschland GmbH", + [3]byte{0, 6, 74}: "Honeywell Co., Ltd. (KOREA)", + [3]byte{0, 6, 75}: "Alexon Co., Ltd.", + [3]byte{0, 6, 76}: "Invicta Networks, Inc.", + [3]byte{0, 6, 77}: "Sencore", + [3]byte{0, 6, 78}: "Broad Net Technology Inc.", + [3]byte{0, 6, 79}: "PRO-NETS Technology Corporation", + [3]byte{0, 6, 80}: "Tiburon Networks, Inc.", + [3]byte{0, 6, 81}: "Aspen Networks Inc.", + [3]byte{0, 6, 82}: "Cisco Systems, Inc", + [3]byte{0, 6, 83}: "Cisco Systems, Inc", + [3]byte{0, 6, 84}: "Winpresa Building Automation Technologies GmbH", + [3]byte{0, 6, 85}: "Yipee, Inc.", + [3]byte{0, 6, 86}: "Tactel AB", + [3]byte{0, 6, 87}: "Market Central, Inc.", + [3]byte{0, 6, 88}: "Helmut Fischer GmbH Institut für Elektronik und Messtechnik", + [3]byte{0, 6, 89}: "EAL (Apeldoorn) B.V.", + [3]byte{0, 6, 90}: "Strix Systems", + [3]byte{0, 6, 91}: "Dell Inc.", + [3]byte{0, 6, 92}: "Malachite Technologies, Inc.", + [3]byte{0, 6, 93}: "Heidelberg Web Systems", + [3]byte{0, 6, 94}: "Photuris, Inc.", + [3]byte{0, 6, 95}: "ECI Telecom Ltd.", + [3]byte{0, 6, 96}: "NADEX Co., Ltd.", + [3]byte{0, 6, 97}: "NIA Home Technologies Corp.", + [3]byte{0, 6, 98}: "MBM Technology Ltd.", + [3]byte{0, 6, 99}: "Human Technology Co., Ltd.", + [3]byte{0, 6, 100}: "Fostex Corporation", + [3]byte{0, 6, 101}: "Sunny Giken, Inc.", + [3]byte{0, 6, 102}: "Roving Networks", + [3]byte{0, 6, 103}: "Tripp Lite", + [3]byte{0, 6, 104}: "Vicon Industries Inc.", + [3]byte{0, 6, 105}: "Datasound Laboratories Ltd", + [3]byte{0, 6, 106}: "InfiniCon Systems, Inc.", + [3]byte{0, 6, 107}: "Sysmex Corporation", + [3]byte{0, 6, 108}: "Robinson Corporation", + [3]byte{0, 6, 109}: "Compuprint S.P.A.", + [3]byte{0, 6, 110}: "Delta Electronics, Inc.", + [3]byte{0, 6, 111}: "Korea Data Systems", + [3]byte{0, 6, 112}: "Upponetti Oy", + [3]byte{0, 6, 113}: "Softing AG", + [3]byte{0, 6, 114}: "Netezza", + [3]byte{0, 6, 115}: "TKH Security Solutions USA", + [3]byte{0, 6, 116}: "Spectrum Control, Inc.", + [3]byte{0, 6, 117}: "Banderacom, Inc.", + [3]byte{0, 6, 118}: "Novra Technologies Inc.", + [3]byte{0, 6, 119}: "SICK AG", + [3]byte{0, 6, 120}: "D&M Holdings Inc.", + [3]byte{0, 6, 121}: "Konami Corporation", + [3]byte{0, 6, 122}: "JMP Systems", + [3]byte{0, 6, 123}: "Toplink C&C Corporation", + [3]byte{0, 6, 124}: "Cisco Systems, Inc", + [3]byte{0, 6, 125}: "Takasago Ltd.", + [3]byte{0, 6, 126}: "WinCom Systems, Inc.", + [3]byte{0, 6, 127}: "Digeo, Inc.", + [3]byte{0, 6, 128}: "Card Access, Inc.", + [3]byte{0, 6, 129}: "Goepel Electronic GmbH", + [3]byte{0, 6, 130}: "Convedia", + [3]byte{0, 6, 131}: "Bravara Communications, Inc.", + [3]byte{0, 6, 132}: "Biacore AB", + [3]byte{0, 6, 133}: "NetNearU Corporation", + [3]byte{0, 6, 134}: "ZARDCOM Co., Ltd.", + [3]byte{0, 6, 135}: "Omnitron Systems Technology, Inc.", + [3]byte{0, 6, 136}: "Telways Communication Co., Ltd.", + [3]byte{0, 6, 137}: "yLez Technologies Pte Ltd", + [3]byte{0, 6, 138}: "NeuronNet Co. Ltd. R&D Center", + [3]byte{0, 6, 139}: "AirRunner Technologies, Inc.", + [3]byte{0, 6, 140}: "3COM CORPORATION", + [3]byte{0, 6, 141}: "SEPATON, Inc.", + [3]byte{0, 6, 142}: "HID Corporation", + [3]byte{0, 6, 143}: "Telemonitor, Inc.", + [3]byte{0, 6, 144}: "Euracom Communication GmbH", + [3]byte{0, 6, 145}: "PT Inovacao", + [3]byte{0, 6, 146}: "Intruvert Networks, Inc.", + [3]byte{0, 6, 147}: "Flexus Computer Technology, Inc.", + [3]byte{0, 6, 148}: "Mobillian Corporation", + [3]byte{0, 6, 149}: "Ensure Technologies, Inc.", + [3]byte{0, 6, 150}: "Advent Networks", + [3]byte{0, 6, 151}: "R & D Center", + [3]byte{0, 6, 152}: "egnite GmbH", + [3]byte{0, 6, 153}: "Vida Design Co.", + [3]byte{0, 6, 154}: "e & Tel", + [3]byte{0, 6, 155}: "AVT Audio Video Technologies GmbH", + [3]byte{0, 6, 156}: "Transmode Systems AB", + [3]byte{0, 6, 157}: "Petards Ltd", + [3]byte{0, 6, 158}: "UNIQA, Inc.", + [3]byte{0, 6, 159}: "Kuokoa Networks", + [3]byte{0, 6, 160}: "Mx Imaging", + [3]byte{0, 6, 161}: "Celsian Technologies, Inc.", + [3]byte{0, 6, 162}: "Microtune, Inc.", + [3]byte{0, 6, 163}: "Bitran Corporation", + [3]byte{0, 6, 164}: "INNOWELL Corp.", + [3]byte{0, 6, 165}: "PINON Corp.", + [3]byte{0, 6, 166}: "Artistic Licence Engineering Ltd", + [3]byte{0, 6, 167}: "Primarion", + [3]byte{0, 6, 168}: "KC Technology, Inc.", + [3]byte{0, 6, 169}: "Universal Instruments Corp.", + [3]byte{0, 6, 170}: "VT Miltope", + [3]byte{0, 6, 171}: "W-Link Systems, Inc.", + [3]byte{0, 6, 172}: "Intersoft Co.", + [3]byte{0, 6, 173}: "KB Electronics Ltd.", + [3]byte{0, 6, 174}: "Himachal Futuristic Communications Ltd", + [3]byte{0, 6, 175}: "Xalted Networks", + [3]byte{0, 6, 176}: "Comtech EF Data Corp.", + [3]byte{0, 6, 177}: "Sonicwall", + [3]byte{0, 6, 178}: "Linxtek Co.", + [3]byte{0, 6, 179}: "Diagraph Corporation", + [3]byte{0, 6, 180}: "Vorne Industries, Inc.", + [3]byte{0, 6, 181}: "Source Photonics, Inc.", + [3]byte{0, 6, 182}: "Nir-Or Israel Ltd.", + [3]byte{0, 6, 183}: "TELEM GmbH", + [3]byte{0, 6, 184}: "Bandspeed Pty Ltd", + [3]byte{0, 6, 185}: "A5TEK Corp.", + [3]byte{0, 6, 186}: "Westwave Communications", + [3]byte{0, 6, 187}: "ATI Technologies Inc.", + [3]byte{0, 6, 188}: "Macrolink, Inc.", + [3]byte{0, 6, 189}: "BNTECHNOLOGY Co., Ltd.", + [3]byte{0, 6, 190}: "Baumer Optronic GmbH", + [3]byte{0, 6, 191}: "Accella Technologies Co., Ltd.", + [3]byte{0, 6, 192}: "United Internetworks, Inc.", + [3]byte{0, 6, 193}: "Cisco Systems, Inc", + [3]byte{0, 6, 194}: "Smartmatic Corporation", + [3]byte{0, 6, 195}: "Schindler Elevator Ltd.", + [3]byte{0, 6, 196}: "Piolink Inc.", + [3]byte{0, 6, 197}: "INNOVI Technologies Limited", + [3]byte{0, 6, 198}: "lesswire AG", + [3]byte{0, 6, 199}: "RFNET Technologies Pte Ltd (S)", + [3]byte{0, 6, 200}: "Sumitomo Metal Micro Devices, Inc.", + [3]byte{0, 6, 201}: "Technical Marketing Research, Inc.", + [3]byte{0, 6, 202}: "American Computer & Digital Components, Inc. (ACDC)", + [3]byte{0, 6, 203}: "Jotron Electronics A/S", + [3]byte{0, 6, 204}: "JMI Electronics Co., Ltd.", + [3]byte{0, 6, 205}: "Leaf Imaging Ltd.", + [3]byte{0, 6, 206}: "DATENO", + [3]byte{0, 6, 207}: "Thales Avionics In-Flight Systems, LLC", + [3]byte{0, 6, 208}: "Elgar Electronics Corp.", + [3]byte{0, 6, 209}: "Tahoe Networks, Inc.", + [3]byte{0, 6, 210}: "Tundra Semiconductor Corp.", + [3]byte{0, 6, 211}: "Alpha Telecom, Inc. U.S.A.", + [3]byte{0, 6, 212}: "Interactive Objects, Inc.", + [3]byte{0, 6, 213}: "Diamond Systems Corp.", + [3]byte{0, 6, 214}: "Cisco Systems, Inc", + [3]byte{0, 6, 215}: "Cisco Systems, Inc", + [3]byte{0, 6, 216}: "Maple Optical Systems", + [3]byte{0, 6, 217}: "IPM-Net S.p.A.", + [3]byte{0, 6, 218}: "ITRAN Communications Ltd.", + [3]byte{0, 6, 219}: "ICHIPS Co., Ltd.", + [3]byte{0, 6, 220}: "Syabas Technology (Amquest)", + [3]byte{0, 6, 221}: "AT & T Laboratories - Cambridge Ltd", + [3]byte{0, 6, 222}: "Flash Technology", + [3]byte{0, 6, 223}: "AIDONIC Corporation", + [3]byte{0, 6, 224}: "MAT Co., Ltd.", + [3]byte{0, 6, 225}: "Techno Trade s.a", + [3]byte{0, 6, 226}: "Ceemax Technology Co., Ltd.", + [3]byte{0, 6, 227}: "Quantitative Imaging Corporation", + [3]byte{0, 6, 228}: "Citel Technologies Ltd.", + [3]byte{0, 6, 229}: "Fujian Newland Computer Ltd. Co.", + [3]byte{0, 6, 230}: "DongYang Telecom Co., Ltd.", + [3]byte{0, 6, 231}: "Bit Blitz Communications Inc.", + [3]byte{0, 6, 232}: "Optical Network Testing, Inc.", + [3]byte{0, 6, 233}: "Intime Corp.", + [3]byte{0, 6, 234}: "ELZET80 Mikrocomputer GmbH&Co. KG", + [3]byte{0, 6, 235}: "Global Data", + [3]byte{0, 6, 236}: "Harris Corporation", + [3]byte{0, 6, 237}: "Inara Networks", + [3]byte{0, 6, 238}: "Shenyang Neu-era Information & Technology Stock Co., Ltd", + [3]byte{0, 6, 239}: "Maxxan Systems, Inc.", + [3]byte{0, 6, 240}: "Digeo, Inc.", + [3]byte{0, 6, 241}: "Optillion", + [3]byte{0, 6, 242}: "Platys Communications", + [3]byte{0, 6, 243}: "AcceLight Networks", + [3]byte{0, 6, 244}: "Prime Electronics & Satellitics Inc.", + [3]byte{0, 6, 245}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 6, 246}: "Cisco Systems, Inc", + [3]byte{0, 6, 247}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 6, 248}: "The Boeing Company", + [3]byte{0, 6, 249}: "Mitsui Zosen Systems Research Inc.", + [3]byte{0, 6, 250}: "IP SQUARE Co, Ltd.", + [3]byte{0, 6, 251}: "Hitachi Printing Solutions, Ltd.", + [3]byte{0, 6, 252}: "Fnet Co., Ltd.", + [3]byte{0, 6, 253}: "Comjet Information Systems Corp.", + [3]byte{0, 6, 254}: "Ambrado, Inc", + [3]byte{0, 6, 255}: "Sheba Systems Co., Ltd.", + [3]byte{0, 7, 0}: "Zettamedia Korea", + [3]byte{0, 7, 1}: "RACAL-DATACOM", + [3]byte{0, 7, 2}: "Varian Medical Systems", + [3]byte{0, 7, 3}: "CSEE Transport", + [3]byte{0, 7, 4}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 7, 5}: "Endress & Hauser GmbH & Co", + [3]byte{0, 7, 6}: "Sanritz Corporation", + [3]byte{0, 7, 7}: "Interalia Inc.", + [3]byte{0, 7, 8}: "Bitrage Inc.", + [3]byte{0, 7, 9}: "Westerstrand Urfabrik AB", + [3]byte{0, 7, 10}: "Unicom Automation Co., Ltd.", + [3]byte{0, 7, 11}: "Novabase SGPS, SA", + [3]byte{0, 7, 12}: "SVA-Intrusion.com Co. Ltd.", + [3]byte{0, 7, 13}: "Cisco Systems, Inc", + [3]byte{0, 7, 14}: "Cisco Systems, Inc", + [3]byte{0, 7, 15}: "Fujant, Inc.", + [3]byte{0, 7, 16}: "Adax, Inc.", + [3]byte{0, 7, 17}: "Acterna", + [3]byte{0, 7, 18}: "JAL Information Technology", + [3]byte{0, 7, 19}: "IP One, Inc.", + [3]byte{0, 7, 20}: "Brightcom", + [3]byte{0, 7, 21}: "General Research of Electronics, Inc.", + [3]byte{0, 7, 22}: "J & S Marine Ltd.", + [3]byte{0, 7, 23}: "Wieland Electric GmbH", + [3]byte{0, 7, 24}: "iCanTek Co., Ltd.", + [3]byte{0, 7, 25}: "Mobiis Co., Ltd.", + [3]byte{0, 7, 26}: "Finedigital Inc.", + [3]byte{0, 7, 27}: "CDVI Americas Ltd", + [3]byte{0, 7, 28}: "AT&T", + [3]byte{0, 7, 29}: "Satelsa Sistemas Y Aplicaciones De Telecomunicaciones, S.A.", + [3]byte{0, 7, 30}: "Tri-M Engineering / Nupak Dev. Corp.", + [3]byte{0, 7, 31}: "European Systems Integration", + [3]byte{0, 7, 32}: "Trutzschler GmbH & Co. KG", + [3]byte{0, 7, 33}: "Formac Elektronik GmbH", + [3]byte{0, 7, 34}: "The Nielsen Company", + [3]byte{0, 7, 35}: "ELCON Systemtechnik GmbH", + [3]byte{0, 7, 36}: "Telemax Co., Ltd.", + [3]byte{0, 7, 37}: "Bematech International Corp.", + [3]byte{0, 7, 38}: "Shenzhen Gongjin Electronics Co., Ltd.", + [3]byte{0, 7, 39}: "Zi Corporation (HK) Ltd.", + [3]byte{0, 7, 40}: "Neo Telecom", + [3]byte{0, 7, 41}: "Kistler Instrumente AG", + [3]byte{0, 7, 42}: "Innovance Networks", + [3]byte{0, 7, 43}: "Jung Myung Telecom Co., Ltd.", + [3]byte{0, 7, 44}: "Fabricom", + [3]byte{0, 7, 45}: "CNSystems", + [3]byte{0, 7, 46}: "North Node AB", + [3]byte{0, 7, 47}: "Intransa, Inc.", + [3]byte{0, 7, 48}: "Hutchison OPTEL Telecom Technology Co., Ltd.", + [3]byte{0, 7, 49}: "Ophir-Spiricon LLC", + [3]byte{0, 7, 50}: "AAEON Technology Inc.", + [3]byte{0, 7, 51}: "DANCONTROL Engineering", + [3]byte{0, 7, 52}: "ONStor, Inc.", + [3]byte{0, 7, 53}: "Flarion Technologies, Inc.", + [3]byte{0, 7, 54}: "Data Video Technologies Co., Ltd.", + [3]byte{0, 7, 55}: "Soriya Co. Ltd.", + [3]byte{0, 7, 56}: "Young Technology Co., Ltd.", + [3]byte{0, 7, 57}: "Scotty Group Austria Gmbh", + [3]byte{0, 7, 58}: "Inventel Systemes", + [3]byte{0, 7, 59}: "Tenovis GmbH & Co KG", + [3]byte{0, 7, 60}: "Telecom Design", + [3]byte{0, 7, 61}: "Nanjing Postel Telecommunications Co., Ltd.", + [3]byte{0, 7, 62}: "China Great-Wall Computer Shenzhen Co., Ltd.", + [3]byte{0, 7, 63}: "Woojyun Systec Co., Ltd.", + [3]byte{0, 7, 64}: "BUFFALO.INC", + [3]byte{0, 7, 65}: "Sierra Automated Systems", + [3]byte{0, 7, 66}: "Ormazabal", + [3]byte{0, 7, 67}: "Chelsio Communications", + [3]byte{0, 7, 68}: "Unico, Inc.", + [3]byte{0, 7, 69}: "Radlan Computer Communications Ltd.", + [3]byte{0, 7, 70}: "TURCK, Inc.", + [3]byte{0, 7, 71}: "Mecalc", + [3]byte{0, 7, 72}: "The Imaging Source Europe", + [3]byte{0, 7, 73}: "CENiX Inc.", + [3]byte{0, 7, 74}: "Carl Valentin GmbH", + [3]byte{0, 7, 75}: "Daihen Corporation", + [3]byte{0, 7, 76}: "Beicom Inc.", + [3]byte{0, 7, 77}: "Zebra Technologies Corp.", + [3]byte{0, 7, 78}: "IPFRONT Inc", + [3]byte{0, 7, 79}: "Cisco Systems, Inc", + [3]byte{0, 7, 80}: "Cisco Systems, Inc", + [3]byte{0, 7, 81}: "m-u-t AG", + [3]byte{0, 7, 82}: "Rhythm Watch Co., Ltd.", + [3]byte{0, 7, 83}: "Beijing Qxcomm Technology Co., Ltd.", + [3]byte{0, 7, 84}: "Xyterra Computing, Inc.", + [3]byte{0, 7, 85}: "Lafon", + [3]byte{0, 7, 86}: "Juyoung Telecom", + [3]byte{0, 7, 87}: "Topcall International AG", + [3]byte{0, 7, 88}: "Dragonwave", + [3]byte{0, 7, 89}: "Boris Manufacturing Corp.", + [3]byte{0, 7, 90}: "Air Products and Chemicals, Inc.", + [3]byte{0, 7, 91}: "Gibson Guitars", + [3]byte{0, 7, 92}: "Eastman Kodak Company", + [3]byte{0, 7, 93}: "Celleritas Inc.", + [3]byte{0, 7, 94}: "Ametek Power Instruments", + [3]byte{0, 7, 95}: "VCS Video Communication Systems AG", + [3]byte{0, 7, 96}: "TOMIS Information & Telecom Corp.", + [3]byte{0, 7, 97}: "29530", + [3]byte{0, 7, 98}: "Group Sense Limited", + [3]byte{0, 7, 99}: "Sunniwell Cyber Tech. Co., Ltd.", + [3]byte{0, 7, 100}: "YoungWoo Telecom Co. Ltd.", + [3]byte{0, 7, 101}: "Jade Quantum Technologies, Inc.", + [3]byte{0, 7, 102}: "Chou Chin Industrial Co., Ltd.", + [3]byte{0, 7, 103}: "Yuxing Electronics Company Limited", + [3]byte{0, 7, 104}: "Danfoss A/S", + [3]byte{0, 7, 105}: "Italiana Macchi SpA", + [3]byte{0, 7, 106}: "NEXTEYE Co., Ltd.", + [3]byte{0, 7, 107}: "Stralfors AB", + [3]byte{0, 7, 108}: "Daehanet, Inc.", + [3]byte{0, 7, 109}: "Flexlight Networks", + [3]byte{0, 7, 110}: "Sinetica Corporation Limited", + [3]byte{0, 7, 111}: "Synoptics Limited", + [3]byte{0, 7, 112}: "Ubiquoss Inc", + [3]byte{0, 7, 113}: "Embedded System Corporation", + [3]byte{0, 7, 114}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{0, 7, 115}: "Ascom Powerline Communications Ltd.", + [3]byte{0, 7, 116}: "GuangZhou Thinker Technology Co. Ltd.", + [3]byte{0, 7, 117}: "Valence Semiconductor, Inc.", + [3]byte{0, 7, 118}: "Federal APD", + [3]byte{0, 7, 119}: "Motah Ltd.", + [3]byte{0, 7, 120}: "GERSTEL GmbH & Co. KG", + [3]byte{0, 7, 121}: "Sungil Telecom Co., Ltd.", + [3]byte{0, 7, 122}: "Infoware System Co., Ltd.", + [3]byte{0, 7, 123}: "Millimetrix Broadband Networks", + [3]byte{0, 7, 124}: "Westermo Teleindustri AB", + [3]byte{0, 7, 125}: "Cisco Systems, Inc", + [3]byte{0, 7, 126}: "Elrest GmbH", + [3]byte{0, 7, 127}: "J Communications Co., Ltd.", + [3]byte{0, 7, 128}: "Bluegiga Technologies OY", + [3]byte{0, 7, 129}: "Itron Inc.", + [3]byte{0, 7, 130}: "Oracle Corporation ", + [3]byte{0, 7, 131}: "SynCom Network, Inc.", + [3]byte{0, 7, 132}: "Cisco Systems, Inc", + [3]byte{0, 7, 133}: "Cisco Systems, Inc", + [3]byte{0, 7, 134}: "Wireless Networks Inc.", + [3]byte{0, 7, 135}: "Idea System Co., Ltd.", + [3]byte{0, 7, 136}: "Clipcomm, Inc.", + [3]byte{0, 7, 137}: "DONGWON SYSTEMS", + [3]byte{0, 7, 138}: "Mentor Data System Inc.", + [3]byte{0, 7, 139}: "Wegener Communications, Inc.", + [3]byte{0, 7, 140}: "Elektronikspecialisten i Borlange AB", + [3]byte{0, 7, 141}: "NetEngines Ltd.", + [3]byte{0, 7, 142}: "Garz & Friche GmbH", + [3]byte{0, 7, 143}: "Emkay Innovative Products", + [3]byte{0, 7, 144}: "Tri-M Technologies (s) Limited", + [3]byte{0, 7, 145}: "International Data Communications, Inc.", + [3]byte{0, 7, 146}: "Sütron Electronic GmbH", + [3]byte{0, 7, 147}: "Shin Satellite Public Company Limited", + [3]byte{0, 7, 148}: "Simple Devices, Inc.", + [3]byte{0, 7, 149}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 7, 150}: "LSI Systems, Inc.", + [3]byte{0, 7, 151}: "Netpower Co., Ltd.", + [3]byte{0, 7, 152}: "Selea SRL", + [3]byte{0, 7, 153}: "Tipping Point Technologies, Inc.", + [3]byte{0, 7, 154}: "Verint Systems Inc", + [3]byte{0, 7, 155}: "Aurora Networks", + [3]byte{0, 7, 156}: "Golden Electronics Technology Co., Ltd.", + [3]byte{0, 7, 157}: "Musashi Co., Ltd.", + [3]byte{0, 7, 158}: "Ilinx Co., Ltd.", + [3]byte{0, 7, 159}: "Action Digital Inc.", + [3]byte{0, 7, 160}: "e-Watch Inc.", + [3]byte{0, 7, 161}: "VIASYS Healthcare GmbH", + [3]byte{0, 7, 162}: "Opteon Corporation", + [3]byte{0, 7, 163}: "Ositis Software, Inc.", + [3]byte{0, 7, 164}: "GN Netcom Ltd.", + [3]byte{0, 7, 165}: "Y.D.K Co. Ltd.", + [3]byte{0, 7, 166}: "Leviton Manufacturing Co., Inc.", + [3]byte{0, 7, 167}: "A-Z Inc.", + [3]byte{0, 7, 168}: "Haier Group Technologies Ltd.", + [3]byte{0, 7, 169}: "Novasonics", + [3]byte{0, 7, 170}: "Quantum Data Inc.", + [3]byte{0, 7, 171}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 7, 172}: "Eolring", + [3]byte{0, 7, 173}: "Pentacon GmbH Foto-und Feinwerktechnik", + [3]byte{0, 7, 174}: "Britestream Networks, Inc.", + [3]byte{0, 7, 175}: "Red Lion Controls, LP", + [3]byte{0, 7, 176}: "Office Details, Inc.", + [3]byte{0, 7, 177}: "Equator Technologies", + [3]byte{0, 7, 178}: "Transaccess S.A.", + [3]byte{0, 7, 179}: "Cisco Systems, Inc", + [3]byte{0, 7, 180}: "Cisco Systems, Inc", + [3]byte{0, 7, 181}: "Any One Wireless Ltd.", + [3]byte{0, 7, 182}: "Telecom Technology Ltd.", + [3]byte{0, 7, 183}: "Samurai Ind. Prods Eletronicos Ltda", + [3]byte{0, 7, 184}: "Corvalent Corporation", + [3]byte{0, 7, 185}: "Ginganet Corporation", + [3]byte{0, 7, 186}: "UTStarcom Inc", + [3]byte{0, 7, 187}: "Candera Inc.", + [3]byte{0, 7, 188}: "Identix Inc.", + [3]byte{0, 7, 189}: "Radionet Ltd.", + [3]byte{0, 7, 190}: "DataLogic SpA", + [3]byte{0, 7, 191}: "Armillaire Technologies, Inc.", + [3]byte{0, 7, 192}: "NetZerver Inc.", + [3]byte{0, 7, 193}: "Overture Networks, Inc.", + [3]byte{0, 7, 194}: "Netsys Telecom", + [3]byte{0, 7, 195}: "Thomson", + [3]byte{0, 7, 196}: "JEAN Co. Ltd.", + [3]byte{0, 7, 197}: "Gcom, Inc.", + [3]byte{0, 7, 198}: "VDS Vosskuhler GmbH", + [3]byte{0, 7, 199}: "Synectics Systems Limited", + [3]byte{0, 7, 200}: "Brain21, Inc.", + [3]byte{0, 7, 201}: "Technol Seven Co., Ltd.", + [3]byte{0, 7, 202}: "Creatix Polymedia Ges Fur Kommunikaitonssysteme", + [3]byte{0, 7, 203}: "FREEBOX SAS", + [3]byte{0, 7, 204}: "Kaba Benzing GmbH", + [3]byte{0, 7, 205}: "Kumoh Electronic Co, Ltd", + [3]byte{0, 7, 206}: "Cabletime Limited", + [3]byte{0, 7, 207}: "Anoto AB", + [3]byte{0, 7, 208}: "Automat Engenharia de Automação Ltda.", + [3]byte{0, 7, 209}: "Spectrum Signal Processing Inc.", + [3]byte{0, 7, 210}: "Logopak Systeme GmbH & Co. KG", + [3]byte{0, 7, 211}: "SPGPrints B.V.", + [3]byte{0, 7, 212}: "Zhejiang Yutong Network Communication Co Ltd.", + [3]byte{0, 7, 213}: "3e Technologies Int;., Inc.", + [3]byte{0, 7, 214}: "Commil Ltd.", + [3]byte{0, 7, 215}: "Caporis Networks AG", + [3]byte{0, 7, 216}: "Hitron Technologies. Inc", + [3]byte{0, 7, 217}: "Splicecom", + [3]byte{0, 7, 218}: "Neuro Telecom Co., Ltd.", + [3]byte{0, 7, 219}: "Kirana Networks, Inc.", + [3]byte{0, 7, 220}: "Atek Co, Ltd.", + [3]byte{0, 7, 221}: "Cradle Technologies", + [3]byte{0, 7, 222}: "eCopilt AB", + [3]byte{0, 7, 223}: "Vbrick Systems Inc.", + [3]byte{0, 7, 224}: "Palm Inc.", + [3]byte{0, 7, 225}: "WIS Communications Co. Ltd.", + [3]byte{0, 7, 226}: "Bitworks, Inc.", + [3]byte{0, 7, 227}: "Navcom Technology, Inc.", + [3]byte{0, 7, 228}: "SoftRadio Co., Ltd.", + [3]byte{0, 7, 229}: "Coup Corporation", + [3]byte{0, 7, 230}: "edgeflow Canada Inc.", + [3]byte{0, 7, 231}: "FreeWave Technologies", + [3]byte{0, 7, 232}: "EdgeWave", + [3]byte{0, 7, 233}: "Intel Corporation", + [3]byte{0, 7, 234}: "Massana, Inc.", + [3]byte{0, 7, 235}: "Cisco Systems, Inc", + [3]byte{0, 7, 236}: "Cisco Systems, Inc", + [3]byte{0, 7, 237}: "Altera Corporation", + [3]byte{0, 7, 238}: "telco Informationssysteme GmbH", + [3]byte{0, 7, 239}: "Lockheed Martin Tactical Systems", + [3]byte{0, 7, 240}: "LogiSync LLC", + [3]byte{0, 7, 241}: "TeraBurst Networks Inc.", + [3]byte{0, 7, 242}: "IOA Corporation", + [3]byte{0, 7, 243}: "Thinkengine Networks", + [3]byte{0, 7, 244}: "Eletex Co., Ltd.", + [3]byte{0, 7, 245}: "Bridgeco Co AG", + [3]byte{0, 7, 246}: "Qqest Software Systems", + [3]byte{0, 7, 247}: "Galtronics", + [3]byte{0, 7, 248}: "ITDevices, Inc.", + [3]byte{0, 7, 249}: "Sensaphone", + [3]byte{0, 7, 250}: "ITT Co., Ltd.", + [3]byte{0, 7, 251}: "Giga Stream UMTS Technologies GmbH", + [3]byte{0, 7, 252}: "Adept Systems Inc.", + [3]byte{0, 7, 253}: "LANergy Ltd.", + [3]byte{0, 7, 254}: "Rigaku Corporation", + [3]byte{0, 7, 255}: "Gluon Networks", + [3]byte{0, 8, 0}: "MULTITECH SYSTEMS, INC.", + [3]byte{0, 8, 1}: "HighSpeed Surfing Inc.", + [3]byte{0, 8, 2}: "Hewlett Packard", + [3]byte{0, 8, 3}: "Cos Tron", + [3]byte{0, 8, 4}: "ICA Inc.", + [3]byte{0, 8, 5}: "Techno-Holon Corporation", + [3]byte{0, 8, 6}: "Raonet Systems, Inc.", + [3]byte{0, 8, 7}: "Access Devices Limited", + [3]byte{0, 8, 8}: "PPT Vision, Inc.", + [3]byte{0, 8, 9}: "Systemonic AG", + [3]byte{0, 8, 10}: "Espera-Werke GmbH", + [3]byte{0, 8, 11}: "Birka BPA Informationssystem AB", + [3]byte{0, 8, 12}: "VDA Elettronica spa", + [3]byte{0, 8, 13}: "Toshiba", + [3]byte{0, 8, 14}: "ARRIS Group, Inc.", + [3]byte{0, 8, 15}: "Proximion Fiber Optics AB", + [3]byte{0, 8, 16}: "Key Technology, Inc.", + [3]byte{0, 8, 17}: "VOIX Corporation", + [3]byte{0, 8, 18}: "GM-2 Corporation", + [3]byte{0, 8, 19}: "Diskbank, Inc.", + [3]byte{0, 8, 20}: "TIL Technologies", + [3]byte{0, 8, 21}: "CATS Co., Ltd.", + [3]byte{0, 8, 22}: "Bluelon ApS", + [3]byte{0, 8, 23}: "EmergeCore Networks LLC", + [3]byte{0, 8, 24}: "Pixelworks, Inc.", + [3]byte{0, 8, 25}: "Banksys", + [3]byte{0, 8, 26}: "Sanrad Intelligence Storage Communications (2000) Ltd.", + [3]byte{0, 8, 27}: "Windigo Systems", + [3]byte{0, 8, 28}: "@pos.com", + [3]byte{0, 8, 29}: "Ipsil, Incorporated", + [3]byte{0, 8, 30}: "Repeatit AB", + [3]byte{0, 8, 31}: "Pou Yuen Tech Corp. Ltd.", + [3]byte{0, 8, 32}: "Cisco Systems, Inc", + [3]byte{0, 8, 33}: "Cisco Systems, Inc", + [3]byte{0, 8, 34}: "InPro Comm", + [3]byte{0, 8, 35}: "Texa Corp.", + [3]byte{0, 8, 36}: "Nuance Document Imaging", + [3]byte{0, 8, 37}: "Acme Packet", + [3]byte{0, 8, 38}: "Colorado Med Tech", + [3]byte{0, 8, 39}: "ADB Broadband Italia", + [3]byte{0, 8, 40}: "Koei Engineering Ltd.", + [3]byte{0, 8, 41}: "Aval Nagasaki Corporation", + [3]byte{0, 8, 42}: "Powerwallz Network Security", + [3]byte{0, 8, 43}: "Wooksung Electronics, Inc.", + [3]byte{0, 8, 44}: "Homag AG", + [3]byte{0, 8, 45}: "Indus Teqsite Private Limited", + [3]byte{0, 8, 46}: "Multitone Electronics PLC", + [3]byte{0, 8, 47}: "Cisco Systems, Inc", + [3]byte{0, 8, 48}: "Cisco Systems, Inc", + [3]byte{0, 8, 49}: "Cisco Systems, Inc", + [3]byte{0, 8, 50}: "Cisco Systems, Inc", + [3]byte{0, 8, 78}: "DivergeNet, Inc.", + [3]byte{0, 8, 79}: "Qualstar Corporation", + [3]byte{0, 8, 80}: "Arizona Instrument Corp.", + [3]byte{0, 8, 81}: "Canadian Bank Note Company, Ltd.", + [3]byte{0, 8, 82}: "Davolink Co. Inc.", + [3]byte{0, 8, 83}: "Schleicher GmbH & Co. Relaiswerke KG", + [3]byte{0, 8, 84}: "Netronix, Inc.", + [3]byte{0, 8, 85}: "NASA-Goddard Space Flight Center", + [3]byte{0, 8, 86}: "Gamatronic Electronic Industries Ltd.", + [3]byte{0, 8, 87}: "Polaris Networks, Inc.", + [3]byte{0, 8, 88}: "Novatechnology Inc.", + [3]byte{0, 8, 89}: "ShenZhen Unitone Electronics Co., Ltd.", + [3]byte{0, 8, 90}: "IntiGate Inc.", + [3]byte{0, 8, 91}: "Hanbit Electronics Co., Ltd.", + [3]byte{0, 8, 92}: "Shanghai Dare Technologies Co. Ltd.", + [3]byte{0, 8, 93}: "Aastra", + [3]byte{0, 8, 94}: "PCO AG", + [3]byte{0, 8, 95}: "Picanol N.V.", + [3]byte{0, 8, 96}: "LodgeNet Entertainment Corp.", + [3]byte{0, 8, 97}: "SoftEnergy Co., Ltd.", + [3]byte{0, 8, 98}: "NEC Eluminant Technologies, Inc.", + [3]byte{0, 8, 99}: "Entrisphere Inc.", + [3]byte{0, 8, 100}: "Fasy S.p.A.", + [3]byte{0, 8, 101}: "JASCOM CO., LTD", + [3]byte{0, 8, 102}: "DSX Access Systems, Inc.", + [3]byte{0, 8, 103}: "Uptime Devices", + [3]byte{0, 8, 104}: "PurOptix", + [3]byte{0, 8, 105}: "Command-e Technology Co.,Ltd.", + [3]byte{0, 8, 106}: "Securiton Gmbh", + [3]byte{0, 8, 107}: "MIPSYS", + [3]byte{0, 8, 108}: "Plasmon LMS", + [3]byte{0, 8, 109}: "Missouri FreeNet", + [3]byte{0, 8, 110}: "Hyglo AB", + [3]byte{0, 8, 111}: "Resources Computer Network Ltd.", + [3]byte{0, 8, 112}: "Rasvia Systems, Inc.", + [3]byte{0, 8, 113}: "NORTHDATA Co., Ltd.", + [3]byte{0, 8, 114}: "Sorenson Communications", + [3]byte{0, 8, 115}: "DapTechnology B.V.", + [3]byte{0, 8, 116}: "Dell Inc.", + [3]byte{0, 8, 117}: "Acorp Electronics Corp.", + [3]byte{0, 8, 118}: "SDSystem", + [3]byte{0, 8, 119}: "Liebert-Hiross Spa", + [3]byte{0, 8, 120}: "Benchmark Storage Innovations", + [3]byte{0, 8, 121}: "CEM Corporation", + [3]byte{0, 8, 122}: "Wipotec GmbH", + [3]byte{0, 8, 123}: "RTX Telecom A/S", + [3]byte{0, 8, 124}: "Cisco Systems, Inc", + [3]byte{0, 8, 125}: "Cisco Systems, Inc", + [3]byte{0, 8, 126}: "Bon Electro-Telecom Inc.", + [3]byte{0, 8, 127}: "SPAUN electronic GmbH & Co. KG", + [3]byte{0, 8, 128}: "BroadTel Canada Communications inc.", + [3]byte{0, 8, 129}: "DIGITAL HANDS CO.,LTD.", + [3]byte{0, 8, 130}: "SIGMA CORPORATION", + [3]byte{0, 8, 131}: "Hewlett Packard", + [3]byte{0, 8, 132}: "Index Braille AB", + [3]byte{0, 8, 133}: "EMS Dr. Thomas Wünsche", + [3]byte{0, 8, 134}: "Hansung Teliann, Inc.", + [3]byte{0, 8, 135}: "Maschinenfabrik Reinhausen GmbH", + [3]byte{0, 8, 136}: "OULLIM Information Technology Inc,.", + [3]byte{0, 8, 137}: "Echostar Technologies Corp", + [3]byte{0, 8, 138}: "Minds@Work", + [3]byte{0, 8, 139}: "Tropic Networks Inc.", + [3]byte{0, 8, 140}: "Quanta Network Systems Inc.", + [3]byte{0, 8, 141}: "Sigma-Links Inc.", + [3]byte{0, 8, 142}: "Nihon Computer Co., Ltd.", + [3]byte{0, 8, 143}: "ADVANCED DIGITAL TECHNOLOGY", + [3]byte{0, 8, 144}: "AVILINKS SA", + [3]byte{0, 8, 145}: "Lyan Inc.", + [3]byte{0, 8, 146}: "EM Solutions", + [3]byte{0, 8, 147}: "LE INFORMATION COMMUNICATION INC.", + [3]byte{0, 8, 148}: "InnoVISION Multimedia Ltd.", + [3]byte{0, 8, 149}: "DIRC Technologie GmbH & Co.KG", + [3]byte{0, 8, 150}: "Printronix, Inc.", + [3]byte{0, 8, 151}: "Quake Technologies", + [3]byte{0, 8, 152}: "Gigabit Optics Corporation", + [3]byte{0, 8, 153}: "Netbind, Inc.", + [3]byte{0, 8, 154}: "Alcatel Microelectronics", + [3]byte{0, 8, 155}: "ICP Electronics Inc.", + [3]byte{0, 8, 156}: "Elecs Industry Co., Ltd.", + [3]byte{0, 8, 157}: "UHD-Elektronik", + [3]byte{0, 8, 158}: "Beijing Enter-Net co.LTD", + [3]byte{0, 8, 159}: "EFM Networks", + [3]byte{0, 8, 160}: "Stotz Feinmesstechnik GmbH", + [3]byte{0, 8, 161}: "CNet Technology Inc.", + [3]byte{0, 8, 162}: "ADI Engineering, Inc.", + [3]byte{0, 8, 163}: "Cisco Systems, Inc", + [3]byte{0, 8, 164}: "Cisco Systems, Inc", + [3]byte{0, 8, 165}: "Peninsula Systems Inc.", + [3]byte{0, 8, 166}: "Multiware & Image Co., Ltd.", + [3]byte{0, 8, 167}: "iLogic Inc.", + [3]byte{0, 8, 168}: "Systec Co., Ltd.", + [3]byte{0, 8, 169}: "SangSang Technology, Inc.", + [3]byte{0, 8, 170}: "KARAM", + [3]byte{0, 8, 171}: "EnerLinx.com, Inc.", + [3]byte{0, 8, 172}: "Eltromat GmbH", + [3]byte{0, 8, 173}: "Toyo-Linx Co., Ltd.", + [3]byte{0, 8, 174}: "PacketFront Network Products AB", + [3]byte{0, 8, 175}: "Novatec Corporation", + [3]byte{0, 8, 176}: "BKtel communications GmbH", + [3]byte{0, 8, 177}: "ProQuent Systems", + [3]byte{0, 8, 178}: "SHENZHEN COMPASS TECHNOLOGY DEVELOPMENT CO.,LTD", + [3]byte{0, 8, 179}: "Fastwel", + [3]byte{0, 8, 180}: "SYSPOL", + [3]byte{0, 8, 181}: "TAI GUEN ENTERPRISE CO., LTD", + [3]byte{0, 8, 182}: "RouteFree, Inc.", + [3]byte{0, 8, 183}: "HIT Incorporated", + [3]byte{0, 8, 184}: "E.F. Johnson", + [3]byte{0, 8, 185}: "Kaonmedia CO., LTD.", + [3]byte{0, 8, 186}: "Erskine Systems Ltd", + [3]byte{0, 8, 187}: "NetExcell", + [3]byte{0, 8, 188}: "Ilevo AB", + [3]byte{0, 8, 189}: "TEPG-US", + [3]byte{0, 8, 190}: "XENPAK MSA Group", + [3]byte{0, 8, 191}: "Aptus Elektronik AB", + [3]byte{0, 8, 192}: "ASA SYSTEMS", + [3]byte{0, 8, 193}: "Avistar Communications Corporation", + [3]byte{0, 8, 194}: "Cisco Systems, Inc", + [3]byte{0, 8, 195}: "Contex A/S", + [3]byte{0, 8, 196}: "Hikari Co.,Ltd.", + [3]byte{0, 8, 197}: "Liontech Co., Ltd.", + [3]byte{0, 8, 198}: "Philips Consumer Communications", + [3]byte{0, 8, 199}: "Hewlett Packard", + [3]byte{0, 8, 200}: "Soneticom, Inc.", + [3]byte{0, 8, 201}: "TechniSat Digital GmbH", + [3]byte{0, 8, 202}: "TwinHan Technology Co.,Ltd", + [3]byte{0, 8, 203}: "Zeta Broadband Inc.", + [3]byte{0, 8, 204}: "Remotec, Inc.", + [3]byte{0, 8, 205}: "With-Net Inc", + [3]byte{0, 8, 206}: "IPMobileNet Inc.", + [3]byte{0, 8, 207}: "Nippon Koei Power Systems Co., Ltd.", + [3]byte{0, 8, 208}: "Musashi Engineering Co., LTD.", + [3]byte{0, 8, 209}: "KAREL INC.", + [3]byte{0, 8, 210}: "ZOOM Networks Inc.", + [3]byte{0, 8, 211}: "Hercules Technologies S.A.S.", + [3]byte{0, 8, 212}: "IneoQuest Technologies, Inc", + [3]byte{0, 8, 213}: "Vanguard Networks Solutions, LLC", + [3]byte{0, 8, 214}: "HASSNET Inc.", + [3]byte{0, 8, 215}: "HOW CORPORATION", + [3]byte{0, 8, 216}: "Dowkey Microwave", + [3]byte{0, 8, 217}: "Mitadenshi Co.,LTD", + [3]byte{0, 8, 218}: "SofaWare Technologies Ltd.", + [3]byte{0, 8, 219}: "Corrigent Systems", + [3]byte{0, 8, 220}: "Wiznet", + [3]byte{0, 8, 221}: "Telena Communications, Inc.", + [3]byte{0, 8, 222}: "3UP Systems", + [3]byte{0, 8, 223}: "Alistel Inc.", + [3]byte{0, 8, 224}: "ATO Technology Ltd.", + [3]byte{0, 8, 225}: "Barix AG", + [3]byte{0, 8, 226}: "Cisco Systems, Inc", + [3]byte{0, 8, 227}: "Cisco Systems, Inc", + [3]byte{0, 8, 228}: "Envenergy Inc", + [3]byte{0, 8, 229}: "IDK Corporation", + [3]byte{0, 8, 230}: "Littlefeet", + [3]byte{0, 8, 231}: "SHI ControlSystems,Ltd.", + [3]byte{0, 8, 232}: "Excel Master Ltd.", + [3]byte{0, 8, 233}: "NextGig", + [3]byte{0, 8, 234}: "Motion Control Engineering, Inc", + [3]byte{0, 8, 235}: "ROMWin Co.,Ltd.", + [3]byte{0, 8, 236}: "Optical Zonu Corporation", + [3]byte{0, 8, 237}: "ST&T Instrument Corp.", + [3]byte{0, 8, 238}: "Logic Product Development", + [3]byte{0, 8, 239}: "DIBAL,S.A.", + [3]byte{0, 8, 240}: "Next Generation Systems, Inc.", + [3]byte{0, 8, 241}: "Voltaire", + [3]byte{0, 8, 242}: "C&S Technology", + [3]byte{0, 8, 243}: "WANY", + [3]byte{0, 8, 244}: "Bluetake Technology Co., Ltd.", + [3]byte{0, 8, 245}: "YESTECHNOLOGY Co.,Ltd.", + [3]byte{0, 8, 246}: "Sumitomo Electric Industries,Ltd", + [3]byte{0, 8, 247}: "Hitachi Ltd, Semiconductor & Integrated Circuits Gr", + [3]byte{0, 8, 248}: "UTC CCS", + [3]byte{0, 8, 249}: "Artesyn Embedded Technologies", + [3]byte{0, 8, 250}: "Karl E.Brinkmann GmbH", + [3]byte{0, 8, 251}: "SonoSite, Inc.", + [3]byte{0, 8, 252}: "Gigaphoton Inc.", + [3]byte{0, 8, 253}: "BlueKorea Co., Ltd.", + [3]byte{0, 8, 254}: "UNIK C&C Co.,Ltd.", + [3]byte{0, 8, 255}: "Trilogy Communications Ltd", + [3]byte{0, 9, 0}: "TMT", + [3]byte{0, 9, 1}: "Shenzhen Shixuntong Information & Technoligy Co", + [3]byte{0, 9, 2}: "Redline Communications Inc.", + [3]byte{0, 9, 3}: "Panasas, Inc", + [3]byte{0, 9, 4}: "MONDIAL electronic", + [3]byte{0, 9, 5}: "iTEC Technologies Ltd.", + [3]byte{0, 9, 6}: "Esteem Networks", + [3]byte{0, 9, 7}: "Chrysalis Development", + [3]byte{0, 9, 8}: "VTech Technology Corp.", + [3]byte{0, 9, 9}: "Telenor Connect A/S", + [3]byte{0, 9, 10}: "SnedFar Technology Co., Ltd.", + [3]byte{0, 9, 11}: "MTL Instruments PLC", + [3]byte{0, 9, 12}: "Mayekawa Mfg. Co. Ltd.", + [3]byte{0, 9, 13}: "LEADER ELECTRONICS CORP.", + [3]byte{0, 9, 14}: "Helix Technology Inc.", + [3]byte{0, 9, 15}: "Fortinet Inc.", + [3]byte{0, 9, 16}: "Simple Access Inc.", + [3]byte{0, 9, 17}: "Cisco Systems, Inc", + [3]byte{0, 9, 18}: "Cisco Systems, Inc", + [3]byte{0, 9, 19}: "SystemK Corporation", + [3]byte{0, 9, 20}: "COMPUTROLS INC.", + [3]byte{0, 9, 21}: "CAS Corp.", + [3]byte{0, 9, 22}: "Listman Home Technologies, Inc.", + [3]byte{0, 9, 23}: "WEM Technology Inc", + [3]byte{0, 9, 24}: "SAMSUNG TECHWIN CO.,LTD", + [3]byte{0, 9, 25}: "MDS Gateways", + [3]byte{0, 9, 26}: "Macat Optics & Electronics Co., Ltd.", + [3]byte{0, 9, 27}: "Digital Generation Inc.", + [3]byte{0, 9, 28}: "CacheVision, Inc", + [3]byte{0, 9, 29}: "Proteam Computer Corporation", + [3]byte{0, 9, 30}: "Firstech Technology Corp.", + [3]byte{0, 9, 31}: "A&D Co., Ltd.", + [3]byte{0, 9, 32}: "EpoX COMPUTER CO.,LTD.", + [3]byte{0, 9, 33}: "Planmeca Oy", + [3]byte{0, 9, 34}: "TST Biometrics GmbH", + [3]byte{0, 9, 35}: "Heaman System Co., Ltd", + [3]byte{0, 9, 36}: "Telebau GmbH", + [3]byte{0, 9, 37}: "VSN Systemen BV", + [3]byte{0, 9, 38}: "YODA COMMUNICATIONS, INC.", + [3]byte{0, 9, 39}: "TOYOKEIKI CO.,LTD.", + [3]byte{0, 9, 40}: "Telecore", + [3]byte{0, 9, 41}: "Sanyo Industries (UK) Limited", + [3]byte{0, 9, 42}: "MYTECS Co.,Ltd.", + [3]byte{0, 9, 43}: "iQstor Networks, Inc.", + [3]byte{0, 9, 44}: "Hitpoint Inc.", + [3]byte{0, 9, 45}: "HTC Corporation", + [3]byte{0, 9, 46}: "B&Tech System Inc.", + [3]byte{0, 9, 47}: "Akom Technology Corporation", + [3]byte{0, 9, 48}: "AeroConcierge Inc.", + [3]byte{0, 9, 49}: "Future Internet, Inc.", + [3]byte{0, 9, 50}: "Omnilux", + [3]byte{0, 9, 51}: "Ophit Co.Ltd.", + [3]byte{0, 9, 52}: "Dream-Multimedia-Tv GmbH", + [3]byte{0, 9, 53}: "Sandvine Incorporated", + [3]byte{0, 9, 54}: "Ipetronik GmbH & Co. KG", + [3]byte{0, 9, 55}: "Inventec Appliance Corp", + [3]byte{0, 9, 56}: "Allot Communications", + [3]byte{0, 9, 57}: "ShibaSoku Co.,Ltd.", + [3]byte{0, 9, 58}: "Molex", + [3]byte{0, 9, 59}: "HYUNDAI NETWORKS INC.", + [3]byte{0, 9, 60}: "Jacques Technologies P/L", + [3]byte{0, 9, 61}: "Newisys,Inc.", + [3]byte{0, 9, 62}: "C&I Technologies", + [3]byte{0, 9, 63}: "Double-Win Enterpirse CO., LTD", + [3]byte{0, 9, 64}: "AGFEO GmbH & Co. KG", + [3]byte{0, 9, 65}: "Allied Telesis R&D Center K.K.", + [3]byte{0, 9, 66}: "Wireless Technologies, Inc", + [3]byte{0, 9, 67}: "Cisco Systems, Inc", + [3]byte{0, 9, 68}: "Cisco Systems, Inc", + [3]byte{0, 9, 69}: "Palmmicro Communications Inc", + [3]byte{0, 9, 70}: "Cluster Labs GmbH", + [3]byte{0, 9, 71}: "Aztek, Inc.", + [3]byte{0, 9, 72}: "Vista Control Systems, Corp.", + [3]byte{0, 9, 73}: "Glyph Technologies Inc.", + [3]byte{0, 9, 74}: "Homenet Communications", + [3]byte{0, 9, 75}: "FillFactory NV", + [3]byte{0, 9, 76}: "Communication Weaver Co.,Ltd.", + [3]byte{0, 9, 77}: "Braintree Communications Pty Ltd", + [3]byte{0, 9, 78}: "BARTECH SYSTEMS INTERNATIONAL, INC", + [3]byte{0, 9, 79}: "elmegt GmbH & Co. KG", + [3]byte{0, 9, 80}: "Independent Storage Corporation", + [3]byte{0, 9, 81}: "Apogee Imaging Systems", + [3]byte{0, 9, 82}: "Auerswald GmbH & Co. KG", + [3]byte{0, 9, 83}: "Linkage System Integration Co.Ltd.", + [3]byte{0, 9, 84}: "AMiT spol. s. r. o.", + [3]byte{0, 9, 85}: "Young Generation International Corp.", + [3]byte{0, 9, 86}: "Network Systems Group, Ltd. (NSG)", + [3]byte{0, 9, 87}: "Supercaller, Inc.", + [3]byte{0, 9, 88}: "INTELNET S.A.", + [3]byte{0, 9, 89}: "Sitecsoft", + [3]byte{0, 9, 90}: "RACEWOOD TECHNOLOGY", + [3]byte{0, 9, 91}: "NETGEAR", + [3]byte{0, 9, 92}: "Philips Medical Systems - Cardiac and Monitoring Systems (CM", + [3]byte{0, 9, 93}: "Dialogue Technology Corp.", + [3]byte{0, 9, 94}: "Masstech Group Inc.", + [3]byte{0, 9, 95}: "Telebyte, Inc.", + [3]byte{0, 9, 96}: "YOZAN Inc.", + [3]byte{0, 9, 97}: "Switchgear and Instrumentation Ltd", + [3]byte{0, 9, 98}: "Sonitor Technologies AS", + [3]byte{0, 9, 99}: "Dominion Lasercom Inc.", + [3]byte{0, 9, 100}: "Hi-Techniques, Inc.", + [3]byte{0, 9, 101}: "HyunJu Computer Co., Ltd.", + [3]byte{0, 9, 102}: "Thales Navigation", + [3]byte{0, 9, 103}: "Tachyon, Inc", + [3]byte{0, 9, 104}: "TECHNOVENTURE, INC.", + [3]byte{0, 9, 105}: "Meret Optical Communications", + [3]byte{0, 9, 106}: "Cloverleaf Communications Inc.", + [3]byte{0, 9, 107}: "IBM Corp", + [3]byte{0, 9, 108}: "Imedia Semiconductor Corp.", + [3]byte{0, 9, 109}: "Powernet Technologies Corp.", + [3]byte{0, 9, 110}: "GIANT ELECTRONICS LTD.", + [3]byte{0, 9, 111}: "Beijing Zhongqing Elegant Tech. Corp.,Limited", + [3]byte{0, 9, 112}: "Vibration Research Corporation", + [3]byte{0, 9, 113}: "Time Management, Inc.", + [3]byte{0, 9, 114}: "Securebase,Inc", + [3]byte{0, 9, 115}: "Lenten Technology Co., Ltd.", + [3]byte{0, 9, 116}: "Innopia Technologies, Inc.", + [3]byte{0, 9, 117}: "fSONA Communications Corporation", + [3]byte{0, 9, 118}: "Datasoft ISDN Systems GmbH", + [3]byte{0, 9, 119}: "Brunner Elektronik AG", + [3]byte{0, 9, 120}: "AIJI System Co., Ltd.", + [3]byte{0, 9, 121}: "Advanced Television Systems Committee, Inc.", + [3]byte{0, 9, 122}: "Louis Design Labs.", + [3]byte{0, 9, 123}: "Cisco Systems, Inc", + [3]byte{0, 9, 124}: "Cisco Systems, Inc", + [3]byte{0, 9, 125}: "SecWell Networks Oy", + [3]byte{0, 9, 126}: "IMI TECHNOLOGY CO., LTD", + [3]byte{0, 9, 127}: "Vsecure 2000 LTD.", + [3]byte{0, 9, 128}: "Power Zenith Inc.", + [3]byte{0, 9, 129}: "Newport Networks", + [3]byte{0, 9, 130}: "Loewe Opta GmbH", + [3]byte{0, 9, 131}: "GlobalTop Technology, Inc.", + [3]byte{0, 9, 132}: "MyCasa Network Inc.", + [3]byte{0, 9, 133}: "Auto Telecom Company", + [3]byte{0, 9, 134}: "Metalink LTD.", + [3]byte{0, 9, 135}: "NISHI NIPPON ELECTRIC WIRE & CABLE CO.,LTD.", + [3]byte{0, 9, 136}: "Nudian Electron Co., Ltd.", + [3]byte{0, 9, 137}: "VividLogic Inc.", + [3]byte{0, 9, 138}: "EqualLogic Inc", + [3]byte{0, 9, 139}: "Entropic Communications, Inc.", + [3]byte{0, 9, 140}: "Option Wireless Sweden", + [3]byte{0, 9, 141}: "Velocity Semiconductor", + [3]byte{0, 9, 142}: "ipcas GmbH", + [3]byte{0, 9, 143}: "Cetacean Networks", + [3]byte{0, 9, 144}: "ACKSYS Communications & systems", + [3]byte{0, 9, 145}: "GE Fanuc Automation Manufacturing, Inc.", + [3]byte{0, 9, 146}: "InterEpoch Technology,INC.", + [3]byte{0, 9, 147}: "Visteon Corporation", + [3]byte{0, 9, 148}: "Cronyx Engineering", + [3]byte{0, 9, 149}: "Castle Technology Ltd", + [3]byte{0, 9, 150}: "RDI", + [3]byte{0, 9, 151}: "Nortel Networks", + [3]byte{0, 9, 152}: "Capinfo Company Limited", + [3]byte{0, 9, 153}: "CP GEORGES RENAULT", + [3]byte{0, 9, 154}: "ELMO COMPANY, LIMITED", + [3]byte{0, 9, 155}: "Western Telematic Inc.", + [3]byte{0, 9, 156}: "Naval Research Laboratory", + [3]byte{0, 9, 157}: "Haliplex Communications", + [3]byte{0, 9, 158}: "Testech, Inc.", + [3]byte{0, 9, 159}: "VIDEX INC.", + [3]byte{0, 9, 160}: "Microtechno Corporation", + [3]byte{0, 9, 161}: "Telewise Communications, Inc.", + [3]byte{0, 9, 162}: "Interface Co., Ltd.", + [3]byte{0, 9, 163}: "Leadfly Techologies Corp. Ltd.", + [3]byte{0, 9, 164}: "HARTEC Corporation", + [3]byte{0, 9, 165}: "HANSUNG ELETRONIC INDUSTRIES DEVELOPMENT CO., LTD", + [3]byte{0, 9, 166}: "Ignis Optics, Inc.", + [3]byte{0, 9, 167}: "Bang & Olufsen A/S", + [3]byte{0, 9, 168}: "Eastmode Pte Ltd", + [3]byte{0, 9, 169}: "Ikanos Communications", + [3]byte{0, 9, 170}: "Data Comm for Business, Inc.", + [3]byte{0, 9, 171}: "Netcontrol Oy", + [3]byte{0, 9, 172}: "LANVOICE", + [3]byte{0, 9, 173}: "HYUNDAI SYSCOMM, INC.", + [3]byte{0, 9, 174}: "OKANO ELECTRIC CO.,LTD", + [3]byte{0, 9, 175}: "e-generis", + [3]byte{0, 9, 176}: "Onkyo Corporation", + [3]byte{0, 9, 177}: "Kanematsu Electronics, Ltd.", + [3]byte{0, 9, 178}: "L&F Inc.", + [3]byte{0, 9, 179}: "MCM Systems Ltd", + [3]byte{0, 9, 180}: "KISAN TELECOM CO., LTD.", + [3]byte{0, 9, 181}: "3J Tech. Co., Ltd.", + [3]byte{0, 9, 182}: "Cisco Systems, Inc", + [3]byte{0, 9, 183}: "Cisco Systems, Inc", + [3]byte{0, 9, 184}: "Entise Systems", + [3]byte{0, 9, 185}: "Action Imaging Solutions", + [3]byte{0, 9, 186}: "MAKU Informationstechik GmbH", + [3]byte{0, 9, 187}: "MathStar, Inc.", + [3]byte{0, 9, 188}: "Digital Safety Technologies, Inc", + [3]byte{0, 9, 189}: "Epygi Technologies, Ltd.", + [3]byte{0, 9, 190}: "Mamiya-OP Co.,Ltd.", + [3]byte{0, 9, 191}: "Nintendo Co., Ltd.", + [3]byte{0, 9, 192}: "6WIND", + [3]byte{0, 9, 193}: "PROCES-DATA A/S", + [3]byte{0, 9, 194}: "Onity, Inc.", + [3]byte{0, 9, 195}: "NETAS", + [3]byte{0, 9, 196}: "Medicore Co., Ltd", + [3]byte{0, 9, 197}: "KINGENE Technology Corporation", + [3]byte{0, 9, 198}: "Visionics Corporation", + [3]byte{0, 9, 199}: "Movistec", + [3]byte{0, 9, 200}: "SINAGAWA TSUSHIN KEISOU SERVICE", + [3]byte{0, 9, 201}: "BlueWINC Co., Ltd.", + [3]byte{0, 9, 202}: "iMaxNetworks(Shenzhen)Limited.", + [3]byte{0, 9, 203}: "HBrain", + [3]byte{0, 9, 204}: "Moog GmbH", + [3]byte{0, 9, 205}: "HUDSON SOFT CO.,LTD.", + [3]byte{0, 9, 206}: "SpaceBridge Semiconductor Corp.", + [3]byte{0, 9, 207}: "iAd GmbH", + [3]byte{0, 9, 208}: "Solacom Technologies Inc.", + [3]byte{0, 9, 209}: "SERANOA NETWORKS INC", + [3]byte{0, 9, 210}: "Mai Logic Inc.", + [3]byte{0, 9, 211}: "Western DataCom Co., Inc.", + [3]byte{0, 9, 212}: "Transtech Networks", + [3]byte{0, 9, 213}: "Signal Communication, Inc.", + [3]byte{0, 9, 214}: "KNC One GmbH", + [3]byte{0, 9, 215}: "DC Security Products", + [3]byte{0, 9, 216}: "Fält Communications AB", + [3]byte{0, 9, 217}: "Neoscale Systems, Inc", + [3]byte{0, 9, 218}: "Control Module Inc.", + [3]byte{0, 9, 219}: "eSpace", + [3]byte{0, 9, 220}: "Galaxis Technology AG", + [3]byte{0, 9, 221}: "Mavin Technology Inc.", + [3]byte{0, 9, 222}: "Samjin Information & Communications Co., Ltd.", + [3]byte{0, 9, 223}: "Vestel Komunikasyon Sanayi ve Ticaret A.S.", + [3]byte{0, 9, 224}: "XEMICS S.A.", + [3]byte{0, 9, 225}: "Gemtek Technology Co., Ltd.", + [3]byte{0, 9, 226}: "Sinbon Electronics Co., Ltd.", + [3]byte{0, 9, 227}: "Angel Iglesias S.A.", + [3]byte{0, 9, 228}: "K Tech Infosystem Inc.", + [3]byte{0, 9, 229}: "Hottinger Baldwin Messtechnik GmbH", + [3]byte{0, 9, 230}: "Cyber Switching Inc.", + [3]byte{0, 9, 231}: "ADC Techonology", + [3]byte{0, 9, 232}: "Cisco Systems, Inc", + [3]byte{0, 9, 233}: "Cisco Systems, Inc", + [3]byte{0, 9, 234}: "YEM Inc.", + [3]byte{0, 9, 235}: "HuMANDATA LTD.", + [3]byte{0, 9, 236}: "Daktronics, Inc.", + [3]byte{0, 9, 237}: "CipherOptics", + [3]byte{0, 9, 238}: "MEIKYO ELECTRIC CO.,LTD", + [3]byte{0, 9, 239}: "Vocera Communications", + [3]byte{0, 9, 240}: "Shimizu Technology Inc.", + [3]byte{0, 9, 241}: "Yamaki Electric Corporation", + [3]byte{0, 9, 242}: "Cohu, Inc., Electronics Division", + [3]byte{0, 9, 243}: "WELL Communication Corp.", + [3]byte{0, 9, 244}: "Alcon Laboratories, Inc.", + [3]byte{0, 9, 245}: "Emerson Network Power Co.,Ltd", + [3]byte{0, 9, 246}: "Shenzhen Eastern Digital Tech Ltd.", + [3]byte{0, 9, 247}: "SED, a division of Calian", + [3]byte{0, 9, 248}: "UNIMO TECHNOLOGY CO., LTD.", + [3]byte{0, 9, 249}: "ART JAPAN CO., LTD.", + [3]byte{0, 9, 251}: "Philips Patient Monitoring", + [3]byte{0, 9, 252}: "IPFLEX Inc.", + [3]byte{0, 9, 253}: "Ubinetics Limited", + [3]byte{0, 9, 254}: "Daisy Technologies, Inc.", + [3]byte{0, 9, 255}: "X.net 2000 GmbH", + [3]byte{0, 10, 0}: "Mediatek Corp.", + [3]byte{0, 10, 1}: "SOHOware, Inc.", + [3]byte{0, 10, 2}: "ANNSO CO., LTD.", + [3]byte{0, 10, 3}: "ENDESA SERVICIOS, S.L.", + [3]byte{0, 10, 4}: "3Com Ltd", + [3]byte{0, 10, 5}: "Widax Corp.", + [3]byte{0, 10, 6}: "Teledex LLC", + [3]byte{0, 10, 7}: "WebWayOne Ltd", + [3]byte{0, 10, 8}: "Alpine Electronics, Inc.", + [3]byte{0, 10, 9}: "TaraCom Integrated Products, Inc.", + [3]byte{0, 10, 10}: "SUNIX Co., Ltd.", + [3]byte{0, 10, 11}: "Sealevel Systems, Inc.", + [3]byte{0, 10, 12}: "Scientific Research Corporation", + [3]byte{0, 10, 13}: "FCI Deutschland GmbH", + [3]byte{0, 10, 14}: "Invivo Research Inc.", + [3]byte{0, 10, 15}: "Ilryung Telesys, Inc", + [3]byte{0, 10, 16}: "FAST media integrations AG", + [3]byte{0, 10, 17}: "ExPet Technologies, Inc", + [3]byte{0, 10, 18}: "Azylex Technology, Inc", + [3]byte{0, 10, 19}: "Honeywell Video Systems", + [3]byte{0, 10, 20}: "TECO a.s.", + [3]byte{0, 10, 21}: "Silicon Data, Inc", + [3]byte{0, 10, 22}: "Lassen Research", + [3]byte{0, 10, 23}: "NESTAR COMMUNICATIONS, INC", + [3]byte{0, 10, 24}: "Vichel Inc.", + [3]byte{0, 10, 25}: "Valere Power, Inc.", + [3]byte{0, 10, 26}: "Imerge Ltd", + [3]byte{0, 10, 27}: "Stream Labs", + [3]byte{0, 10, 28}: "Bridge Information Co., Ltd.", + [3]byte{0, 10, 29}: "Optical Communications Products Inc.", + [3]byte{0, 10, 30}: "Red-M Products Limited", + [3]byte{0, 10, 31}: "ART WARE Telecommunication Co., Ltd.", + [3]byte{0, 10, 32}: "SVA Networks, Inc.", + [3]byte{0, 10, 33}: "Integra Telecom Co. Ltd", + [3]byte{0, 10, 34}: "Amperion Inc", + [3]byte{0, 10, 35}: "Parama Networks Inc", + [3]byte{0, 10, 36}: "Octave Communications", + [3]byte{0, 10, 37}: "CERAGON NETWORKS", + [3]byte{0, 10, 38}: "CEIA S.p.A.", + [3]byte{0, 10, 39}: "Apple, Inc.", + [3]byte{0, 10, 40}: "Motorola", + [3]byte{0, 10, 41}: "Pan Dacom Networking AG", + [3]byte{0, 10, 42}: "QSI Systems Inc.", + [3]byte{0, 10, 43}: "Etherstuff", + [3]byte{0, 10, 44}: "Active Tchnology Corporation", + [3]byte{0, 10, 45}: "Cabot Communications Limited", + [3]byte{0, 10, 46}: "MAPLE NETWORKS CO., LTD", + [3]byte{0, 10, 47}: "Artnix Inc.", + [3]byte{0, 10, 48}: "Visteon Corporation", + [3]byte{0, 10, 49}: "HCV Consulting", + [3]byte{0, 10, 50}: "Xsido Corporation", + [3]byte{0, 10, 51}: "Emulex Corporation", + [3]byte{0, 10, 52}: "Identicard Systems Incorporated", + [3]byte{0, 10, 53}: "Xilinx", + [3]byte{0, 10, 54}: "Synelec Telecom Multimedia", + [3]byte{0, 10, 55}: "Procera Networks, Inc.", + [3]byte{0, 10, 56}: "Apani Networks", + [3]byte{0, 10, 57}: "LoPA Information Technology", + [3]byte{0, 10, 58}: "J-THREE INTERNATIONAL Holding Co., Ltd.", + [3]byte{0, 10, 59}: "GCT Semiconductor, Inc", + [3]byte{0, 10, 60}: "Enerpoint Ltd.", + [3]byte{0, 10, 61}: "Elo Sistemas Eletronicos S.A.", + [3]byte{0, 10, 62}: "EADS Telecom", + [3]byte{0, 10, 63}: "Data East Corporation", + [3]byte{0, 10, 64}: "Crown Audio -- Harmanm International", + [3]byte{0, 10, 65}: "Cisco Systems, Inc", + [3]byte{0, 10, 66}: "Cisco Systems, Inc", + [3]byte{0, 10, 67}: "Chunghwa Telecom Co., Ltd.", + [3]byte{0, 10, 68}: "Avery Dennison Deutschland GmbH", + [3]byte{0, 10, 69}: "Audio-Technica Corp.", + [3]byte{0, 10, 70}: "ARO WELDING TECHNOLOGIES SAS", + [3]byte{0, 10, 71}: "Allied Vision Technologies", + [3]byte{0, 10, 72}: "Albatron Technology", + [3]byte{0, 10, 73}: "F5 Networks, Inc.", + [3]byte{0, 10, 74}: "Targa Systems Ltd.", + [3]byte{0, 10, 75}: "DataPower Technology, Inc.", + [3]byte{0, 10, 76}: "Molecular Devices Corporation", + [3]byte{0, 10, 77}: "Noritz Corporation", + [3]byte{0, 10, 78}: "UNITEK Electronics INC.", + [3]byte{0, 10, 79}: "Brain Boxes Limited", + [3]byte{0, 10, 80}: "REMOTEK CORPORATION", + [3]byte{0, 10, 81}: "GyroSignal Technology Co., Ltd.", + [3]byte{0, 10, 82}: "AsiaRF Ltd.", + [3]byte{0, 10, 83}: "Intronics, Incorporated", + [3]byte{0, 10, 84}: "Laguna Hills, Inc.", + [3]byte{0, 10, 85}: "MARKEM Corporation", + [3]byte{0, 10, 86}: "HITACHI Maxell Ltd.", + [3]byte{0, 10, 87}: "Hewlett Packard", + [3]byte{0, 10, 88}: "Freyer & Siegel Elektronik GmbH & Co. KG", + [3]byte{0, 10, 89}: "HW server", + [3]byte{0, 10, 90}: "GreenNET Technologies Co.,Ltd.", + [3]byte{0, 10, 91}: "Power-One as", + [3]byte{0, 10, 92}: "Carel s.p.a.", + [3]byte{0, 10, 93}: "FingerTec Worldwide Sdn Bhd", + [3]byte{0, 10, 94}: "3COM Corporation", + [3]byte{0, 10, 95}: "almedio inc.", + [3]byte{0, 10, 96}: "Autostar Technology Pte Ltd", + [3]byte{0, 10, 97}: "Cellinx Systems Inc.", + [3]byte{0, 10, 98}: "Crinis Networks, Inc.", + [3]byte{0, 10, 99}: "DHD GmbH", + [3]byte{0, 10, 100}: "Eracom Technologies", + [3]byte{0, 10, 101}: "GentechMedia.co.,ltd.", + [3]byte{0, 10, 102}: "MITSUBISHI ELECTRIC SYSTEM & SERVICE CO.,LTD.", + [3]byte{0, 10, 103}: "OngCorp", + [3]byte{0, 10, 104}: "Solarflare Communications Inc", + [3]byte{0, 10, 105}: "SUNNY bell Technology Co., Ltd.", + [3]byte{0, 10, 106}: "SVM Microwaves s.r.o.", + [3]byte{0, 10, 107}: "Tadiran Telecom Business Systems LTD", + [3]byte{0, 10, 108}: "Walchem Corporation", + [3]byte{0, 10, 109}: "EKS Elektronikservice GmbH", + [3]byte{0, 10, 110}: "Harmonic, Inc", + [3]byte{0, 10, 111}: "ZyFLEX Technologies Inc", + [3]byte{0, 10, 112}: "MPLS Forum", + [3]byte{0, 10, 113}: "Avrio Technologies, Inc", + [3]byte{0, 10, 114}: "STEC, INC.", + [3]byte{0, 10, 115}: "Scientific Atlanta", + [3]byte{0, 10, 116}: "Manticom Networks Inc.", + [3]byte{0, 10, 117}: "Caterpillar, Inc", + [3]byte{0, 10, 118}: "Beida Jade Bird Huaguang Technology Co.,Ltd", + [3]byte{0, 10, 119}: "Bluewire Technologies LLC", + [3]byte{0, 10, 120}: "OLITEC", + [3]byte{0, 10, 121}: "corega K.K", + [3]byte{0, 10, 122}: "Kyoritsu Electric Co., Ltd.", + [3]byte{0, 10, 123}: "Cornelius Consult", + [3]byte{0, 10, 124}: "Tecton Ltd", + [3]byte{0, 10, 125}: "Valo, Inc.", + [3]byte{0, 10, 126}: "The Advantage Group", + [3]byte{0, 10, 127}: "Teradon Industries, Inc", + [3]byte{0, 10, 128}: "Telkonet Inc.", + [3]byte{0, 10, 129}: "TEIMA Audiotex S.L.", + [3]byte{0, 10, 130}: "TATSUTA SYSTEM ELECTRONICS CO.,LTD.", + [3]byte{0, 10, 131}: "SALTO SYSTEMS S.L.", + [3]byte{0, 10, 132}: "Rainsun Enterprise Co., Ltd.", + [3]byte{0, 10, 133}: "PLAT'C2,Inc", + [3]byte{0, 10, 134}: "Lenze", + [3]byte{0, 10, 135}: "Integrated Micromachines Inc.", + [3]byte{0, 10, 136}: "InCypher S.A.", + [3]byte{0, 10, 137}: "Creval Systems, Inc.", + [3]byte{0, 10, 138}: "Cisco Systems, Inc", + [3]byte{0, 10, 139}: "Cisco Systems, Inc", + [3]byte{0, 10, 140}: "Guardware Systems Ltd.", + [3]byte{0, 10, 141}: "EUROTHERM LIMITED", + [3]byte{0, 10, 142}: "Invacom Ltd", + [3]byte{0, 10, 143}: "Aska International Inc.", + [3]byte{0, 10, 144}: "Bayside Interactive, Inc.", + [3]byte{0, 10, 145}: "HemoCue AB", + [3]byte{0, 10, 146}: "Presonus Corporation", + [3]byte{0, 10, 147}: "W2 Networks, Inc.", + [3]byte{0, 10, 148}: "ShangHai cellink CO., LTD", + [3]byte{0, 10, 149}: "Apple, Inc.", + [3]byte{0, 10, 150}: "MEWTEL TECHNOLOGY INC.", + [3]byte{0, 10, 151}: "SONICblue, Inc.", + [3]byte{0, 10, 152}: "M+F Gwinner GmbH & Co", + [3]byte{0, 10, 153}: "Calamp Wireless Networks Inc", + [3]byte{0, 10, 154}: "Aiptek International Inc", + [3]byte{0, 10, 155}: "TB Group Inc", + [3]byte{0, 10, 156}: "Server Technology, Inc.", + [3]byte{0, 10, 157}: "King Young Technology Co. Ltd.", + [3]byte{0, 10, 158}: "BroadWeb Corportation", + [3]byte{0, 10, 159}: "Pannaway Technologies, Inc.", + [3]byte{0, 10, 160}: "Cedar Point Communications", + [3]byte{0, 10, 161}: "V V S Limited", + [3]byte{0, 10, 162}: "SYSTEK INC.", + [3]byte{0, 10, 163}: "SHIMAFUJI ELECTRIC CO.,LTD.", + [3]byte{0, 10, 164}: "SHANGHAI SURVEILLANCE TECHNOLOGY CO,LTD", + [3]byte{0, 10, 165}: "MAXLINK INDUSTRIES LIMITED", + [3]byte{0, 10, 166}: "Hochiki Corporation", + [3]byte{0, 10, 167}: "FEI Electron Optics", + [3]byte{0, 10, 168}: "ePipe Pty. Ltd.", + [3]byte{0, 10, 169}: "Brooks Automation GmbH", + [3]byte{0, 10, 170}: "AltiGen Communications Inc.", + [3]byte{0, 10, 171}: "Toyota Technical Development Corporation", + [3]byte{0, 10, 172}: "TerraTec Electronic GmbH", + [3]byte{0, 10, 173}: "Stargames Corporation", + [3]byte{0, 10, 174}: "Rosemount Process Analytical", + [3]byte{0, 10, 175}: "Pipal Systems", + [3]byte{0, 10, 176}: "LOYTEC electronics GmbH", + [3]byte{0, 10, 177}: "GENETEC Corporation", + [3]byte{0, 10, 178}: "Fresnel Wireless Systems", + [3]byte{0, 10, 179}: "Fa. GIRA", + [3]byte{0, 10, 180}: "ETIC Telecommunications", + [3]byte{0, 10, 181}: "Digital Electronic Network", + [3]byte{0, 10, 182}: "COMPUNETIX, INC", + [3]byte{0, 10, 183}: "Cisco Systems, Inc", + [3]byte{0, 10, 184}: "Cisco Systems, Inc", + [3]byte{0, 10, 185}: "Astera Technologies Corp.", + [3]byte{0, 10, 186}: "Arcon Technology Limited", + [3]byte{0, 10, 187}: "Taiwan Secom Co,. Ltd", + [3]byte{0, 10, 188}: "Seabridge Ltd.", + [3]byte{0, 10, 189}: "Rupprecht & Patashnick Co.", + [3]byte{0, 10, 190}: "OPNET Technologies CO., LTD.", + [3]byte{0, 10, 191}: "HIROTA SS", + [3]byte{0, 10, 192}: "Fuyoh Video Industry CO., LTD.", + [3]byte{0, 10, 193}: "Futuretel", + [3]byte{0, 10, 194}: "Wuhan FiberHome Digital Technology Co.,Ltd.", + [3]byte{0, 10, 195}: "eM Technics Co., Ltd.", + [3]byte{0, 10, 196}: "Daewoo Teletech Co., Ltd", + [3]byte{0, 10, 197}: "Color Kinetics", + [3]byte{0, 10, 198}: "Overture Networks.", + [3]byte{0, 10, 199}: "Unication Group", + [3]byte{0, 10, 200}: "ZPSYS CO.,LTD. (Planning&Management)", + [3]byte{0, 10, 201}: "Zambeel Inc", + [3]byte{0, 10, 202}: "YOKOYAMA SHOKAI CO.,Ltd.", + [3]byte{0, 10, 203}: "XPAK MSA Group", + [3]byte{0, 10, 204}: "Winnow Networks, Inc.", + [3]byte{0, 10, 205}: "Sunrich Technology Limited", + [3]byte{0, 10, 206}: "RADIANTECH, INC.", + [3]byte{0, 10, 207}: "PROVIDEO Multimedia Co. Ltd.", + [3]byte{0, 10, 208}: "Niigata Develoment Center, F.I.T. Co., Ltd.", + [3]byte{0, 10, 209}: "MWS", + [3]byte{0, 10, 210}: "JEPICO Corporation", + [3]byte{0, 10, 211}: "INITECH Co., Ltd", + [3]byte{0, 10, 212}: "CoreBell Systems Inc.", + [3]byte{0, 10, 213}: "Brainchild Electronic Co., Ltd.", + [3]byte{0, 10, 214}: "BeamReach Networks", + [3]byte{0, 10, 215}: "Origin ELECTRIC CO.,LTD.", + [3]byte{0, 10, 216}: "IPCserv Technology Corp.", + [3]byte{0, 10, 217}: "Sony Mobile Communications AB", + [3]byte{0, 10, 218}: "Vindicator Technologies", + [3]byte{0, 10, 219}: "SkyPilot Network, Inc", + [3]byte{0, 10, 220}: "RuggedCom Inc.", + [3]byte{0, 10, 221}: "Allworx Corp.", + [3]byte{0, 10, 222}: "Happy Communication Co., Ltd.", + [3]byte{0, 10, 223}: "Gennum Corporation", + [3]byte{0, 10, 224}: "Fujitsu Softek", + [3]byte{0, 10, 225}: "EG Technology", + [3]byte{0, 10, 226}: "Binatone Electronics International, Ltd", + [3]byte{0, 10, 227}: "YANG MEI TECHNOLOGY CO., LTD", + [3]byte{0, 10, 228}: "Wistron Corporation", + [3]byte{0, 10, 229}: "ScottCare Corporation", + [3]byte{0, 10, 230}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 10, 231}: "ELIOP S.A.", + [3]byte{0, 10, 232}: "Cathay Roxus Information Technology Co. LTD", + [3]byte{0, 10, 233}: "AirVast Technology Inc.", + [3]byte{0, 10, 234}: "ADAM ELEKTRONIK LTD. ŞTI", + [3]byte{0, 10, 235}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 10, 236}: "Koatsu Gas Kogyo Co., Ltd.", + [3]byte{0, 10, 237}: "HARTING Electronics GmbH", + [3]byte{0, 10, 238}: "GCD Hard- & Software GmbH", + [3]byte{0, 10, 239}: "OTRUM ASA", + [3]byte{0, 10, 240}: "SHIN-OH ELECTRONICS CO., LTD. R&D", + [3]byte{0, 10, 241}: "Clarity Design, Inc.", + [3]byte{0, 10, 242}: "NeoAxiom Corp.", + [3]byte{0, 10, 243}: "Cisco Systems, Inc", + [3]byte{0, 10, 244}: "Cisco Systems, Inc", + [3]byte{0, 10, 245}: "Airgo Networks, Inc.", + [3]byte{0, 10, 246}: "Emerson Climate Technologies Retail Solutions, Inc.", + [3]byte{0, 10, 247}: "Broadcom", + [3]byte{0, 10, 248}: "American Telecare Inc.", + [3]byte{0, 10, 249}: "HiConnect, Inc.", + [3]byte{0, 10, 250}: "Traverse Technologies Australia", + [3]byte{0, 10, 251}: "Ambri Limited", + [3]byte{0, 10, 252}: "Core Tec Communications, LLC", + [3]byte{0, 10, 253}: "Kentec Electronics", + [3]byte{0, 10, 254}: "NovaPal Ltd", + [3]byte{0, 10, 255}: "Kilchherr Elektronik AG", + [3]byte{0, 11, 0}: "FUJIAN START COMPUTER EQUIPMENT CO.,LTD", + [3]byte{0, 11, 1}: "DAIICHI ELECTRONICS CO., LTD.", + [3]byte{0, 11, 2}: "Dallmeier electronic", + [3]byte{0, 11, 3}: "Taekwang Industrial Co., Ltd", + [3]byte{0, 11, 4}: "Volktek Corporation", + [3]byte{0, 11, 5}: "Pacific Broadband Networks", + [3]byte{0, 11, 6}: "ARRIS Group, Inc.", + [3]byte{0, 11, 7}: "Voxpath Networks", + [3]byte{0, 11, 8}: "Pillar Data Systems", + [3]byte{0, 11, 9}: "Ifoundry Systems Singapore", + [3]byte{0, 11, 10}: "dBm Optics", + [3]byte{0, 11, 11}: "Corrent Corporation", + [3]byte{0, 11, 12}: "Agile Systems Inc.", + [3]byte{0, 11, 13}: "Air2U, Inc.", + [3]byte{0, 11, 14}: "Trapeze Networks", + [3]byte{0, 11, 15}: "Bosch Rexroth", + [3]byte{0, 11, 16}: "11wave Technonlogy Co.,Ltd", + [3]byte{0, 11, 17}: "HIMEJI ABC TRADING CO.,LTD.", + [3]byte{0, 11, 18}: "NURI Telecom Co., Ltd.", + [3]byte{0, 11, 19}: "ZETRON INC", + [3]byte{0, 11, 20}: "ViewSonic Corporation", + [3]byte{0, 11, 21}: "Platypus Technology", + [3]byte{0, 11, 22}: "Communication Machinery Corporation", + [3]byte{0, 11, 23}: "MKS Instruments", + [3]byte{0, 11, 24}: "Private", + [3]byte{0, 11, 25}: "Vernier Networks, Inc.", + [3]byte{0, 11, 26}: "Industrial Defender, Inc.", + [3]byte{0, 11, 27}: "Systronix, Inc.", + [3]byte{0, 11, 28}: "SIBCO bv", + [3]byte{0, 11, 29}: "LayerZero Power Systems, Inc.", + [3]byte{0, 11, 30}: "KAPPA opto-electronics GmbH", + [3]byte{0, 11, 31}: "I CON Computer Co.", + [3]byte{0, 11, 32}: "Hirata corporation", + [3]byte{0, 11, 33}: "G-Star Communications Inc.", + [3]byte{0, 11, 34}: "Environmental Systems and Services", + [3]byte{0, 11, 35}: "Siemens Subscriber Networks", + [3]byte{0, 11, 36}: "AirLogic", + [3]byte{0, 11, 37}: "Aeluros", + [3]byte{0, 11, 38}: "Wetek Corporation", + [3]byte{0, 11, 39}: "Scion Corporation", + [3]byte{0, 11, 40}: "Quatech Inc.", + [3]byte{0, 11, 41}: "LS(LG) Industrial Systems co.,Ltd", + [3]byte{0, 11, 42}: "HOWTEL Co., Ltd.", + [3]byte{0, 11, 43}: "HOSTNET CORPORATION", + [3]byte{0, 11, 44}: "Eiki Industrial Co. Ltd.", + [3]byte{0, 11, 45}: "Danfoss Inc.", + [3]byte{0, 11, 46}: "Cal-Comp Electronics & Communications Company Ltd.", + [3]byte{0, 11, 47}: "bplan GmbH", + [3]byte{0, 11, 48}: "Beijing Gongye Science & Technology Co.,Ltd", + [3]byte{0, 11, 49}: "Yantai ZhiYang Scientific and technology industry CO., LTD", + [3]byte{0, 11, 50}: "VORMETRIC, INC.", + [3]byte{0, 11, 51}: "Vivato Technologies", + [3]byte{0, 11, 52}: "ShangHai Broadband Technologies CO.LTD", + [3]byte{0, 11, 53}: "Quad Bit System co., Ltd.", + [3]byte{0, 11, 54}: "Productivity Systems, Inc.", + [3]byte{0, 11, 55}: "MANUFACTURE DES MONTRES ROLEX SA", + [3]byte{0, 11, 56}: "Knürr GmbH", + [3]byte{0, 11, 57}: "Keisoku Giken Co.,Ltd.", + [3]byte{0, 11, 58}: "QuStream Corporation", + [3]byte{0, 11, 59}: "devolo AG", + [3]byte{0, 11, 60}: "Cygnal Integrated Products, Inc.", + [3]byte{0, 11, 61}: "CONTAL OK Ltd.", + [3]byte{0, 11, 62}: "BittWare, Inc", + [3]byte{0, 11, 63}: "Anthology Solutions Inc.", + [3]byte{0, 11, 64}: "Oclaro", + [3]byte{0, 11, 65}: "Ing. Büro Dr. Beutlhauser", + [3]byte{0, 11, 66}: "commax Co., Ltd.", + [3]byte{0, 11, 67}: "Microscan Systems, Inc.", + [3]byte{0, 11, 68}: "Concord IDea Corp.", + [3]byte{0, 11, 69}: "Cisco Systems, Inc", + [3]byte{0, 11, 70}: "Cisco Systems, Inc", + [3]byte{0, 11, 71}: "Advanced Energy", + [3]byte{0, 11, 72}: "sofrel", + [3]byte{0, 11, 73}: "RF-Link System Inc.", + [3]byte{0, 11, 74}: "Visimetrics (UK) Ltd", + [3]byte{0, 11, 75}: "VISIOWAVE SA", + [3]byte{0, 11, 76}: "Clarion (M) Sdn Bhd", + [3]byte{0, 11, 77}: "Emuzed", + [3]byte{0, 11, 78}: "VertexRSI, General Dynamics SatCOM Technologies, Inc.", + [3]byte{0, 11, 79}: "Verifone", + [3]byte{0, 11, 80}: "Oxygnet", + [3]byte{0, 11, 81}: "Micetek International Inc.", + [3]byte{0, 11, 82}: "JOYMAX ELECTRONICS CO. LTD.", + [3]byte{0, 11, 83}: "INITIUM Co., Ltd.", + [3]byte{0, 11, 84}: "BiTMICRO Networks, Inc.", + [3]byte{0, 11, 85}: "ADInstruments", + [3]byte{0, 11, 86}: "Cybernetics", + [3]byte{0, 11, 87}: "Silicon Laboratories", + [3]byte{0, 11, 88}: "Astronautics C.A LTD", + [3]byte{0, 11, 89}: "ScriptPro, LLC", + [3]byte{0, 11, 90}: "HyperEdge", + [3]byte{0, 11, 91}: "Rincon Research Corporation", + [3]byte{0, 11, 92}: "Newtech Co.,Ltd", + [3]byte{0, 11, 93}: "FUJITSU LIMITED", + [3]byte{0, 11, 94}: "Audio Engineering Society Inc.", + [3]byte{0, 11, 95}: "Cisco Systems, Inc", + [3]byte{0, 11, 96}: "Cisco Systems, Inc", + [3]byte{0, 11, 97}: "Friedrich Lütze GmbH & Co. KG", + [3]byte{0, 11, 98}: "ib-mohnen KG", + [3]byte{0, 11, 99}: "Kaleidescape", + [3]byte{0, 11, 100}: "Kieback & Peter GmbH & Co KG", + [3]byte{0, 11, 101}: "Sy.A.C. srl", + [3]byte{0, 11, 102}: "Teralink Communications", + [3]byte{0, 11, 103}: "Topview Technology Corporation", + [3]byte{0, 11, 104}: "Addvalue Communications Pte Ltd", + [3]byte{0, 11, 105}: "Franke Finland Oy", + [3]byte{0, 11, 106}: "Asiarock Technology Limited", + [3]byte{0, 11, 107}: "Wistron Neweb Corporation", + [3]byte{0, 11, 108}: "Sychip Inc.", + [3]byte{0, 11, 109}: "SOLECTRON JAPAN NAKANIIDA", + [3]byte{0, 11, 110}: "Neff Instrument Corp.", + [3]byte{0, 11, 111}: "Media Streaming Networks Inc", + [3]byte{0, 11, 112}: "Load Technology, Inc.", + [3]byte{0, 11, 113}: "Litchfield Communications Inc.", + [3]byte{0, 11, 114}: "Lawo AG", + [3]byte{0, 11, 115}: "Kodeos Communications", + [3]byte{0, 11, 116}: "Kingwave Technology Co., Ltd.", + [3]byte{0, 11, 117}: "Iosoft Ltd.", + [3]byte{0, 11, 118}: "ET&T Technology Co. Ltd.", + [3]byte{0, 11, 119}: "Cogent Systems, Inc.", + [3]byte{0, 11, 120}: "TAIFATECH INC.", + [3]byte{0, 11, 121}: "X-COM, Inc.", + [3]byte{0, 11, 122}: "L-3 Linkabit", + [3]byte{0, 11, 123}: "Test-Um Inc.", + [3]byte{0, 11, 124}: "Telex Communications", + [3]byte{0, 11, 125}: "SOLOMON EXTREME INTERNATIONAL LTD.", + [3]byte{0, 11, 126}: "SAGINOMIYA Seisakusho Inc.", + [3]byte{0, 11, 127}: "Align Engineering LLC", + [3]byte{0, 11, 128}: "Lycium Networks", + [3]byte{0, 11, 129}: "Kaparel Corporation", + [3]byte{0, 11, 130}: "Grandstream Networks, Inc.", + [3]byte{0, 11, 131}: "DATAWATT B.V.", + [3]byte{0, 11, 132}: "BODET", + [3]byte{0, 11, 133}: "Cisco Systems, Inc", + [3]byte{0, 11, 134}: "Aruba Networks", + [3]byte{0, 11, 135}: "American Reliance Inc.", + [3]byte{0, 11, 136}: "Vidisco ltd.", + [3]byte{0, 11, 137}: "Top Global Technology, Ltd.", + [3]byte{0, 11, 138}: "MITEQ Inc.", + [3]byte{0, 11, 139}: "KERAJET, S.A.", + [3]byte{0, 11, 140}: "Flextronics", + [3]byte{0, 11, 141}: "Avvio Networks", + [3]byte{0, 11, 142}: "Ascent Corporation", + [3]byte{0, 11, 143}: "AKITA ELECTRONICS SYSTEMS CO.,LTD.", + [3]byte{0, 11, 144}: "ADVA Optical Networking Ltd.", + [3]byte{0, 11, 145}: "Aglaia Gesellschaft für Bildverarbeitung und Kommunikation mbH", + [3]byte{0, 11, 146}: "Ascom Danmark A/S", + [3]byte{0, 11, 147}: "Ritter Elektronik", + [3]byte{0, 11, 148}: "Digital Monitoring Products, Inc.", + [3]byte{0, 11, 149}: "eBet Gaming Systems Pty Ltd", + [3]byte{0, 11, 150}: "Innotrac Diagnostics Oy", + [3]byte{0, 11, 151}: "Matsushita Electric Industrial Co.,Ltd.", + [3]byte{0, 11, 152}: "NiceTechVision", + [3]byte{0, 11, 153}: "SensAble Technologies, Inc.", + [3]byte{0, 11, 154}: "Shanghai Ulink Telecom Equipment Co. Ltd.", + [3]byte{0, 11, 155}: "Sirius System Co, Ltd.", + [3]byte{0, 11, 156}: "TriBeam Technologies, Inc.", + [3]byte{0, 11, 157}: "TwinMOS Technologies Inc.", + [3]byte{0, 11, 158}: "Yasing Technology Corp.", + [3]byte{0, 11, 159}: "Neue ELSA GmbH", + [3]byte{0, 11, 160}: "T&L Information Inc.", + [3]byte{0, 11, 161}: "Fujikura Solutions Ltd.", + [3]byte{0, 11, 162}: "Sumitomo Electric Industries,Ltd", + [3]byte{0, 11, 163}: "Siemens AG, I&S", + [3]byte{0, 11, 164}: "Shiron Satellite Communications Ltd. (1996)", + [3]byte{0, 11, 165}: "Quasar Cipta Mandiri, PT", + [3]byte{0, 11, 166}: "Miyakawa Electric Works Ltd.", + [3]byte{0, 11, 167}: "Maranti Networks", + [3]byte{0, 11, 168}: "HANBACK ELECTRONICS CO., LTD.", + [3]byte{0, 11, 169}: "CloudShield Technologies, Inc.", + [3]byte{0, 11, 170}: "Aiphone co.,Ltd", + [3]byte{0, 11, 171}: "Advantech Technology (CHINA) Co., Ltd.", + [3]byte{0, 11, 172}: "3Com Ltd", + [3]byte{0, 11, 173}: "PC-PoS Inc.", + [3]byte{0, 11, 174}: "Vitals System Inc.", + [3]byte{0, 11, 175}: "WOOJU COMMUNICATIONS Co,.Ltd", + [3]byte{0, 11, 176}: "Sysnet Telematica srl", + [3]byte{0, 11, 177}: "Super Star Technology Co., Ltd.", + [3]byte{0, 11, 178}: "SMALLBIG TECHNOLOGY", + [3]byte{0, 11, 179}: "RiT technologies Ltd.", + [3]byte{0, 11, 180}: "RDC Semiconductor Inc.,", + [3]byte{0, 11, 181}: "nStor Technologies, Inc.", + [3]byte{0, 11, 182}: "Metalligence Technology Corp.", + [3]byte{0, 11, 183}: "Micro Systems Co.,Ltd.", + [3]byte{0, 11, 184}: "Kihoku Electronic Co.", + [3]byte{0, 11, 185}: "Imsys AB", + [3]byte{0, 11, 186}: "Harmonic, Inc", + [3]byte{0, 11, 187}: "Etin Systems Co., Ltd", + [3]byte{0, 11, 188}: "En Garde Systems, Inc.", + [3]byte{0, 11, 189}: "Connexionz Limited", + [3]byte{0, 11, 190}: "Cisco Systems, Inc", + [3]byte{0, 11, 191}: "Cisco Systems, Inc", + [3]byte{0, 11, 192}: "China IWNComm Co., Ltd.", + [3]byte{0, 11, 193}: "Bay Microsystems, Inc.", + [3]byte{0, 11, 194}: "Corinex Communication Corp.", + [3]byte{0, 11, 195}: "Multiplex, Inc.", + [3]byte{0, 11, 196}: "BIOTRONIK GmbH & Co", + [3]byte{0, 11, 197}: "SMC Networks, Inc.", + [3]byte{0, 11, 198}: "ISAC, Inc.", + [3]byte{0, 11, 199}: "ICET S.p.A.", + [3]byte{0, 11, 200}: "AirFlow Networks", + [3]byte{0, 11, 201}: "Electroline Equipment", + [3]byte{0, 11, 202}: "DATAVAN TC", + [3]byte{0, 11, 203}: "Fagor Automation , S. Coop", + [3]byte{0, 11, 204}: "JUSAN, S.A.", + [3]byte{0, 11, 205}: "Hewlett Packard", + [3]byte{0, 11, 206}: "Free2move AB", + [3]byte{0, 11, 207}: "AGFA NDT INC.", + [3]byte{0, 11, 208}: "XiMeta Technology Americas Inc.", + [3]byte{0, 11, 209}: "Aeronix, Inc.", + [3]byte{0, 11, 210}: "Remopro Technology Inc.", + [3]byte{0, 11, 211}: "cd3o", + [3]byte{0, 11, 212}: "Beijing Wise Technology & Science Development Co.Ltd", + [3]byte{0, 11, 213}: "Nvergence, Inc.", + [3]byte{0, 11, 214}: "Paxton Access Ltd", + [3]byte{0, 11, 215}: "DORMA Time + Access GmbH", + [3]byte{0, 11, 216}: "Industrial Scientific Corp.", + [3]byte{0, 11, 217}: "General Hydrogen", + [3]byte{0, 11, 218}: "EyeCross Co.,Inc.", + [3]byte{0, 11, 219}: "Dell Inc.", + [3]byte{0, 11, 220}: "AKCP", + [3]byte{0, 11, 221}: "TOHOKU RICOH Co., LTD.", + [3]byte{0, 11, 222}: "TELDIX GmbH", + [3]byte{0, 11, 223}: "Shenzhen RouterD Networks Limited", + [3]byte{0, 11, 224}: "SercoNet Ltd.", + [3]byte{0, 11, 225}: "Nokia NET Product Operations", + [3]byte{0, 11, 226}: "Lumenera Corporation", + [3]byte{0, 11, 227}: "Key Stream Co., Ltd.", + [3]byte{0, 11, 228}: "Hosiden Corporation", + [3]byte{0, 11, 229}: "HIMS International Corporation", + [3]byte{0, 11, 230}: "Datel Electronics", + [3]byte{0, 11, 231}: "COMFLUX TECHNOLOGY INC.", + [3]byte{0, 11, 232}: "AOIP", + [3]byte{0, 11, 233}: "Actel Corporation", + [3]byte{0, 11, 234}: "Zultys Technologies", + [3]byte{0, 11, 235}: "Systegra AG", + [3]byte{0, 11, 236}: "NIPPON ELECTRIC INSTRUMENT, INC.", + [3]byte{0, 11, 237}: "ELM Inc.", + [3]byte{0, 11, 238}: "inc.jet, Incorporated", + [3]byte{0, 11, 239}: "Code Corporation", + [3]byte{0, 11, 240}: "MoTEX Products Co., Ltd.", + [3]byte{0, 11, 241}: "LAP Laser Applikations", + [3]byte{0, 11, 242}: "Chih-Kan Technology Co., Ltd.", + [3]byte{0, 11, 243}: "BAE SYSTEMS", + [3]byte{0, 11, 244}: "Private", + [3]byte{0, 11, 245}: "Shanghai Sibo Telecom Technology Co.,Ltd", + [3]byte{0, 11, 246}: "Nitgen Co., Ltd", + [3]byte{0, 11, 247}: "NIDEK CO.,LTD", + [3]byte{0, 11, 248}: "Infinera", + [3]byte{0, 11, 249}: "Gemstone Communications, Inc.", + [3]byte{0, 11, 250}: "EXEMYS SRL", + [3]byte{0, 11, 251}: "D-NET International Corporation", + [3]byte{0, 11, 252}: "Cisco Systems, Inc", + [3]byte{0, 11, 253}: "Cisco Systems, Inc", + [3]byte{0, 11, 254}: "CASTEL Broadband Limited", + [3]byte{0, 11, 255}: "Berkeley Camera Engineering", + [3]byte{0, 12, 0}: "BEB Industrie-Elektronik AG", + [3]byte{0, 12, 1}: "Abatron AG", + [3]byte{0, 12, 2}: "ABB Oy", + [3]byte{0, 12, 3}: "HDMI Licensing, LLC", + [3]byte{0, 12, 4}: "Tecnova", + [3]byte{0, 12, 5}: "RPA Reserch Co., Ltd.", + [3]byte{0, 12, 6}: "Nixvue Systems Pte Ltd", + [3]byte{0, 12, 7}: "Iftest AG", + [3]byte{0, 12, 8}: "HUMEX Technologies Corp.", + [3]byte{0, 12, 9}: "Hitachi IE Systems Co., Ltd", + [3]byte{0, 12, 10}: "Guangdong Province Electronic Technology Research Institute", + [3]byte{0, 12, 11}: "Broadbus Technologies", + [3]byte{0, 12, 12}: "APPRO TECHNOLOGY INC.", + [3]byte{0, 12, 13}: "Communications & Power Industries / Satcom Division", + [3]byte{0, 12, 14}: "XtremeSpectrum, Inc.", + [3]byte{0, 12, 15}: "Techno-One Co., Ltd", + [3]byte{0, 12, 16}: "PNI Corporation", + [3]byte{0, 12, 17}: "NIPPON DEMPA CO.,LTD.", + [3]byte{0, 12, 18}: "Micro-Optronic-Messtechnik GmbH", + [3]byte{0, 12, 19}: "MediaQ", + [3]byte{0, 12, 20}: "Diagnostic Instruments, Inc.", + [3]byte{0, 12, 21}: "CyberPower Systems, Inc.", + [3]byte{0, 12, 22}: "Concorde Microsystems Inc.", + [3]byte{0, 12, 23}: "AJA Video Systems Inc", + [3]byte{0, 12, 24}: "Zenisu Keisoku Inc.", + [3]byte{0, 12, 25}: "Telio Communications GmbH", + [3]byte{0, 12, 26}: "Quest Technical Solutions Inc.", + [3]byte{0, 12, 27}: "ORACOM Co, Ltd.", + [3]byte{0, 12, 28}: "MicroWeb Co., Ltd.", + [3]byte{0, 12, 29}: "Mettler & Fuchs AG", + [3]byte{0, 12, 30}: "Global Cache", + [3]byte{0, 12, 31}: "Glimmerglass Networks", + [3]byte{0, 12, 32}: "Fi WIn, Inc.", + [3]byte{0, 12, 33}: "Faculty of Science and Technology, Keio University", + [3]byte{0, 12, 34}: "Double D Electronics Ltd", + [3]byte{0, 12, 35}: "Beijing Lanchuan Tech. Co., Ltd.", + [3]byte{0, 12, 36}: "ANATOR", + [3]byte{0, 12, 37}: "Allied Telesis Labs, Inc. ", + [3]byte{0, 12, 38}: "Weintek Labs. Inc.", + [3]byte{0, 12, 39}: "Sammy Corporation", + [3]byte{0, 12, 40}: "RIFATRON", + [3]byte{0, 12, 41}: "VMware, Inc.", + [3]byte{0, 12, 42}: "OCTTEL Communication Co., Ltd.", + [3]byte{0, 12, 43}: "ELIAS Technology, Inc.", + [3]byte{0, 12, 44}: "Enwiser Inc.", + [3]byte{0, 12, 45}: "FullWave Technology Co., Ltd.", + [3]byte{0, 12, 46}: "Openet information technology(shenzhen) Co., Ltd.", + [3]byte{0, 12, 47}: "SeorimTechnology Co.,Ltd.", + [3]byte{0, 12, 48}: "Cisco Systems, Inc", + [3]byte{0, 12, 49}: "Cisco Systems, Inc", + [3]byte{0, 12, 50}: "Avionic Design Development GmbH", + [3]byte{0, 12, 51}: "Compucase Enterprise Co. Ltd.", + [3]byte{0, 12, 52}: "Vixen Co., Ltd.", + [3]byte{0, 12, 53}: "KaVo Dental GmbH & Co. KG", + [3]byte{0, 12, 54}: "SHARP TAKAYA ELECTRONICS INDUSTRY CO.,LTD.", + [3]byte{0, 12, 55}: "Geomation, Inc.", + [3]byte{0, 12, 56}: "TelcoBridges Inc.", + [3]byte{0, 12, 57}: "Sentinel Wireless Inc.", + [3]byte{0, 12, 58}: "Oxance", + [3]byte{0, 12, 59}: "Orion Electric Co., Ltd.", + [3]byte{0, 12, 60}: "MediaChorus, Inc.", + [3]byte{0, 12, 61}: "Glsystech Co., Ltd.", + [3]byte{0, 12, 62}: "Crest Audio", + [3]byte{0, 12, 63}: "Cogent Defence & Security Networks,", + [3]byte{0, 12, 64}: "Altech Controls", + [3]byte{0, 12, 65}: "Cisco-Linksys, LLC", + [3]byte{0, 12, 66}: "Routerboard.com", + [3]byte{0, 12, 67}: "Ralink Technology, Corp.", + [3]byte{0, 12, 68}: "Automated Interfaces, Inc.", + [3]byte{0, 12, 69}: "Animation Technologies Inc.", + [3]byte{0, 12, 70}: "Allied Telesyn Inc.", + [3]byte{0, 12, 71}: "SK Teletech(R&D Planning Team)", + [3]byte{0, 12, 72}: "QoStek Corporation", + [3]byte{0, 12, 73}: "Dangaard Telecom Denmark A/S", + [3]byte{0, 12, 74}: "Cygnus Microsystems (P) Limited", + [3]byte{0, 12, 75}: "Cheops Elektronik", + [3]byte{0, 12, 76}: "Arcor AG&Co.", + [3]byte{0, 12, 77}: "Curtiss-Wright Controls Avionics & Electronics", + [3]byte{0, 12, 78}: "Winbest Technology CO,LT", + [3]byte{0, 12, 79}: "UDTech Japan Corporation", + [3]byte{0, 12, 80}: "Seagate Technology", + [3]byte{0, 12, 81}: "Scientific Technologies Inc.", + [3]byte{0, 12, 82}: "Roll Systems Inc.", + [3]byte{0, 12, 83}: "Private", + [3]byte{0, 12, 84}: "Pedestal Networks, Inc", + [3]byte{0, 12, 85}: "Microlink Communications Inc.", + [3]byte{0, 12, 86}: "Megatel Computer (1986) Corp.", + [3]byte{0, 12, 87}: "MACKIE Engineering Services Belgium BVBA", + [3]byte{0, 12, 88}: "M&S Systems", + [3]byte{0, 12, 89}: "Indyme Electronics, Inc.", + [3]byte{0, 12, 90}: "IBSmm Embedded Electronics Consulting", + [3]byte{0, 12, 91}: "HANWANG TECHNOLOGY CO.,LTD", + [3]byte{0, 12, 92}: "GTN Systems B.V.", + [3]byte{0, 12, 93}: "CHIC TECHNOLOGY (CHINA) CORP.", + [3]byte{0, 12, 94}: "Calypso Medical", + [3]byte{0, 12, 95}: "Avtec, Inc.", + [3]byte{0, 12, 96}: "ACM Systems", + [3]byte{0, 12, 97}: "AC Tech corporation DBA Advanced Digital", + [3]byte{0, 12, 98}: "ABB AB, Cewe-Control ", + [3]byte{0, 12, 99}: "Zenith Electronics Corporation", + [3]byte{0, 12, 100}: "X2 MSA Group", + [3]byte{0, 12, 101}: "Sunin Telecom", + [3]byte{0, 12, 102}: "Pronto Networks Inc", + [3]byte{0, 12, 103}: "OYO ELECTRIC CO.,LTD", + [3]byte{0, 12, 104}: "SigmaTel, Inc.", + [3]byte{0, 12, 105}: "National Radio Astronomy Observatory", + [3]byte{0, 12, 106}: "MBARI", + [3]byte{0, 12, 107}: "Kurz Industrie-Elektronik GmbH", + [3]byte{0, 12, 108}: "Elgato Systems LLC", + [3]byte{0, 12, 109}: "Edwards Ltd.", + [3]byte{0, 12, 110}: "ASUSTek COMPUTER INC.", + [3]byte{0, 12, 111}: "Amtek system co.,LTD.", + [3]byte{0, 12, 112}: "ACC GmbH", + [3]byte{0, 12, 113}: "Wybron, Inc", + [3]byte{0, 12, 114}: "Tempearl Industrial Co., Ltd.", + [3]byte{0, 12, 115}: "TELSON ELECTRONICS CO., LTD", + [3]byte{0, 12, 116}: "RIVERTEC CORPORATION", + [3]byte{0, 12, 117}: "Oriental integrated electronics. LTD", + [3]byte{0, 12, 118}: "MICRO-STAR INTERNATIONAL CO., LTD.", + [3]byte{0, 12, 119}: "Life Racing Ltd", + [3]byte{0, 12, 120}: "In-Tech Electronics Limited", + [3]byte{0, 12, 121}: "Extel Communications P/L", + [3]byte{0, 12, 122}: "DaTARIUS Technologies GmbH", + [3]byte{0, 12, 123}: "ALPHA PROJECT Co.,Ltd.", + [3]byte{0, 12, 124}: "Internet Information Image Inc.", + [3]byte{0, 12, 125}: "TEIKOKU ELECTRIC MFG. CO., LTD", + [3]byte{0, 12, 126}: "Tellium Incorporated", + [3]byte{0, 12, 127}: "synertronixx GmbH", + [3]byte{0, 12, 128}: "Opelcomm Inc.", + [3]byte{0, 12, 129}: "Schneider Electric (Australia) ", + [3]byte{0, 12, 130}: "NETWORK TECHNOLOGIES INC", + [3]byte{0, 12, 131}: "Logical Solutions", + [3]byte{0, 12, 132}: "Eazix, Inc.", + [3]byte{0, 12, 133}: "Cisco Systems, Inc", + [3]byte{0, 12, 134}: "Cisco Systems, Inc", + [3]byte{0, 12, 135}: "AMD", + [3]byte{0, 12, 136}: "Apache Micro Peripherals, Inc.", + [3]byte{0, 12, 137}: "AC Electric Vehicles, Ltd.", + [3]byte{0, 12, 138}: "Bose Corporation", + [3]byte{0, 12, 139}: "Connect Tech Inc", + [3]byte{0, 12, 140}: "KODICOM CO.,LTD.", + [3]byte{0, 12, 141}: "MATRIX VISION GmbH", + [3]byte{0, 12, 142}: "Mentor Engineering Inc", + [3]byte{0, 12, 143}: "Nergal s.r.l.", + [3]byte{0, 12, 144}: "Octasic Inc.", + [3]byte{0, 12, 145}: "Riverhead Networks Inc.", + [3]byte{0, 12, 146}: "WolfVision Gmbh", + [3]byte{0, 12, 147}: "Xeline Co., Ltd.", + [3]byte{0, 12, 148}: "United Electronic Industries, Inc. (EUI)", + [3]byte{0, 12, 149}: "PrimeNet", + [3]byte{0, 12, 150}: "OQO, Inc.", + [3]byte{0, 12, 151}: "NV ADB TTV Technologies SA", + [3]byte{0, 12, 152}: "LETEK Communications Inc.", + [3]byte{0, 12, 153}: "HITEL LINK Co.,Ltd", + [3]byte{0, 12, 154}: "Hitech Electronics Corp.", + [3]byte{0, 12, 155}: "EE Solutions, Inc", + [3]byte{0, 12, 156}: "Chongho information & communications", + [3]byte{0, 12, 157}: "UbeeAirWalk, Inc.", + [3]byte{0, 12, 158}: "MemoryLink Corp.", + [3]byte{0, 12, 159}: "NKE Corporation", + [3]byte{0, 12, 160}: "StorCase Technology, Inc.", + [3]byte{0, 12, 161}: "SIGMACOM Co., LTD.", + [3]byte{0, 12, 162}: "Harmonic Video Network", + [3]byte{0, 12, 163}: "Rancho Technology, Inc.", + [3]byte{0, 12, 164}: "Prompttec Product Management GmbH", + [3]byte{0, 12, 165}: "Naman NZ LTd", + [3]byte{0, 12, 166}: "Mintera Corporation", + [3]byte{0, 12, 167}: "Metro (Suzhou) Technologies Co., Ltd.", + [3]byte{0, 12, 168}: "Garuda Networks Corporation", + [3]byte{0, 12, 169}: "Ebtron Inc.", + [3]byte{0, 12, 170}: "Cubic Transportation Systems Inc", + [3]byte{0, 12, 171}: "COMMEND International", + [3]byte{0, 12, 172}: "Citizen Watch Co., Ltd.", + [3]byte{0, 12, 173}: "BTU International", + [3]byte{0, 12, 174}: "Ailocom Oy", + [3]byte{0, 12, 175}: "TRI TERM CO.,LTD.", + [3]byte{0, 12, 176}: "Star Semiconductor Corporation", + [3]byte{0, 12, 177}: "Salland Engineering (Europe) BV", + [3]byte{0, 12, 178}: "UNION co., ltd.", + [3]byte{0, 12, 179}: "ROUND Co.,Ltd.", + [3]byte{0, 12, 180}: "AutoCell Laboratories, Inc.", + [3]byte{0, 12, 181}: "Premier Technolgies, Inc", + [3]byte{0, 12, 182}: "NANJING SEU MOBILE & INTERNET TECHNOLOGY CO.,LTD", + [3]byte{0, 12, 183}: "Nanjing Huazhuo Electronics Co., Ltd.", + [3]byte{0, 12, 184}: "MEDION AG", + [3]byte{0, 12, 185}: "LEA", + [3]byte{0, 12, 186}: "Jamex, Inc.", + [3]byte{0, 12, 187}: "ISKRAEMECO", + [3]byte{0, 12, 188}: "Iscutum", + [3]byte{0, 12, 189}: "Interface Masters, Inc", + [3]byte{0, 12, 190}: "Innominate Security Technologies AG", + [3]byte{0, 12, 191}: "Holy Stone Ent. Co., Ltd.", + [3]byte{0, 12, 192}: "Genera Oy", + [3]byte{0, 12, 193}: "Eaton Corporation", + [3]byte{0, 12, 194}: "ControlNet (India) Private Limited", + [3]byte{0, 12, 195}: "BeWAN systems", + [3]byte{0, 12, 196}: "Tiptel AG", + [3]byte{0, 12, 197}: "Nextlink Co., Ltd.", + [3]byte{0, 12, 198}: "Ka-Ro electronics GmbH", + [3]byte{0, 12, 199}: "Intelligent Computer Solutions Inc.", + [3]byte{0, 12, 200}: "Xytronix Research & Design, Inc.", + [3]byte{0, 12, 201}: "ILWOO DATA & TECHNOLOGY CO.,LTD", + [3]byte{0, 12, 202}: "HGST a Western Digital Company", + [3]byte{0, 12, 203}: "Design Combus Ltd", + [3]byte{0, 12, 204}: "Aeroscout Ltd.", + [3]byte{0, 12, 205}: "IEC - TC57", + [3]byte{0, 12, 206}: "Cisco Systems, Inc", + [3]byte{0, 12, 207}: "Cisco Systems, Inc", + [3]byte{0, 12, 208}: "Symetrix", + [3]byte{0, 12, 209}: "SFOM Technology Corp.", + [3]byte{0, 12, 210}: "Schaffner EMV AG", + [3]byte{0, 12, 211}: "Prettl Elektronik Radeberg GmbH", + [3]byte{0, 12, 212}: "Positron Public Safety Systems inc.", + [3]byte{0, 12, 213}: "Passave Inc.", + [3]byte{0, 12, 214}: "PARTNER TECH", + [3]byte{0, 12, 215}: "Nallatech Ltd", + [3]byte{0, 12, 216}: "M. K. Juchheim GmbH & Co", + [3]byte{0, 12, 217}: "Itcare Co., Ltd", + [3]byte{0, 12, 218}: "FreeHand Systems, Inc.", + [3]byte{0, 12, 219}: "Brocade Communications Systems, Inc.", + [3]byte{0, 12, 220}: "BECS Technology, Inc", + [3]byte{0, 12, 221}: "AOS technologies AG", + [3]byte{0, 12, 222}: "ABB STOTZ-KONTAKT GmbH", + [3]byte{0, 12, 223}: "PULNiX America, Inc", + [3]byte{0, 12, 224}: "Trek Diagnostics Inc.", + [3]byte{0, 12, 225}: "The Open Group", + [3]byte{0, 12, 226}: "Rolls-Royce", + [3]byte{0, 12, 227}: "Option International N.V.", + [3]byte{0, 12, 228}: "NeuroCom International, Inc.", + [3]byte{0, 12, 229}: "ARRIS Group, Inc.", + [3]byte{0, 12, 230}: "Meru Networks Inc", + [3]byte{0, 12, 231}: "MediaTek Inc.", + [3]byte{0, 12, 232}: "GuangZhou AnJuBao Co., Ltd", + [3]byte{0, 12, 233}: "BLOOMBERG L.P.", + [3]byte{0, 12, 234}: "aphona Kommunikationssysteme", + [3]byte{0, 12, 235}: "CNMP Networks, Inc.", + [3]byte{0, 12, 236}: "Spectracom Corp.", + [3]byte{0, 12, 237}: "Real Digital Media", + [3]byte{0, 12, 238}: "jp-embedded", + [3]byte{0, 12, 239}: "Open Networks Engineering Ltd", + [3]byte{0, 12, 240}: "M & N GmbH", + [3]byte{0, 12, 241}: "Intel Corporation", + [3]byte{0, 12, 242}: "GAMESA Eólica", + [3]byte{0, 12, 243}: "CALL IMAGE SA", + [3]byte{0, 12, 244}: "AKATSUKI ELECTRIC MFG.CO.,LTD.", + [3]byte{0, 12, 245}: "InfoExpress", + [3]byte{0, 12, 246}: "Sitecom Europe BV", + [3]byte{0, 12, 247}: "Nortel Networks", + [3]byte{0, 12, 248}: "Nortel Networks", + [3]byte{0, 12, 249}: "Xylem Water Solutions", + [3]byte{0, 12, 250}: "Digital Systems Corp", + [3]byte{0, 12, 251}: "Korea Network Systems", + [3]byte{0, 12, 252}: "S2io Technologies Corp", + [3]byte{0, 12, 253}: "Hyundai ImageQuest Co.,Ltd.", + [3]byte{0, 12, 254}: "Grand Electronic Co., Ltd", + [3]byte{0, 12, 255}: "MRO-TEK LIMITED", + [3]byte{0, 13, 0}: "Seaway Networks Inc.", + [3]byte{0, 13, 1}: "P&E Microcomputer Systems, Inc.", + [3]byte{0, 13, 2}: "NEC Platforms, Ltd.", + [3]byte{0, 13, 3}: "Matrics, Inc.", + [3]byte{0, 13, 4}: "Foxboro Eckardt Development GmbH", + [3]byte{0, 13, 5}: "cybernet manufacturing inc.", + [3]byte{0, 13, 6}: "Compulogic Limited", + [3]byte{0, 13, 7}: "Calrec Audio Ltd", + [3]byte{0, 13, 8}: "AboveCable, Inc.", + [3]byte{0, 13, 9}: "Yuehua(Zhuhai) Electronic CO. LTD", + [3]byte{0, 13, 10}: "Projectiondesign as", + [3]byte{0, 13, 11}: "BUFFALO.INC", + [3]byte{0, 13, 12}: "MDI Security Systems", + [3]byte{0, 13, 13}: "ITSupported, LLC", + [3]byte{0, 13, 14}: "Inqnet Systems, Inc.", + [3]byte{0, 13, 15}: "Finlux Ltd", + [3]byte{0, 13, 16}: "Embedtronics Oy", + [3]byte{0, 13, 17}: "DENTSPLY - Gendex", + [3]byte{0, 13, 18}: "AXELL Corporation", + [3]byte{0, 13, 19}: "Wilhelm Rutenbeck GmbH&Co.KG", + [3]byte{0, 13, 20}: "Vtech Innovation LP dba Advanced American Telephones", + [3]byte{0, 13, 21}: "Voipac s.r.o.", + [3]byte{0, 13, 22}: "UHS Systems Pty Ltd", + [3]byte{0, 13, 23}: "Turbo Networks Co.Ltd", + [3]byte{0, 13, 24}: "Mega-Trend Electronics CO., LTD.", + [3]byte{0, 13, 25}: "ROBE Show lighting", + [3]byte{0, 13, 26}: "Mustek System Inc.", + [3]byte{0, 13, 27}: "Kyoto Electronics Manufacturing Co., Ltd.", + [3]byte{0, 13, 28}: "Amesys Defense", + [3]byte{0, 13, 29}: "HIGH-TEK HARNESS ENT. CO., LTD.", + [3]byte{0, 13, 30}: "Control Techniques", + [3]byte{0, 13, 31}: "AV Digital", + [3]byte{0, 13, 32}: "ASAHIKASEI TECHNOSYSTEM CO.,LTD.", + [3]byte{0, 13, 33}: "WISCORE Inc.", + [3]byte{0, 13, 34}: "Unitronics LTD", + [3]byte{0, 13, 35}: "Smart Solution, Inc", + [3]byte{0, 13, 36}: "SENTEC E&E CO., LTD.", + [3]byte{0, 13, 37}: "SANDEN CORPORATION", + [3]byte{0, 13, 38}: "Primagraphics Limited", + [3]byte{0, 13, 39}: "MICROPLEX Printware AG", + [3]byte{0, 13, 40}: "Cisco Systems, Inc", + [3]byte{0, 13, 41}: "Cisco Systems, Inc", + [3]byte{0, 13, 42}: "Scanmatic AS", + [3]byte{0, 13, 43}: "Racal Instruments", + [3]byte{0, 13, 44}: "Net2Edge Limited", + [3]byte{0, 13, 45}: "NCT Deutschland GmbH", + [3]byte{0, 13, 46}: "Matsushita Avionics Systems Corporation", + [3]byte{0, 13, 47}: "AIN Comm.Tech.Co., LTD", + [3]byte{0, 13, 48}: "IceFyre Semiconductor", + [3]byte{0, 13, 49}: "Compellent Technologies, Inc.", + [3]byte{0, 13, 50}: "DispenseSource, Inc.", + [3]byte{0, 13, 51}: "Prediwave Corp.", + [3]byte{0, 13, 52}: "Shell International Exploration and Production, Inc.", + [3]byte{0, 13, 53}: "PAC International Ltd", + [3]byte{0, 13, 54}: "Wu Han Routon Electronic Co., Ltd", + [3]byte{0, 13, 55}: "WIPLUG", + [3]byte{0, 13, 56}: "NISSIN INC.", + [3]byte{0, 13, 57}: "Network Electronics", + [3]byte{0, 13, 58}: "Microsoft Corp.", + [3]byte{0, 13, 59}: "Microelectronics Technology Inc.", + [3]byte{0, 13, 60}: "i.Tech Dynamic Ltd", + [3]byte{0, 13, 61}: "Hammerhead Systems, Inc.", + [3]byte{0, 13, 62}: "APLUX Communications Ltd.", + [3]byte{0, 13, 63}: "VTI Instruments Corporation", + [3]byte{0, 13, 64}: "Verint Loronix Video Solutions", + [3]byte{0, 13, 65}: "Siemens AG ICM MP UC RD IT KLF1", + [3]byte{0, 13, 66}: "Newbest Development Limited", + [3]byte{0, 13, 67}: "DRS Tactical Systems Inc.", + [3]byte{0, 13, 68}: "Audio BU - Logitech", + [3]byte{0, 13, 69}: "Tottori SANYO Electric Co., Ltd.", + [3]byte{0, 13, 70}: "Parker SSD Drives", + [3]byte{0, 13, 71}: "Collex", + [3]byte{0, 13, 72}: "AEWIN Technologies Co., Ltd.", + [3]byte{0, 13, 73}: "Triton Systems of Delaware, Inc.", + [3]byte{0, 13, 74}: "Steag ETA-Optik", + [3]byte{0, 13, 75}: "Roku, Inc.", + [3]byte{0, 13, 76}: "Outline Electronics Ltd.", + [3]byte{0, 13, 77}: "Ninelanes", + [3]byte{0, 13, 78}: "NDR Co.,LTD.", + [3]byte{0, 13, 79}: "Kenwood Corporation", + [3]byte{0, 13, 80}: "Galazar Networks", + [3]byte{0, 13, 81}: "DIVR Systems, Inc.", + [3]byte{0, 13, 82}: "Comart system", + [3]byte{0, 13, 83}: "Beijing 5w Communication Corp.", + [3]byte{0, 13, 84}: "3Com Ltd", + [3]byte{0, 13, 85}: "SANYCOM Technology Co.,Ltd", + [3]byte{0, 13, 86}: "Dell Inc.", + [3]byte{0, 13, 87}: "Fujitsu I-Network Systems Limited.", + [3]byte{0, 13, 88}: "Private", + [3]byte{0, 13, 89}: "Amity Systems, Inc.", + [3]byte{0, 13, 90}: "Tiesse SpA", + [3]byte{0, 13, 91}: "Smart Empire Investments Limited", + [3]byte{0, 13, 92}: "Robert Bosch GmbH, VT-ATMO", + [3]byte{0, 13, 93}: "Raritan Computer, Inc", + [3]byte{0, 13, 94}: "NEC Personal Products", + [3]byte{0, 13, 95}: "Minds Inc", + [3]byte{0, 13, 96}: "IBM Corp", + [3]byte{0, 13, 97}: "Giga-Byte Technology Co., Ltd.", + [3]byte{0, 13, 98}: "Funkwerk Dabendorf GmbH", + [3]byte{0, 13, 99}: "DENT Instruments, Inc.", + [3]byte{0, 13, 100}: "COMAG Handels AG", + [3]byte{0, 13, 101}: "Cisco Systems, Inc", + [3]byte{0, 13, 102}: "Cisco Systems, Inc", + [3]byte{0, 13, 103}: "Ericsson", + [3]byte{0, 13, 104}: "Vinci Systems, Inc.", + [3]byte{0, 13, 105}: "TMT&D Corporation", + [3]byte{0, 13, 106}: "Redwood Technologies LTD", + [3]byte{0, 13, 107}: "Mita-Teknik A/S", + [3]byte{0, 13, 108}: "M-Audio", + [3]byte{0, 13, 109}: "K-Tech Devices Corp.", + [3]byte{0, 13, 110}: "K-Patents Oy", + [3]byte{0, 13, 111}: "Ember Corporation", + [3]byte{0, 13, 112}: "Datamax Corporation", + [3]byte{0, 13, 113}: "boca systems", + [3]byte{0, 13, 114}: "2Wire Inc", + [3]byte{0, 13, 115}: "Technical Support, Inc.", + [3]byte{0, 13, 116}: "Sand Network Systems, Inc.", + [3]byte{0, 13, 117}: "Kobian Pte Ltd - Taiwan Branch", + [3]byte{0, 13, 118}: "Hokuto Denshi Co,. Ltd.", + [3]byte{0, 13, 119}: "FalconStor Software", + [3]byte{0, 13, 120}: "Engineering & Security", + [3]byte{0, 13, 121}: "Dynamic Solutions Co,.Ltd.", + [3]byte{0, 13, 122}: "DiGATTO Asia Pacific Pte Ltd", + [3]byte{0, 13, 123}: "Consensys Computers Inc.", + [3]byte{0, 13, 124}: "Codian Ltd", + [3]byte{0, 13, 125}: "Afco Systems", + [3]byte{0, 13, 126}: "Axiowave Networks, Inc.", + [3]byte{0, 13, 127}: "MIDAS COMMUNICATION TECHNOLOGIES PTE LTD ( Foreign Branch)", + [3]byte{0, 13, 128}: "Online Development Inc", + [3]byte{0, 13, 129}: "Pepperl+Fuchs GmbH", + [3]byte{0, 13, 130}: "PHS srl", + [3]byte{0, 13, 131}: "Sanmina-SCI Hungary Ltd.", + [3]byte{0, 13, 132}: "Makus Inc.", + [3]byte{0, 13, 133}: "Tapwave, Inc.", + [3]byte{0, 13, 134}: "Huber + Suhner AG", + [3]byte{0, 13, 135}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 13, 136}: "D-Link Corporation", + [3]byte{0, 13, 137}: "Bils Technology Inc", + [3]byte{0, 13, 138}: "Winners Electronics Co., Ltd.", + [3]byte{0, 13, 139}: "T&D Corporation", + [3]byte{0, 13, 140}: "Shanghai Wedone Digital Ltd. CO.", + [3]byte{0, 13, 141}: "Prosoft Technology, Inc", + [3]byte{0, 13, 142}: "Koden Electronics Co., Ltd.", + [3]byte{0, 13, 143}: "King Tsushin Kogyo Co., LTD.", + [3]byte{0, 13, 144}: "Factum Electronics AB", + [3]byte{0, 13, 145}: "Eclipse (HQ Espana) S.L.", + [3]byte{0, 13, 146}: "ARIMA Communications Corp.", + [3]byte{0, 13, 147}: "Apple, Inc.", + [3]byte{0, 13, 148}: "AFAR Communications,Inc", + [3]byte{0, 13, 149}: "Opti-cell, Inc.", + [3]byte{0, 13, 150}: "Vtera Technology Inc.", + [3]byte{0, 13, 151}: "ABB Inc./Tropos", + [3]byte{0, 13, 152}: "S.W.A.C. Schmitt-Walter Automation Consult GmbH", + [3]byte{0, 13, 153}: "Orbital Sciences Corp.; Launch Systems Group", + [3]byte{0, 13, 154}: "INFOTEC LTD", + [3]byte{0, 13, 155}: "Heraeus Electro-Nite International N.V.", + [3]byte{0, 13, 156}: "Elan GmbH & Co KG", + [3]byte{0, 13, 157}: "Hewlett Packard", + [3]byte{0, 13, 158}: "TOKUDEN OHIZUMI SEISAKUSYO Co.,Ltd.", + [3]byte{0, 13, 159}: "RF Micro Devices", + [3]byte{0, 13, 160}: "NEDAP N.V.", + [3]byte{0, 13, 161}: "MIRAE ITS Co.,LTD.", + [3]byte{0, 13, 162}: "Infrant Technologies, Inc.", + [3]byte{0, 13, 163}: "Emerging Technologies Limited", + [3]byte{0, 13, 164}: "DOSCH & AMAND SYSTEMS AG", + [3]byte{0, 13, 165}: "Fabric7 Systems, Inc", + [3]byte{0, 13, 166}: "Universal Switching Corporation", + [3]byte{0, 13, 167}: "Private", + [3]byte{0, 13, 168}: "Teletronics Technology Corporation", + [3]byte{0, 13, 169}: "T.E.A.M. S.L.", + [3]byte{0, 13, 170}: "S.A.Tehnology co.,Ltd.", + [3]byte{0, 13, 171}: "Parker Hannifin GmbH Electromechanical Division Europe", + [3]byte{0, 13, 172}: "Japan CBM Corporation", + [3]byte{0, 13, 173}: "Dataprobe, Inc.", + [3]byte{0, 13, 174}: "SAMSUNG HEAVY INDUSTRIES CO., LTD.", + [3]byte{0, 13, 175}: "Plexus Corp (UK) Ltd", + [3]byte{0, 13, 176}: "Olym-tech Co.,Ltd.", + [3]byte{0, 13, 177}: "Japan Network Service Co., Ltd.", + [3]byte{0, 13, 178}: "Ammasso, Inc.", + [3]byte{0, 13, 179}: "SDO Communication Corperation", + [3]byte{0, 13, 180}: "NETASQ", + [3]byte{0, 13, 181}: "GLOBALSAT TECHNOLOGY CORPORATION", + [3]byte{0, 13, 182}: "Broadcom", + [3]byte{0, 13, 183}: "SANKO ELECTRIC CO,.LTD", + [3]byte{0, 13, 184}: "SCHILLER AG", + [3]byte{0, 13, 185}: "PC Engines GmbH", + [3]byte{0, 13, 186}: "Océ Document Technologies GmbH", + [3]byte{0, 13, 187}: "Nippon Dentsu Co.,Ltd.", + [3]byte{0, 13, 188}: "Cisco Systems, Inc", + [3]byte{0, 13, 189}: "Cisco Systems, Inc", + [3]byte{0, 13, 190}: "Bel Fuse Europe Ltd.,UK", + [3]byte{0, 13, 191}: "TekTone Sound & Signal Mfg., Inc.", + [3]byte{0, 13, 192}: "Spagat AS", + [3]byte{0, 13, 193}: "SafeWeb Inc", + [3]byte{0, 13, 194}: "Private", + [3]byte{0, 13, 195}: "First Communication, Inc.", + [3]byte{0, 13, 196}: "Emcore Corporation", + [3]byte{0, 13, 197}: "EchoStar Global B.V. ", + [3]byte{0, 13, 198}: "DigiRose Technology Co., Ltd.", + [3]byte{0, 13, 199}: "COSMIC ENGINEERING INC.", + [3]byte{0, 13, 200}: "AirMagnet, Inc", + [3]byte{0, 13, 201}: "THALES Elektronik Systeme GmbH", + [3]byte{0, 13, 202}: "Tait Electronics", + [3]byte{0, 13, 203}: "Petcomkorea Co., Ltd.", + [3]byte{0, 13, 204}: "NEOSMART Corp.", + [3]byte{0, 13, 205}: "GROUPE TXCOM", + [3]byte{0, 13, 206}: "Dynavac Technology Pte Ltd", + [3]byte{0, 13, 207}: "Cidra Corp.", + [3]byte{0, 13, 208}: "TetraTec Instruments GmbH", + [3]byte{0, 13, 209}: "Stryker Corporation", + [3]byte{0, 13, 210}: "Simrad Optronics ASA", + [3]byte{0, 13, 211}: "SAMWOO Telecommunication Co.,Ltd.", + [3]byte{0, 13, 212}: "Symantec Corporation", + [3]byte{0, 13, 213}: "O'RITE TECHNOLOGY CO.,LTD", + [3]byte{0, 13, 214}: "ITI LTD", + [3]byte{0, 13, 215}: "Bright", + [3]byte{0, 13, 216}: "BBN", + [3]byte{0, 13, 217}: "Anton Paar GmbH", + [3]byte{0, 13, 218}: "ALLIED TELESIS K.K.", + [3]byte{0, 13, 219}: "AIRWAVE TECHNOLOGIES INC.", + [3]byte{0, 13, 220}: "VAC", + [3]byte{0, 13, 221}: "Profilo Telra Elektronik Sanayi ve Ticaret. A.Ş", + [3]byte{0, 13, 222}: "Joyteck Co., Ltd.", + [3]byte{0, 13, 223}: "Japan Image & Network Inc.", + [3]byte{0, 13, 224}: "ICPDAS Co.,LTD", + [3]byte{0, 13, 225}: "Control Products, Inc.", + [3]byte{0, 13, 226}: "CMZ Sistemi Elettronici", + [3]byte{0, 13, 227}: "AT Sweden AB", + [3]byte{0, 13, 228}: "DIGINICS, Inc.", + [3]byte{0, 13, 229}: "Samsung Thales", + [3]byte{0, 13, 230}: "YOUNGBO ENGINEERING CO.,LTD", + [3]byte{0, 13, 231}: "Snap-on OEM Group", + [3]byte{0, 13, 232}: "Nasaco Electronics Pte. Ltd", + [3]byte{0, 13, 233}: "Napatech Aps", + [3]byte{0, 13, 234}: "Kingtel Telecommunication Corp.", + [3]byte{0, 13, 235}: "CompXs Limited", + [3]byte{0, 13, 236}: "Cisco Systems, Inc", + [3]byte{0, 13, 237}: "Cisco Systems, Inc", + [3]byte{0, 13, 238}: "Andrew RF Power Amplifier Group", + [3]byte{0, 13, 239}: "Soc. Coop. Bilanciai", + [3]byte{0, 13, 240}: "QCOM TECHNOLOGY INC.", + [3]byte{0, 13, 241}: "IONIX INC.", + [3]byte{0, 13, 242}: "Private", + [3]byte{0, 13, 243}: "Asmax Solutions", + [3]byte{0, 13, 244}: "Watertek Co.", + [3]byte{0, 13, 245}: "Teletronics International Inc.", + [3]byte{0, 13, 246}: "Technology Thesaurus Corp.", + [3]byte{0, 13, 247}: "Space Dynamics Lab", + [3]byte{0, 13, 248}: "ORGA Kartensysteme GmbH", + [3]byte{0, 13, 249}: "NDS Limited", + [3]byte{0, 13, 250}: "Micro Control Systems Ltd.", + [3]byte{0, 13, 251}: "Komax AG", + [3]byte{0, 13, 252}: "ITFOR Inc.", + [3]byte{0, 13, 253}: "Huges Hi-Tech Inc.,", + [3]byte{0, 13, 254}: "Hauppauge Computer Works, Inc.", + [3]byte{0, 13, 255}: "CHENMING MOLD INDUSTRY CORP.", + [3]byte{0, 14, 0}: "Atrie", + [3]byte{0, 14, 1}: "ASIP Technologies Inc.", + [3]byte{0, 14, 2}: "Advantech AMT Inc.", + [3]byte{0, 14, 3}: "Emulex Corporation", + [3]byte{0, 14, 4}: "CMA/Microdialysis AB", + [3]byte{0, 14, 5}: "WIRELESS MATRIX CORP.", + [3]byte{0, 14, 6}: "Team Simoco Ltd", + [3]byte{0, 14, 7}: "Sony Mobile Communications AB", + [3]byte{0, 14, 8}: "Cisco-Linksys, LLC", + [3]byte{0, 14, 9}: "Shenzhen Coship Software Co.,LTD.", + [3]byte{0, 14, 10}: "SAKUMA DESIGN OFFICE", + [3]byte{0, 14, 11}: "Netac Technology Co., Ltd.", + [3]byte{0, 14, 12}: "Intel Corporation", + [3]byte{0, 14, 13}: "Hesch Schröder GmbH", + [3]byte{0, 14, 14}: "ESA elettronica S.P.A.", + [3]byte{0, 14, 15}: "ERMME", + [3]byte{0, 14, 16}: "C-guys, Inc.", + [3]byte{0, 14, 17}: "BDT Büro und Datentechnik GmbH & Co.KG ", + [3]byte{0, 14, 18}: "Adaptive Micro Systems Inc.", + [3]byte{0, 14, 19}: "Accu-Sort Systems inc.", + [3]byte{0, 14, 20}: "Visionary Solutions, Inc.", + [3]byte{0, 14, 21}: "Tadlys LTD", + [3]byte{0, 14, 22}: "SouthWing S.L.", + [3]byte{0, 14, 23}: "Private", + [3]byte{0, 14, 24}: "MyA Technology", + [3]byte{0, 14, 25}: "LogicaCMG Pty Ltd", + [3]byte{0, 14, 26}: "JPS Communications", + [3]byte{0, 14, 27}: "IAV GmbH", + [3]byte{0, 14, 28}: "Hach Company", + [3]byte{0, 14, 29}: "ARION Technology Inc.", + [3]byte{0, 14, 30}: "QLogic Corporation", + [3]byte{0, 14, 31}: "TCL Networks Equipment Co., Ltd.", + [3]byte{0, 14, 32}: "ACCESS Systems Americas, Inc.", + [3]byte{0, 14, 33}: "MTU Friedrichshafen GmbH", + [3]byte{0, 14, 34}: "Private", + [3]byte{0, 14, 35}: "Incipient, Inc.", + [3]byte{0, 14, 36}: "Huwell Technology Inc.", + [3]byte{0, 14, 37}: "Hannae Technology Co., Ltd", + [3]byte{0, 14, 38}: "Gincom Technology Corp.", + [3]byte{0, 14, 39}: "Crere Networks, Inc.", + [3]byte{0, 14, 40}: "Dynamic Ratings P/L", + [3]byte{0, 14, 41}: "Shester Communications Inc", + [3]byte{0, 14, 42}: "Private", + [3]byte{0, 14, 43}: "Safari Technologies", + [3]byte{0, 14, 44}: "Netcodec co.", + [3]byte{0, 14, 45}: "Hyundai Digital Technology Co.,Ltd.", + [3]byte{0, 14, 46}: "Edimax Technology Co. Ltd.", + [3]byte{0, 14, 47}: "Roche Diagnostics GmbH", + [3]byte{0, 14, 48}: "AERAS Networks, Inc.", + [3]byte{0, 14, 49}: "Olympus Soft Imaging Solutions GmbH", + [3]byte{0, 14, 50}: "Kontron Medical", + [3]byte{0, 14, 51}: "Shuko Electronics Co.,Ltd", + [3]byte{0, 14, 52}: "NexGen City, LP", + [3]byte{0, 14, 53}: "Intel Corporation", + [3]byte{0, 14, 54}: "HEINESYS, Inc.", + [3]byte{0, 14, 55}: "Harms & Wende GmbH & Co.KG", + [3]byte{0, 14, 56}: "Cisco Systems, Inc", + [3]byte{0, 14, 57}: "Cisco Systems, Inc", + [3]byte{0, 14, 58}: "Cirrus Logic", + [3]byte{0, 14, 59}: "Hawking Technologies, Inc.", + [3]byte{0, 14, 60}: "Transact Technologies Inc", + [3]byte{0, 14, 61}: "Televic N.V.", + [3]byte{0, 14, 62}: "Sun Optronics Inc", + [3]byte{0, 14, 63}: "Soronti, Inc.", + [3]byte{0, 14, 64}: "Nortel Networks", + [3]byte{0, 14, 65}: "NIHON MECHATRONICS CO.,LTD.", + [3]byte{0, 14, 66}: "Motic Incoporation Ltd.", + [3]byte{0, 14, 67}: "G-Tek Electronics Sdn. Bhd.", + [3]byte{0, 14, 68}: "Digital 5, Inc.", + [3]byte{0, 14, 69}: "Beijing Newtry Electronic Technology Ltd", + [3]byte{0, 14, 70}: "Niigata Seimitsu Co.,Ltd.", + [3]byte{0, 14, 71}: "NCI System Co.,Ltd.", + [3]byte{0, 14, 72}: "Lipman TransAction Solutions", + [3]byte{0, 14, 73}: "Forsway Scandinavia AB", + [3]byte{0, 14, 74}: "Changchun Huayu WEBPAD Co.,LTD", + [3]byte{0, 14, 75}: "atrium c and i", + [3]byte{0, 14, 76}: "Bermai Inc.", + [3]byte{0, 14, 77}: "Numesa Inc.", + [3]byte{0, 14, 78}: "Waveplus Technology Co., Ltd.", + [3]byte{0, 14, 79}: "Trajet GmbH", + [3]byte{0, 14, 80}: "Thomson Telecom Belgium", + [3]byte{0, 14, 81}: "tecna elettronica srl", + [3]byte{0, 14, 82}: "Optium Corporation", + [3]byte{0, 14, 83}: "AV TECH CORPORATION", + [3]byte{0, 14, 84}: "AlphaCell Wireless Ltd.", + [3]byte{0, 14, 85}: "AUVITRAN", + [3]byte{0, 14, 86}: "4G Systems GmbH & Co. KG", + [3]byte{0, 14, 87}: "Iworld Networking, Inc.", + [3]byte{0, 14, 88}: "Sonos, Inc.", + [3]byte{0, 14, 89}: "Sagemcom Broadband SAS", + [3]byte{0, 14, 90}: "TELEFIELD inc.", + [3]byte{0, 14, 91}: "ParkerVision - Direct2Data", + [3]byte{0, 14, 92}: "ARRIS Group, Inc.", + [3]byte{0, 14, 93}: "Triple Play Technologies A/S", + [3]byte{0, 14, 94}: "Raisecom Technology", + [3]byte{0, 14, 95}: "activ-net GmbH & Co. KG", + [3]byte{0, 14, 96}: "360SUN Digital Broadband Corporation", + [3]byte{0, 14, 97}: "MICROTROL LIMITED", + [3]byte{0, 14, 98}: "Nortel Networks", + [3]byte{0, 14, 99}: "Lemke Diagnostics GmbH", + [3]byte{0, 14, 100}: "Elphel, Inc", + [3]byte{0, 14, 101}: "TransCore", + [3]byte{0, 14, 102}: "Hitachi Industry & Control Solutions, Ltd.", + [3]byte{0, 14, 103}: "Eltis Microelectronics Ltd.", + [3]byte{0, 14, 104}: "E-TOP Network Technology Inc.", + [3]byte{0, 14, 105}: "China Electric Power Research Institute", + [3]byte{0, 14, 106}: "3Com Ltd", + [3]byte{0, 14, 107}: "Janitza electronics GmbH", + [3]byte{0, 14, 108}: "Device Drivers Limited", + [3]byte{0, 14, 109}: "Murata Manufacturing Co., Ltd.", + [3]byte{0, 14, 110}: "MAT S.A. (Mircrelec Advanced Technology)", + [3]byte{0, 14, 111}: "IRIS Corporation Berhad", + [3]byte{0, 14, 112}: "in2 Networks", + [3]byte{0, 14, 113}: "Gemstar Technology Development Ltd.", + [3]byte{0, 14, 114}: "CTS electronics", + [3]byte{0, 14, 115}: "Tpack A/S", + [3]byte{0, 14, 116}: "Solar Telecom. Tech", + [3]byte{0, 14, 117}: "New York Air Brake Corp.", + [3]byte{0, 14, 118}: "GEMSOC INNOVISION INC.", + [3]byte{0, 14, 119}: "Decru, Inc.", + [3]byte{0, 14, 120}: "Amtelco", + [3]byte{0, 14, 121}: "Ample Communications Inc.", + [3]byte{0, 14, 122}: "GemWon Communications Co., Ltd.", + [3]byte{0, 14, 123}: "Toshiba", + [3]byte{0, 14, 124}: "Televes S.A.", + [3]byte{0, 14, 125}: "Electronics Line 3000 Ltd.", + [3]byte{0, 14, 126}: "ionSign Oy", + [3]byte{0, 14, 127}: "Hewlett Packard", + [3]byte{0, 14, 128}: "Thomson Technology Inc", + [3]byte{0, 14, 129}: "Devicescape Software, Inc.", + [3]byte{0, 14, 130}: "Commtech Wireless", + [3]byte{0, 14, 131}: "Cisco Systems, Inc", + [3]byte{0, 14, 132}: "Cisco Systems, Inc", + [3]byte{0, 14, 133}: "Catalyst Enterprises, Inc.", + [3]byte{0, 14, 134}: "Alcatel North America", + [3]byte{0, 14, 135}: "adp Gauselmann GmbH", + [3]byte{0, 14, 136}: "VIDEOTRON CORP.", + [3]byte{0, 14, 137}: "CLEMATIC", + [3]byte{0, 14, 138}: "Avara Technologies Pty. Ltd.", + [3]byte{0, 14, 139}: "Astarte Technology Co, Ltd.", + [3]byte{0, 14, 140}: "Siemens AG A&D ET", + [3]byte{0, 14, 141}: "Systems in Progress Holding GmbH", + [3]byte{0, 14, 142}: "SparkLAN Communications, Inc.", + [3]byte{0, 14, 143}: "Sercomm Corp.", + [3]byte{0, 14, 144}: "PONICO CORP.", + [3]byte{0, 14, 145}: "Navico Auckland Ltd", + [3]byte{0, 14, 146}: "Open Telecom", + [3]byte{0, 14, 147}: "Milénio 3 Sistemas Electrónicos, Lda.", + [3]byte{0, 14, 148}: "Maas International BV", + [3]byte{0, 14, 149}: "Fujiya Denki Seisakusho Co.,Ltd.", + [3]byte{0, 14, 150}: "Cubic Defense Applications, Inc.", + [3]byte{0, 14, 151}: "Ultracker Technology CO., Inc", + [3]byte{0, 14, 152}: "HME Clear-Com LTD.", + [3]byte{0, 14, 153}: "Spectrum Digital, Inc", + [3]byte{0, 14, 154}: "BOE TECHNOLOGY GROUP CO.,LTD", + [3]byte{0, 14, 155}: "Ambit Microsystems Corporation", + [3]byte{0, 14, 156}: "Benchmark Electronics ", + [3]byte{0, 14, 157}: "Tiscali UK Ltd", + [3]byte{0, 14, 158}: "Topfield Co., Ltd", + [3]byte{0, 14, 159}: "TEMIC SDS GmbH", + [3]byte{0, 14, 160}: "NetKlass Technology Inc.", + [3]byte{0, 14, 161}: "Formosa Teletek Corporation", + [3]byte{0, 14, 162}: "McAfee, Inc", + [3]byte{0, 14, 163}: "CNCR-IT CO.,LTD,HangZhou P.R.CHINA", + [3]byte{0, 14, 164}: "Certance Inc.", + [3]byte{0, 14, 165}: "BLIP Systems", + [3]byte{0, 14, 166}: "ASUSTek COMPUTER INC.", + [3]byte{0, 14, 167}: "Endace Technology", + [3]byte{0, 14, 168}: "United Technologists Europe Limited", + [3]byte{0, 14, 169}: "Shanghai Xun Shi Communications Equipment Ltd. Co.", + [3]byte{0, 14, 170}: "Scalent Systems, Inc.", + [3]byte{0, 14, 171}: "Cray Inc", + [3]byte{0, 14, 172}: "MINTRON ENTERPRISE CO., LTD.", + [3]byte{0, 14, 173}: "Metanoia Technologies, Inc.", + [3]byte{0, 14, 174}: "GAWELL TECHNOLOGIES CORP.", + [3]byte{0, 14, 175}: "CASTEL", + [3]byte{0, 14, 176}: "Solutions Radio BV", + [3]byte{0, 14, 177}: "Newcotech,Ltd", + [3]byte{0, 14, 178}: "Micro-Research Finland Oy", + [3]byte{0, 14, 179}: "Hewlett Packard", + [3]byte{0, 14, 180}: "GUANGZHOU GAOKE COMMUNICATIONS TECHNOLOGY CO.LTD.", + [3]byte{0, 14, 181}: "Ecastle Electronics Co., Ltd.", + [3]byte{0, 14, 182}: "Riverbed Technology, Inc.", + [3]byte{0, 14, 183}: "Knovative, Inc.", + [3]byte{0, 14, 184}: "Iiga co.,Ltd", + [3]byte{0, 14, 185}: "HASHIMOTO Electronics Industry Co.,Ltd.", + [3]byte{0, 14, 186}: "HANMI SEMICONDUCTOR CO., LTD.", + [3]byte{0, 14, 187}: "Everbee Networks", + [3]byte{0, 14, 188}: "Paragon Fidelity GmbH", + [3]byte{0, 14, 189}: "Burdick, a Quinton Compny", + [3]byte{0, 14, 190}: "B&B Electronics Manufacturing Co.", + [3]byte{0, 14, 191}: "Remsdaq Limited", + [3]byte{0, 14, 192}: "Nortel Networks", + [3]byte{0, 14, 193}: "MYNAH Technologies", + [3]byte{0, 14, 194}: "Lowrance Electronics, Inc.", + [3]byte{0, 14, 195}: "Logic Controls, Inc.", + [3]byte{0, 14, 196}: "Iskra Transmission d.d.", + [3]byte{0, 14, 197}: "Digital Multitools Inc", + [3]byte{0, 14, 198}: "ASIX ELECTRONICS CORP.", + [3]byte{0, 14, 199}: "Motorola Korea", + [3]byte{0, 14, 200}: "Zoran Corporation", + [3]byte{0, 14, 201}: "YOKO Technology Corp.", + [3]byte{0, 14, 202}: "WTSS Inc", + [3]byte{0, 14, 203}: "VineSys Technology", + [3]byte{0, 14, 204}: "Tableau, LLC", + [3]byte{0, 14, 205}: "SKOV A/S", + [3]byte{0, 14, 206}: "S.I.T.T.I. S.p.A.", + [3]byte{0, 14, 207}: "PROFIBUS Nutzerorganisation e.V.", + [3]byte{0, 14, 208}: "Privaris, Inc.", + [3]byte{0, 14, 209}: "Osaka Micro Computer.", + [3]byte{0, 14, 210}: "Filtronic plc", + [3]byte{0, 14, 211}: "Epicenter, Inc.", + [3]byte{0, 14, 212}: "CRESITT INDUSTRIE", + [3]byte{0, 14, 213}: "COPAN Systems Inc.", + [3]byte{0, 14, 214}: "Cisco Systems, Inc", + [3]byte{0, 14, 215}: "Cisco Systems, Inc", + [3]byte{0, 14, 216}: "Positron Access Solutions Corp", + [3]byte{0, 14, 217}: "Aksys, Ltd.", + [3]byte{0, 14, 218}: "C-TECH UNITED CORP.", + [3]byte{0, 14, 219}: "XiNCOM Corp.", + [3]byte{0, 14, 220}: "Tellion INC.", + [3]byte{0, 14, 221}: "SHURE INCORPORATED", + [3]byte{0, 14, 222}: "REMEC, Inc.", + [3]byte{0, 14, 223}: "PLX Technology", + [3]byte{0, 14, 224}: "Mcharge", + [3]byte{0, 14, 225}: "ExtremeSpeed Inc.", + [3]byte{0, 14, 226}: "Custom Engineering", + [3]byte{0, 14, 227}: "Chiyu Technology Co.,Ltd", + [3]byte{0, 14, 228}: "BOE TECHNOLOGY GROUP CO.,LTD", + [3]byte{0, 14, 229}: "bitWallet, Inc.", + [3]byte{0, 14, 230}: "Adimos Systems LTD", + [3]byte{0, 14, 231}: "AAC ELECTRONICS CORP.", + [3]byte{0, 14, 232}: "Zioncom Electronics (Shenzhen) Ltd.", + [3]byte{0, 14, 233}: "WayTech Development, Inc.", + [3]byte{0, 14, 234}: "Shadong Luneng Jicheng Electronics,Co.,Ltd", + [3]byte{0, 14, 235}: "Sandmartin(zhong shan)Electronics Co.,Ltd", + [3]byte{0, 14, 236}: "Orban", + [3]byte{0, 14, 237}: "Nokia Danmark A/S", + [3]byte{0, 14, 238}: "Muco Industrie BV", + [3]byte{0, 14, 239}: "Private", + [3]byte{0, 14, 240}: "Festo AG & Co. KG", + [3]byte{0, 14, 241}: "EZQUEST INC.", + [3]byte{0, 14, 242}: "Infinico Corporation", + [3]byte{0, 14, 243}: "Smarthome", + [3]byte{0, 14, 244}: "Kasda Networks Inc", + [3]byte{0, 14, 245}: "iPAC Technology Co., Ltd.", + [3]byte{0, 14, 246}: "E-TEN Information Systems Co., Ltd.", + [3]byte{0, 14, 247}: "Vulcan Portals Inc", + [3]byte{0, 14, 248}: "SBC ASI", + [3]byte{0, 14, 249}: "REA Elektronik GmbH", + [3]byte{0, 14, 250}: "Optoway Technology Incorporation", + [3]byte{0, 14, 251}: "Macey Enterprises", + [3]byte{0, 14, 252}: "JTAG Technologies B.V.", + [3]byte{0, 14, 253}: "FUJINON CORPORATION", + [3]byte{0, 14, 254}: "EndRun Technologies LLC", + [3]byte{0, 14, 255}: "Megasolution,Inc.", + [3]byte{0, 15, 0}: "Legra Systems, Inc.", + [3]byte{0, 15, 1}: "DIGITALKS INC", + [3]byte{0, 15, 2}: "Digicube Technology Co., Ltd", + [3]byte{0, 15, 3}: "COM&C CO., LTD", + [3]byte{0, 15, 4}: "cim-usa inc", + [3]byte{0, 15, 5}: "3B SYSTEM INC.", + [3]byte{0, 15, 6}: "Nortel Networks", + [3]byte{0, 15, 7}: "Mangrove Systems, Inc.", + [3]byte{0, 15, 8}: "Indagon Oy", + [3]byte{0, 15, 9}: "Private", + [3]byte{0, 15, 10}: "Clear Edge Networks", + [3]byte{0, 15, 11}: "Kentima Technologies AB", + [3]byte{0, 15, 12}: "SYNCHRONIC ENGINEERING", + [3]byte{0, 15, 13}: "Hunt Electronic Co., Ltd.", + [3]byte{0, 15, 14}: "WaveSplitter Technologies, Inc.", + [3]byte{0, 15, 15}: "Real ID Technology Co., Ltd.", + [3]byte{0, 15, 16}: "RDM Corporation", + [3]byte{0, 15, 17}: "Prodrive B.V.", + [3]byte{0, 15, 18}: "Panasonic Europe Ltd.", + [3]byte{0, 15, 19}: "Nisca corporation", + [3]byte{0, 15, 20}: "Mindray Co., Ltd.", + [3]byte{0, 15, 21}: "Kjaerulff1 A/S", + [3]byte{0, 15, 22}: "JAY HOW TECHNOLOGY CO.,", + [3]byte{0, 15, 23}: "Insta Elektro GmbH", + [3]byte{0, 15, 24}: "Industrial Control Systems", + [3]byte{0, 15, 25}: "Boston Scientific", + [3]byte{0, 15, 26}: "Gaming Support B.V.", + [3]byte{0, 15, 27}: "Ego Systems Inc.", + [3]byte{0, 15, 28}: "DigitAll World Co., Ltd", + [3]byte{0, 15, 29}: "Cosmo Techs Co., Ltd.", + [3]byte{0, 15, 30}: "Chengdu KT Electric Co.of High & New Technology", + [3]byte{0, 15, 31}: "Dell Inc.", + [3]byte{0, 15, 32}: "Hewlett Packard", + [3]byte{0, 15, 33}: "Scientific Atlanta, Inc", + [3]byte{0, 15, 34}: "Helius, Inc.", + [3]byte{0, 15, 35}: "Cisco Systems, Inc", + [3]byte{0, 15, 36}: "Cisco Systems, Inc", + [3]byte{0, 15, 37}: "AimValley B.V.", + [3]byte{0, 15, 38}: "WorldAccxx LLC", + [3]byte{0, 15, 39}: "TEAL Electronics, Inc.", + [3]byte{0, 15, 40}: "Itronix Corporation", + [3]byte{0, 15, 41}: "Augmentix Corporation", + [3]byte{0, 15, 42}: "Cableware Electronics", + [3]byte{0, 15, 43}: "GREENBELL SYSTEMS", + [3]byte{0, 15, 44}: "Uplogix, Inc.", + [3]byte{0, 15, 45}: "CHUNG-HSIN ELECTRIC & MACHINERY MFG.CORP.", + [3]byte{0, 15, 46}: "Megapower International Corp.", + [3]byte{0, 15, 47}: "W-LINX TECHNOLOGY CO., LTD.", + [3]byte{0, 15, 48}: "Raza Microelectronics Inc", + [3]byte{0, 15, 49}: "Allied Vision Technologies Canada Inc", + [3]byte{0, 15, 50}: "Lootom Telcovideo Network Wuxi Co Ltd", + [3]byte{0, 15, 51}: "DUALi Inc.", + [3]byte{0, 15, 52}: "Cisco Systems, Inc", + [3]byte{0, 15, 53}: "Cisco Systems, Inc", + [3]byte{0, 15, 54}: "Accurate Techhnologies, Inc.", + [3]byte{0, 15, 55}: "Xambala Incorporated", + [3]byte{0, 15, 56}: "Netstar", + [3]byte{0, 15, 57}: "IRIS SENSORS", + [3]byte{0, 15, 58}: "HISHARP", + [3]byte{0, 15, 59}: "Fuji System Machines Co., Ltd.", + [3]byte{0, 15, 60}: "Endeleo Limited", + [3]byte{0, 15, 61}: "D-Link Corporation", + [3]byte{0, 15, 62}: "CardioNet, Inc", + [3]byte{0, 15, 63}: "Big Bear Networks", + [3]byte{0, 15, 64}: "Optical Internetworking Forum", + [3]byte{0, 15, 65}: "Zipher Ltd", + [3]byte{0, 15, 66}: "Xalyo Systems", + [3]byte{0, 15, 67}: "Wasabi Systems Inc.", + [3]byte{0, 15, 68}: "Tivella Inc.", + [3]byte{0, 15, 69}: "Stretch, Inc.", + [3]byte{0, 15, 70}: "SINAR AG", + [3]byte{0, 15, 71}: "ROBOX SPA", + [3]byte{0, 15, 72}: "Polypix Inc.", + [3]byte{0, 15, 73}: "Northover Solutions Limited", + [3]byte{0, 15, 74}: "Kyushu-kyohan co.,ltd", + [3]byte{0, 15, 75}: "Oracle Corporation", + [3]byte{0, 15, 76}: "Elextech INC", + [3]byte{0, 15, 77}: "TalkSwitch", + [3]byte{0, 15, 78}: "Cellink", + [3]byte{0, 15, 79}: "PCS Systemtechnik GmbH", + [3]byte{0, 15, 80}: "StreamScale Limited", + [3]byte{0, 15, 81}: "Azul Systems, Inc.", + [3]byte{0, 15, 82}: "YORK Refrigeration, Marine & Controls", + [3]byte{0, 15, 83}: "Solarflare Communications Inc", + [3]byte{0, 15, 84}: "Entrelogic Corporation", + [3]byte{0, 15, 85}: "Datawire Communication Networks Inc.", + [3]byte{0, 15, 86}: "Continuum Photonics Inc", + [3]byte{0, 15, 87}: "CABLELOGIC Co., Ltd.", + [3]byte{0, 15, 88}: "Adder Technology Limited", + [3]byte{0, 15, 89}: "Phonak AG", + [3]byte{0, 15, 90}: "Peribit Networks", + [3]byte{0, 15, 91}: "Delta Information Systems, Inc.", + [3]byte{0, 15, 92}: "Day One Digital Media Limited", + [3]byte{0, 15, 93}: "Genexis BV", + [3]byte{0, 15, 94}: "Veo", + [3]byte{0, 15, 95}: "Nicety Technologies Inc. (NTS)", + [3]byte{0, 15, 96}: "Lifetron Co.,Ltd", + [3]byte{0, 15, 97}: "Hewlett Packard", + [3]byte{0, 15, 98}: "Alcatel Bell Space N.V.", + [3]byte{0, 15, 99}: "Obzerv Technologies", + [3]byte{0, 15, 100}: "D&R Electronica Weesp BV", + [3]byte{0, 15, 101}: "icube Corp.", + [3]byte{0, 15, 102}: "Cisco-Linksys, LLC", + [3]byte{0, 15, 103}: "West Instruments", + [3]byte{0, 15, 104}: "Vavic Network Technology, Inc.", + [3]byte{0, 15, 105}: "SEW Eurodrive GmbH & Co. KG", + [3]byte{0, 15, 106}: "Nortel Networks", + [3]byte{0, 15, 107}: "GateWare Communications GmbH", + [3]byte{0, 15, 108}: "ADDI-DATA GmbH", + [3]byte{0, 15, 109}: "Midas Engineering", + [3]byte{0, 15, 110}: "BBox", + [3]byte{0, 15, 111}: "FTA Communication Technologies", + [3]byte{0, 15, 112}: "Wintec Industries, inc.", + [3]byte{0, 15, 113}: "Sanmei Electronics Co.,Ltd", + [3]byte{0, 15, 114}: "Sandburst", + [3]byte{0, 15, 115}: "RS Automation Co., Ltd", + [3]byte{0, 15, 116}: "Qamcom Technology AB", + [3]byte{0, 15, 117}: "First Silicon Solutions", + [3]byte{0, 15, 118}: "Digital Keystone, Inc.", + [3]byte{0, 15, 119}: "DENTUM CO.,LTD", + [3]byte{0, 15, 120}: "Datacap Systems Inc", + [3]byte{0, 15, 121}: "Bluetooth Interest Group Inc.", + [3]byte{0, 15, 122}: "BeiJing NuQX Technology CO.,LTD", + [3]byte{0, 15, 123}: "Arce Sistemas, S.A.", + [3]byte{0, 15, 124}: "ACTi Corporation", + [3]byte{0, 15, 125}: "Xirrus", + [3]byte{0, 15, 126}: "Ablerex Electronics Co., LTD", + [3]byte{0, 15, 127}: "UBSTORAGE Co.,Ltd.", + [3]byte{0, 15, 128}: "Trinity Security Systems,Inc.", + [3]byte{0, 15, 129}: "PAL Pacific Inc.", + [3]byte{0, 15, 130}: "Mortara Instrument, Inc.", + [3]byte{0, 15, 131}: "Brainium Technologies Inc.", + [3]byte{0, 15, 132}: "Astute Networks, Inc.", + [3]byte{0, 15, 133}: "ADDO-Japan Corporation", + [3]byte{0, 15, 134}: "BlackBerry RTS", + [3]byte{0, 15, 135}: "Maxcess International", + [3]byte{0, 15, 136}: "AMETEK, Inc.", + [3]byte{0, 15, 137}: "Winnertec System Co., Ltd.", + [3]byte{0, 15, 138}: "WideView", + [3]byte{0, 15, 139}: "Orion MultiSystems Inc", + [3]byte{0, 15, 140}: "Gigawavetech Pte Ltd", + [3]byte{0, 15, 141}: "FAST TV-Server AG", + [3]byte{0, 15, 142}: "DONGYANG TELECOM CO.,LTD.", + [3]byte{0, 15, 143}: "Cisco Systems, Inc", + [3]byte{0, 15, 144}: "Cisco Systems, Inc", + [3]byte{0, 15, 145}: "Aerotelecom Co.,Ltd.", + [3]byte{0, 15, 146}: "Microhard Systems Inc.", + [3]byte{0, 15, 147}: "Landis+Gyr Ltd.", + [3]byte{0, 15, 148}: "Genexis BV", + [3]byte{0, 15, 149}: "ELECOM Co.,LTD Laneed Division", + [3]byte{0, 15, 150}: "Telco Systems, Inc. ", + [3]byte{0, 15, 151}: "Avanex Corporation", + [3]byte{0, 15, 152}: "Avamax Co. Ltd.", + [3]byte{0, 15, 153}: "APAC opto Electronics Inc.", + [3]byte{0, 15, 154}: "Synchrony, Inc.", + [3]byte{0, 15, 155}: "Ross Video Limited", + [3]byte{0, 15, 156}: "Panduit Corp", + [3]byte{0, 15, 157}: "DisplayLink (UK) Ltd", + [3]byte{0, 15, 158}: "Murrelektronik GmbH", + [3]byte{0, 15, 159}: "ARRIS Group, Inc.", + [3]byte{0, 15, 160}: "CANON KOREA BUSINESS SOLUTIONS INC.", + [3]byte{0, 15, 161}: "Gigabit Systems Inc.", + [3]byte{0, 15, 162}: "2xWireless", + [3]byte{0, 15, 163}: "Alpha Networks Inc.", + [3]byte{0, 15, 164}: "Sprecher Automation GmbH", + [3]byte{0, 15, 165}: "BWA Technology GmbH", + [3]byte{0, 15, 166}: "S2 Security Corporation", + [3]byte{0, 15, 167}: "Raptor Networks Technology", + [3]byte{0, 15, 168}: "Photometrics, Inc.", + [3]byte{0, 15, 169}: "PC Fabrik", + [3]byte{0, 15, 170}: "Nexus Technologies", + [3]byte{0, 15, 171}: "Kyushu Electronics Systems Inc.", + [3]byte{0, 15, 172}: "IEEE 802.11", + [3]byte{0, 15, 173}: "FMN communications GmbH", + [3]byte{0, 15, 174}: "E2O Communications", + [3]byte{0, 15, 175}: "Dialog Inc.", + [3]byte{0, 15, 176}: "COMPAL ELECTRONICS, INC.", + [3]byte{0, 15, 177}: "Cognio Inc.", + [3]byte{0, 15, 178}: "Broadband Pacenet (India) Pvt. Ltd.", + [3]byte{0, 15, 179}: "Actiontec Electronics, Inc", + [3]byte{0, 15, 180}: "Timespace Technology", + [3]byte{0, 15, 181}: "NETGEAR", + [3]byte{0, 15, 182}: "Europlex Technologies", + [3]byte{0, 15, 183}: "Cavium", + [3]byte{0, 15, 184}: "CallURL Inc.", + [3]byte{0, 15, 185}: "Adaptive Instruments", + [3]byte{0, 15, 186}: "Tevebox AB", + [3]byte{0, 15, 187}: "Nokia Siemens Networks GmbH & Co. KG.", + [3]byte{0, 15, 188}: "Onkey Technologies, Inc.", + [3]byte{0, 15, 189}: "MRV Communications (Networks) LTD", + [3]byte{0, 15, 190}: "e-w/you Inc.", + [3]byte{0, 15, 191}: "DGT Sp. z o.o.", + [3]byte{0, 15, 192}: "DELCOMp", + [3]byte{0, 15, 193}: "WAVE Corporation", + [3]byte{0, 15, 194}: "Uniwell Corporation", + [3]byte{0, 15, 195}: "PalmPalm Technology, Inc.", + [3]byte{0, 15, 196}: "NST co.,LTD.", + [3]byte{0, 15, 197}: "KeyMed Ltd", + [3]byte{0, 15, 198}: "Eurocom Industries A/S", + [3]byte{0, 15, 199}: "Dionica R&D Ltd.", + [3]byte{0, 15, 200}: "Chantry Networks", + [3]byte{0, 15, 201}: "Allnet GmbH", + [3]byte{0, 15, 202}: "A-JIN TECHLINE CO, LTD", + [3]byte{0, 15, 203}: "3Com Ltd", + [3]byte{0, 15, 204}: "ARRIS Group, Inc.", + [3]byte{0, 15, 205}: "Nortel Networks", + [3]byte{0, 15, 206}: "Kikusui Electronics Corp.", + [3]byte{0, 15, 207}: "DataWind Research", + [3]byte{0, 15, 208}: "ASTRI", + [3]byte{0, 15, 209}: "Applied Wireless Identifications Group, Inc.", + [3]byte{0, 15, 210}: "EWA Technologies, Inc.", + [3]byte{0, 15, 211}: "Digium", + [3]byte{0, 15, 212}: "Soundcraft", + [3]byte{0, 15, 213}: "Schwechat - RISE", + [3]byte{0, 15, 214}: "Sarotech Co., Ltd", + [3]byte{0, 15, 215}: "Harman Music Group", + [3]byte{0, 15, 216}: "Force, Inc.", + [3]byte{0, 15, 217}: "FlexDSL Telecommunications AG", + [3]byte{0, 15, 218}: "YAZAKI CORPORATION", + [3]byte{0, 15, 219}: "Westell Technologies Inc.", + [3]byte{0, 15, 220}: "Ueda Japan Radio Co., Ltd.", + [3]byte{0, 15, 221}: "SORDIN AB", + [3]byte{0, 15, 222}: "Sony Mobile Communications AB", + [3]byte{0, 15, 223}: "SOLOMON Technology Corp.", + [3]byte{0, 15, 224}: "NComputing Co.,Ltd.", + [3]byte{0, 15, 225}: "ID DIGITAL CORPORATION", + [3]byte{0, 15, 226}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{0, 15, 227}: "Damm Cellular Systems A/S", + [3]byte{0, 15, 228}: "Pantech Co.,Ltd", + [3]byte{0, 15, 229}: "MERCURY SECURITY CORPORATION", + [3]byte{0, 15, 230}: "MBTech Systems, Inc.", + [3]byte{0, 15, 231}: "Lutron Electronics Co., Inc.", + [3]byte{0, 15, 232}: "Lobos, Inc.", + [3]byte{0, 15, 233}: "GW TECHNOLOGIES CO.,LTD.", + [3]byte{0, 15, 234}: "Giga-Byte Technology Co.,LTD.", + [3]byte{0, 15, 235}: "Cylon Controls", + [3]byte{0, 15, 236}: "ARKUS Inc.", + [3]byte{0, 15, 237}: "Anam Electronics Co., Ltd", + [3]byte{0, 15, 238}: "XTec, Incorporated", + [3]byte{0, 15, 239}: "Thales e-Transactions GmbH", + [3]byte{0, 15, 240}: "Sunray Co. Ltd.", + [3]byte{0, 15, 241}: "nex-G Systems Pte.Ltd", + [3]byte{0, 15, 242}: "Loud Technologies Inc.", + [3]byte{0, 15, 243}: "Jung Myoung Communications&Technology", + [3]byte{0, 15, 244}: "Guntermann & Drunck GmbH", + [3]byte{0, 15, 245}: "GN&S company", + [3]byte{0, 15, 246}: "DARFON LIGHTING CORP", + [3]byte{0, 15, 247}: "Cisco Systems, Inc", + [3]byte{0, 15, 248}: "Cisco Systems, Inc", + [3]byte{0, 15, 249}: "Valcretec, Inc.", + [3]byte{0, 15, 250}: "Optinel Systems, Inc.", + [3]byte{0, 15, 251}: "Nippon Denso Industry Co., Ltd.", + [3]byte{0, 15, 252}: "Merit Li-Lin Ent.", + [3]byte{0, 15, 253}: "Glorytek Network Inc.", + [3]byte{0, 15, 254}: "G-PRO COMPUTER", + [3]byte{0, 15, 255}: "Control4", + [3]byte{0, 16, 0}: "CABLE TELEVISION LABORATORIES, INC.", + [3]byte{0, 16, 1}: "Citel", + [3]byte{0, 16, 2}: "ACTIA", + [3]byte{0, 16, 3}: "IMATRON, INC.", + [3]byte{0, 16, 4}: "THE BRANTLEY COILE COMPANY,INC", + [3]byte{0, 16, 5}: "UEC COMMERCIAL", + [3]byte{0, 16, 6}: "Thales Contact Solutions Ltd.", + [3]byte{0, 16, 7}: "Cisco Systems, Inc", + [3]byte{0, 16, 8}: "VIENNA SYSTEMS CORPORATION", + [3]byte{0, 16, 9}: "HORO QUARTZ", + [3]byte{0, 16, 10}: "WILLIAMS COMMUNICATIONS GROUP", + [3]byte{0, 16, 11}: "Cisco Systems, Inc", + [3]byte{0, 16, 12}: "ITO CO., LTD.", + [3]byte{0, 16, 13}: "Cisco Systems, Inc", + [3]byte{0, 16, 14}: "MICRO LINEAR COPORATION", + [3]byte{0, 16, 15}: "INDUSTRIAL CPU SYSTEMS", + [3]byte{0, 16, 16}: "INITIO CORPORATION", + [3]byte{0, 16, 17}: "Cisco Systems, Inc", + [3]byte{0, 16, 18}: "PROCESSOR SYSTEMS (I) PVT LTD", + [3]byte{0, 16, 19}: "Kontron America, Inc.", + [3]byte{0, 16, 20}: "Cisco Systems, Inc", + [3]byte{0, 16, 21}: "OOmon Inc.", + [3]byte{0, 16, 22}: "T.SQWARE", + [3]byte{0, 16, 23}: "Bosch Access Systems GmbH", + [3]byte{0, 16, 24}: "Broadcom", + [3]byte{0, 16, 25}: "SIRONA DENTAL SYSTEMS GmbH & Co. KG", + [3]byte{0, 16, 26}: "PictureTel Corp.", + [3]byte{0, 16, 27}: "CORNET TECHNOLOGY, INC.", + [3]byte{0, 16, 28}: "OHM TECHNOLOGIES INTL, LLC", + [3]byte{0, 16, 29}: "WINBOND ELECTRONICS CORP.", + [3]byte{0, 16, 30}: "MATSUSHITA ELECTRONIC INSTRUMENTS CORP.", + [3]byte{0, 16, 31}: "Cisco Systems, Inc", + [3]byte{0, 16, 32}: "Hand Held Products Inc", + [3]byte{0, 16, 33}: "ENCANTO NETWORKS, INC.", + [3]byte{0, 16, 34}: "SatCom Media Corporation", + [3]byte{0, 16, 35}: "Network Equipment Technologies", + [3]byte{0, 16, 36}: "NAGOYA ELECTRIC WORKS CO., LTD", + [3]byte{0, 16, 37}: "Grayhill, Inc", + [3]byte{0, 16, 38}: "ACCELERATED NETWORKS, INC.", + [3]byte{0, 16, 39}: "L-3 COMMUNICATIONS EAST", + [3]byte{0, 16, 40}: "COMPUTER TECHNICA, INC.", + [3]byte{0, 16, 41}: "Cisco Systems, Inc", + [3]byte{0, 16, 42}: "ZF MICROSYSTEMS, INC.", + [3]byte{0, 16, 43}: "UMAX DATA SYSTEMS, INC.", + [3]byte{0, 16, 44}: "Lasat Networks A/S", + [3]byte{0, 16, 45}: "HITACHI SOFTWARE ENGINEERING", + [3]byte{0, 16, 46}: "NETWORK SYSTEMS & TECHNOLOGIES PVT. LTD.", + [3]byte{0, 16, 47}: "Cisco Systems, Inc", + [3]byte{0, 16, 48}: "EION Inc.", + [3]byte{0, 16, 49}: "OBJECTIVE COMMUNICATIONS, INC.", + [3]byte{0, 16, 50}: "ALTA TECHNOLOGY", + [3]byte{0, 16, 51}: "ACCESSLAN COMMUNICATIONS, INC.", + [3]byte{0, 16, 52}: "GNP Computers", + [3]byte{0, 16, 53}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 16, 54}: "INTER-TEL INTEGRATED SYSTEMS", + [3]byte{0, 16, 55}: "CYQ've Technology Co., Ltd.", + [3]byte{0, 16, 56}: "MICRO RESEARCH INSTITUTE, INC.", + [3]byte{0, 16, 57}: "Vectron Systems AG", + [3]byte{0, 16, 58}: "DIAMOND NETWORK TECH", + [3]byte{0, 16, 59}: "HIPPI NETWORKING FORUM", + [3]byte{0, 16, 60}: "IC ENSEMBLE, INC.", + [3]byte{0, 16, 61}: "PHASECOM, LTD.", + [3]byte{0, 16, 62}: "NETSCHOOLS CORPORATION", + [3]byte{0, 16, 63}: "TOLLGRADE COMMUNICATIONS, INC.", + [3]byte{0, 16, 64}: "INTERMEC CORPORATION", + [3]byte{0, 16, 65}: "BRISTOL BABCOCK, INC.", + [3]byte{0, 16, 66}: "Alacritech, Inc.", + [3]byte{0, 16, 67}: "A2 CORPORATION", + [3]byte{0, 16, 68}: "InnoLabs Corporation", + [3]byte{0, 16, 69}: "Nortel Networks", + [3]byte{0, 16, 70}: "ALCORN MCBRIDE INC.", + [3]byte{0, 16, 71}: "ECHO ELETRIC CO. LTD.", + [3]byte{0, 16, 72}: "HTRC AUTOMATION, INC.", + [3]byte{0, 16, 73}: "ShoreTel, Inc", + [3]byte{0, 16, 74}: "The Parvus Corporation", + [3]byte{0, 16, 75}: "3COM CORPORATION", + [3]byte{0, 16, 76}: "Teledyne LeCroy, Inc", + [3]byte{0, 16, 77}: "SURTEC INDUSTRIES, INC.", + [3]byte{0, 16, 78}: "CEOLOGIC", + [3]byte{0, 16, 79}: "Oracle Corporation ", + [3]byte{0, 16, 80}: "RION CO., LTD.", + [3]byte{0, 16, 81}: "CMICRO CORPORATION", + [3]byte{0, 16, 82}: "METTLER-TOLEDO (ALBSTADT) GMBH", + [3]byte{0, 16, 83}: "COMPUTER TECHNOLOGY CORP.", + [3]byte{0, 16, 84}: "Cisco Systems, Inc", + [3]byte{0, 16, 85}: "FUJITSU MICROELECTRONICS, INC.", + [3]byte{0, 16, 86}: "SODICK CO., LTD.", + [3]byte{0, 16, 87}: "Rebel.com, Inc.", + [3]byte{0, 16, 88}: "ArrowPoint Communications", + [3]byte{0, 16, 89}: "DIABLO RESEARCH CO. LLC", + [3]byte{0, 16, 90}: "3COM CORPORATION", + [3]byte{0, 16, 91}: "NET INSIGHT AB", + [3]byte{0, 16, 92}: "QUANTUM DESIGNS (H.K.) LTD.", + [3]byte{0, 16, 93}: "Draeger Medical", + [3]byte{0, 16, 94}: "Spirent plc, Service Assurance Broadband", + [3]byte{0, 16, 95}: "ZODIAC DATA SYSTEMS", + [3]byte{0, 16, 96}: "BILLIONTON SYSTEMS, INC.", + [3]byte{0, 16, 97}: "HOSTLINK CORP.", + [3]byte{0, 16, 98}: "NX SERVER, ILNC.", + [3]byte{0, 16, 99}: "STARGUIDE DIGITAL NETWORKS", + [3]byte{0, 16, 100}: "DNPG, LLC", + [3]byte{0, 16, 101}: "RADYNE CORPORATION", + [3]byte{0, 16, 102}: "ADVANCED CONTROL SYSTEMS, INC.", + [3]byte{0, 16, 103}: "Ericsson", + [3]byte{0, 16, 104}: "COMOS TELECOM", + [3]byte{0, 16, 105}: "HELIOSS COMMUNICATIONS, INC.", + [3]byte{0, 16, 106}: "DIGITAL MICROWAVE CORPORATION", + [3]byte{0, 16, 107}: "SONUS NETWORKS, INC.", + [3]byte{0, 16, 108}: "EDNT GmbH", + [3]byte{0, 16, 109}: "Axxcelera Broadband Wireless", + [3]byte{0, 16, 110}: "TADIRAN COM. LTD.", + [3]byte{0, 16, 111}: "TRENTON TECHNOLOGY INC.", + [3]byte{0, 16, 112}: "CARADON TREND LTD.", + [3]byte{0, 16, 113}: "ADVANET INC.", + [3]byte{0, 16, 114}: "GVN TECHNOLOGIES, INC.", + [3]byte{0, 16, 115}: "TECHNOBOX, INC.", + [3]byte{0, 16, 116}: "ATEN INTERNATIONAL CO., LTD.", + [3]byte{0, 16, 117}: "Segate Technology LLC", + [3]byte{0, 16, 118}: "EUREM GmbH", + [3]byte{0, 16, 119}: "SAF DRIVE SYSTEMS, LTD.", + [3]byte{0, 16, 120}: "NUERA COMMUNICATIONS, INC.", + [3]byte{0, 16, 121}: "Cisco Systems, Inc", + [3]byte{0, 16, 122}: "AmbiCom, Inc.", + [3]byte{0, 16, 123}: "Cisco Systems, Inc", + [3]byte{0, 16, 124}: "P-COM, INC.", + [3]byte{0, 16, 125}: "AURORA COMMUNICATIONS, LTD.", + [3]byte{0, 16, 126}: "BACHMANN ELECTRONIC GmbH", + [3]byte{0, 16, 127}: "CRESTRON ELECTRONICS, INC.", + [3]byte{0, 16, 128}: "METAWAVE COMMUNICATIONS", + [3]byte{0, 16, 129}: "DPS, INC.", + [3]byte{0, 16, 130}: "JNA TELECOMMUNICATIONS LIMITED", + [3]byte{0, 16, 131}: "Hewlett Packard", + [3]byte{0, 16, 132}: "K-BOT COMMUNICATIONS", + [3]byte{0, 16, 133}: "POLARIS COMMUNICATIONS, INC.", + [3]byte{0, 16, 134}: "ATTO Technology, Inc.", + [3]byte{0, 16, 135}: "XSTREAMIS PLC", + [3]byte{0, 16, 136}: "AMERICAN NETWORKS INC.", + [3]byte{0, 16, 137}: "WebSonic", + [3]byte{0, 16, 138}: "TeraLogic, Inc.", + [3]byte{0, 16, 139}: "LASERANIMATION SOLLINGER GMBH", + [3]byte{0, 16, 140}: "Fujitsu Services Ltd", + [3]byte{0, 16, 141}: "Johnson Controls, Inc.", + [3]byte{0, 16, 142}: "HUGH SYMONS CONCEPT Technologies Ltd.", + [3]byte{0, 16, 143}: "RAPTOR SYSTEMS", + [3]byte{0, 16, 144}: "CIMETRICS, INC.", + [3]byte{0, 16, 145}: "NO WIRES NEEDED BV", + [3]byte{0, 16, 146}: "NETCORE INC.", + [3]byte{0, 16, 147}: "CMS COMPUTERS, LTD.", + [3]byte{0, 16, 148}: "Performance Analysis Broadband, Spirent plc", + [3]byte{0, 16, 149}: "Thomson Inc.", + [3]byte{0, 16, 150}: "TRACEWELL SYSTEMS, INC.", + [3]byte{0, 16, 151}: "WinNet Metropolitan Communications Systems, Inc.", + [3]byte{0, 16, 152}: "STARNET TECHNOLOGIES, INC.", + [3]byte{0, 16, 153}: "InnoMedia, Inc.", + [3]byte{0, 16, 154}: "NETLINE", + [3]byte{0, 16, 155}: "Emulex Corporation", + [3]byte{0, 16, 156}: "M-SYSTEM CO., LTD.", + [3]byte{0, 16, 157}: "CLARINET SYSTEMS, INC.", + [3]byte{0, 16, 158}: "AWARE, INC.", + [3]byte{0, 16, 159}: "PAVO, INC.", + [3]byte{0, 16, 160}: "INNOVEX TECHNOLOGIES, INC.", + [3]byte{0, 16, 161}: "KENDIN SEMICONDUCTOR, INC.", + [3]byte{0, 16, 162}: "TNS", + [3]byte{0, 16, 163}: "OMNITRONIX, INC.", + [3]byte{0, 16, 164}: "XIRCOM", + [3]byte{0, 16, 165}: "OXFORD INSTRUMENTS", + [3]byte{0, 16, 166}: "Cisco Systems, Inc", + [3]byte{0, 16, 167}: "UNEX TECHNOLOGY CORPORATION", + [3]byte{0, 16, 168}: "RELIANCE COMPUTER CORP.", + [3]byte{0, 16, 169}: "ADHOC TECHNOLOGIES", + [3]byte{0, 16, 170}: "MEDIA4, INC.", + [3]byte{0, 16, 171}: "KOITO ELECTRIC INDUSTRIES, LTD.", + [3]byte{0, 16, 172}: "IMCI TECHNOLOGIES", + [3]byte{0, 16, 173}: "SOFTRONICS USB, INC.", + [3]byte{0, 16, 174}: "SHINKO ELECTRIC INDUSTRIES CO.", + [3]byte{0, 16, 175}: "TAC SYSTEMS, INC.", + [3]byte{0, 16, 176}: "MERIDIAN TECHNOLOGY CORP.", + [3]byte{0, 16, 177}: "FOR-A CO., LTD.", + [3]byte{0, 16, 178}: "COACTIVE AESTHETICS", + [3]byte{0, 16, 179}: "NOKIA MULTIMEDIA TERMINALS", + [3]byte{0, 16, 180}: "ATMOSPHERE NETWORKS", + [3]byte{0, 16, 181}: "Accton Technology Corp", + [3]byte{0, 16, 182}: "ENTRATA COMMUNICATIONS CORP.", + [3]byte{0, 16, 183}: "COYOTE TECHNOLOGIES, LLC", + [3]byte{0, 16, 184}: "ISHIGAKI COMPUTER SYSTEM CO.", + [3]byte{0, 16, 185}: "MAXTOR CORP.", + [3]byte{0, 16, 186}: "MARTINHO-DAVIS SYSTEMS, INC.", + [3]byte{0, 16, 187}: "DATA & INFORMATION TECHNOLOGY", + [3]byte{0, 16, 188}: "Aastra Telecom", + [3]byte{0, 16, 189}: "THE TELECOMMUNICATION TECHNOLOGY COMMITTEE (TTC)", + [3]byte{0, 16, 190}: "MARCH NETWORKS CORPORATION", + [3]byte{0, 16, 191}: "InterAir Wireless", + [3]byte{0, 16, 192}: "ARMA, Inc.", + [3]byte{0, 16, 193}: "OI ELECTRIC CO.,LTD", + [3]byte{0, 16, 194}: "WILLNET, INC.", + [3]byte{0, 16, 195}: "CSI-CONTROL SYSTEMS", + [3]byte{0, 16, 196}: "MEDIA GLOBAL LINKS CO., LTD.", + [3]byte{0, 16, 197}: "PROTOCOL TECHNOLOGIES, INC.", + [3]byte{0, 16, 198}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{0, 16, 199}: "DATA TRANSMISSION NETWORK", + [3]byte{0, 16, 200}: "COMMUNICATIONS ELECTRONICS SECURITY GROUP", + [3]byte{0, 16, 201}: "MITSUBISHI ELECTRONICS LOGISTIC SUPPORT CO.", + [3]byte{0, 16, 202}: "Telco Systems, Inc. ", + [3]byte{0, 16, 203}: "FACIT K.K.", + [3]byte{0, 16, 204}: "CLP COMPUTER LOGISTIK PLANUNG GmbH", + [3]byte{0, 16, 205}: "INTERFACE CONCEPT", + [3]byte{0, 16, 206}: "VOLAMP, LTD.", + [3]byte{0, 16, 207}: "FIBERLANE COMMUNICATIONS", + [3]byte{0, 16, 208}: "WITCOM, LTD.", + [3]byte{0, 16, 209}: "Top Layer Networks, Inc.", + [3]byte{0, 16, 210}: "NITTO TSUSHINKI CO., LTD", + [3]byte{0, 16, 211}: "GRIPS ELECTRONIC GMBH", + [3]byte{0, 16, 212}: "STORAGE COMPUTER CORPORATION", + [3]byte{0, 16, 213}: "IMASDE CANARIAS, S.A.", + [3]byte{0, 16, 214}: "Exelis", + [3]byte{0, 16, 215}: "ARGOSY RESEARCH INC.", + [3]byte{0, 16, 216}: "CALISTA", + [3]byte{0, 16, 217}: "IBM JAPAN, FUJISAWA MT+D", + [3]byte{0, 16, 218}: "Kollmorgen Corp", + [3]byte{0, 16, 219}: "Juniper Networks", + [3]byte{0, 16, 220}: "MICRO-STAR INTERNATIONAL CO., LTD.", + [3]byte{0, 16, 221}: "ENABLE SEMICONDUCTOR, INC.", + [3]byte{0, 16, 222}: "INTERNATIONAL DATACASTING CORPORATION", + [3]byte{0, 16, 223}: "RISE COMPUTER INC.", + [3]byte{0, 16, 224}: "Oracle Corporation ", + [3]byte{0, 16, 225}: "S.I. TECH, INC.", + [3]byte{0, 16, 226}: "ArrayComm, Inc.", + [3]byte{0, 16, 227}: "Hewlett Packard", + [3]byte{0, 16, 228}: "NSI CORPORATION", + [3]byte{0, 16, 229}: "SOLECTRON TEXAS", + [3]byte{0, 16, 230}: "APPLIED INTELLIGENT SYSTEMS, INC.", + [3]byte{0, 16, 231}: "Breezecom, Ltd.", + [3]byte{0, 16, 232}: "TELOCITY, INCORPORATED", + [3]byte{0, 16, 233}: "RAIDTEC LTD.", + [3]byte{0, 16, 234}: "ADEPT TECHNOLOGY", + [3]byte{0, 16, 235}: "SELSIUS SYSTEMS, INC.", + [3]byte{0, 16, 236}: "RPCG, LLC", + [3]byte{0, 16, 237}: "SUNDANCE TECHNOLOGY, INC.", + [3]byte{0, 16, 238}: "CTI PRODUCTS, INC.", + [3]byte{0, 16, 239}: "DBTEL INCORPORATED", + [3]byte{0, 16, 240}: "RITTAL-WERK RUDOLF LOH GmbH & Co.", + [3]byte{0, 16, 241}: "I-O CORPORATION", + [3]byte{0, 16, 242}: "ANTEC", + [3]byte{0, 16, 243}: "Nexcom International Co., Ltd.", + [3]byte{0, 16, 244}: "Vertical Communications", + [3]byte{0, 16, 245}: "AMHERST SYSTEMS, INC.", + [3]byte{0, 16, 246}: "Cisco Systems, Inc", + [3]byte{0, 16, 247}: "IRIICHI TECHNOLOGIES Inc.", + [3]byte{0, 16, 248}: "TEXIO TECHNOLOGY CORPORATION", + [3]byte{0, 16, 249}: "UNIQUE SYSTEMS, INC.", + [3]byte{0, 16, 250}: "Apple, Inc.", + [3]byte{0, 16, 251}: "ZIDA TECHNOLOGIES LIMITED", + [3]byte{0, 16, 252}: "BROADBAND NETWORKS, INC.", + [3]byte{0, 16, 253}: "COCOM A/S", + [3]byte{0, 16, 254}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{0, 16, 255}: "Cisco Systems, Inc", + [3]byte{0, 17, 0}: "Schneider Electric", + [3]byte{0, 17, 1}: "CET Technologies Pte Ltd", + [3]byte{0, 17, 2}: "Aurora Multimedia Corp.", + [3]byte{0, 17, 3}: "kawamura electric inc.", + [3]byte{0, 17, 4}: "TELEXY", + [3]byte{0, 17, 5}: "Sunplus Technology Co., Ltd.", + [3]byte{0, 17, 6}: "Siemens NV (Belgium)", + [3]byte{0, 17, 7}: "RGB Networks Inc.", + [3]byte{0, 17, 8}: "Orbital Data Corporation", + [3]byte{0, 17, 9}: "Micro-Star International", + [3]byte{0, 17, 10}: "Hewlett Packard", + [3]byte{0, 17, 11}: "Franklin Technology Systems", + [3]byte{0, 17, 12}: "Atmark Techno, Inc.", + [3]byte{0, 17, 13}: "SANBlaze Technology, Inc.", + [3]byte{0, 17, 14}: "Tsurusaki Sealand Transportation Co. Ltd.", + [3]byte{0, 17, 15}: "netplat,Inc.", + [3]byte{0, 17, 16}: "Maxanna Technology Co., Ltd.", + [3]byte{0, 17, 17}: "Intel Corporation", + [3]byte{0, 17, 18}: "Honeywell CMSS", + [3]byte{0, 17, 19}: "Fraunhofer FOKUS", + [3]byte{0, 17, 20}: "EverFocus Electronics Corp.", + [3]byte{0, 17, 21}: "EPIN Technologies, Inc.", + [3]byte{0, 17, 22}: "COTEAU VERT CO., LTD.", + [3]byte{0, 17, 23}: "CESNET", + [3]byte{0, 17, 24}: "BLX IC Design Corp., Ltd.", + [3]byte{0, 17, 25}: "Solteras, Inc.", + [3]byte{0, 17, 26}: "ARRIS Group, Inc.", + [3]byte{0, 17, 27}: "Targa Systems Div L-3 Communications", + [3]byte{0, 17, 28}: "Pleora Technologies Inc.", + [3]byte{0, 17, 29}: "Hectrix Limited", + [3]byte{0, 17, 30}: "EPSG (Ethernet Powerlink Standardization Group)", + [3]byte{0, 17, 31}: "Doremi Labs, Inc.", + [3]byte{0, 17, 32}: "Cisco Systems, Inc", + [3]byte{0, 17, 33}: "Cisco Systems, Inc", + [3]byte{0, 17, 34}: "CIMSYS Inc", + [3]byte{0, 17, 35}: "Appointech, Inc.", + [3]byte{0, 17, 36}: "Apple, Inc.", + [3]byte{0, 17, 37}: "IBM Corp", + [3]byte{0, 17, 38}: "Venstar Inc.", + [3]byte{0, 17, 39}: "TASI, Inc", + [3]byte{0, 17, 40}: "Streamit", + [3]byte{0, 17, 41}: "Paradise Datacom Ltd.", + [3]byte{0, 17, 42}: "Niko NV", + [3]byte{0, 17, 43}: "NetModule AG", + [3]byte{0, 17, 44}: "IZT GmbH", + [3]byte{0, 17, 45}: "iPulse Systems", + [3]byte{0, 17, 46}: "CEICOM", + [3]byte{0, 17, 47}: "ASUSTek COMPUTER INC.", + [3]byte{0, 17, 48}: "Allied Telesis (Hong Kong) Ltd.", + [3]byte{0, 17, 49}: "UNATECH. CO.,LTD", + [3]byte{0, 17, 50}: "Synology Incorporated", + [3]byte{0, 17, 51}: "Siemens Austria SIMEA", + [3]byte{0, 17, 52}: "MediaCell, Inc.", + [3]byte{0, 17, 53}: "Grandeye Ltd", + [3]byte{0, 17, 54}: "Goodrich Sensor Systems", + [3]byte{0, 17, 55}: "AICHI ELECTRIC CO., LTD.", + [3]byte{0, 17, 56}: "TAISHIN CO., LTD.", + [3]byte{0, 17, 57}: "STOEBER ANTRIEBSTECHNIK GmbH + Co. KG.", + [3]byte{0, 17, 58}: "SHINBORAM", + [3]byte{0, 17, 59}: "Micronet Communications Inc.", + [3]byte{0, 17, 60}: "Micronas GmbH", + [3]byte{0, 17, 61}: "KN SOLTEC CO.,LTD.", + [3]byte{0, 17, 62}: "JL Corporation", + [3]byte{0, 17, 63}: "Alcatel DI", + [3]byte{0, 17, 64}: "Nanometrics Inc.", + [3]byte{0, 17, 65}: "GoodMan Corporation", + [3]byte{0, 17, 66}: "e-SMARTCOM INC.", + [3]byte{0, 17, 67}: "Dell Inc.", + [3]byte{0, 17, 68}: "Assurance Technology Corp", + [3]byte{0, 17, 69}: "ValuePoint Networks", + [3]byte{0, 17, 70}: "Telecard-Pribor Ltd", + [3]byte{0, 17, 71}: "Secom-Industry co.LTD.", + [3]byte{0, 17, 72}: "Prolon Control Systems", + [3]byte{0, 17, 73}: "Proliphix Inc.", + [3]byte{0, 17, 74}: "KAYABA INDUSTRY Co,.Ltd.", + [3]byte{0, 17, 75}: "Francotyp-Postalia GmbH", + [3]byte{0, 17, 76}: "caffeina applied research ltd.", + [3]byte{0, 17, 77}: "Atsumi Electric Co.,LTD.", + [3]byte{0, 17, 78}: "690885 Ontario Inc.", + [3]byte{0, 17, 79}: "US Digital Television, Inc", + [3]byte{0, 17, 80}: "Belkin Corporation", + [3]byte{0, 17, 81}: "Mykotronx", + [3]byte{0, 17, 82}: "Eidsvoll Electronics AS", + [3]byte{0, 17, 83}: "Trident Tek, Inc.", + [3]byte{0, 17, 84}: "Webpro Technologies Inc.", + [3]byte{0, 17, 85}: "Sevis Systems", + [3]byte{0, 17, 86}: "Pharos Systems NZ", + [3]byte{0, 17, 87}: "Oki Electric Industry Co., Ltd.", + [3]byte{0, 17, 88}: "Nortel Networks", + [3]byte{0, 17, 89}: "MATISSE NETWORKS INC", + [3]byte{0, 17, 90}: "Ivoclar Vivadent AG", + [3]byte{0, 17, 91}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 17, 92}: "Cisco Systems, Inc", + [3]byte{0, 17, 93}: "Cisco Systems, Inc", + [3]byte{0, 17, 94}: "ProMinent Dosiertechnik GmbH", + [3]byte{0, 17, 95}: "ITX Security Co., Ltd.", + [3]byte{0, 17, 96}: "ARTDIO Company Co., LTD", + [3]byte{0, 17, 97}: "NetStreams, LLC", + [3]byte{0, 17, 98}: "STAR MICRONICS CO.,LTD.", + [3]byte{0, 17, 99}: "SYSTEM SPA DEPT. ELECTRONICS", + [3]byte{0, 17, 100}: "ACARD Technology Corp.", + [3]byte{0, 17, 101}: "ZNYX Networks, Inc.", + [3]byte{0, 17, 102}: "Taelim Electronics Co., Ltd.", + [3]byte{0, 17, 103}: "Integrated System Solution Corp.", + [3]byte{0, 17, 104}: "HomeLogic LLC", + [3]byte{0, 17, 105}: "EMS Satcom", + [3]byte{0, 17, 106}: "Domo Ltd", + [3]byte{0, 17, 107}: "Digital Data Communications Asia Co.,Ltd", + [3]byte{0, 17, 108}: "Nanwang Multimedia Inc.,Ltd", + [3]byte{0, 17, 109}: "American Time and Signal", + [3]byte{0, 17, 110}: "Peplink International Ltd.", + [3]byte{0, 17, 111}: "Netforyou Co., LTD.", + [3]byte{0, 17, 112}: "GSC SRL", + [3]byte{0, 17, 113}: "DEXTER Communications, Inc.", + [3]byte{0, 17, 114}: "COTRON CORPORATION", + [3]byte{0, 17, 115}: "SMART Storage Systems", + [3]byte{0, 17, 116}: "Mojo Networks, Inc.", + [3]byte{0, 17, 117}: "Intel Corporation", + [3]byte{0, 17, 118}: "Intellambda Systems, Inc.", + [3]byte{0, 17, 119}: "Coaxial Networks, Inc.", + [3]byte{0, 17, 120}: "Chiron Technology Ltd", + [3]byte{0, 17, 121}: "Singular Technology Co. Ltd.", + [3]byte{0, 17, 122}: "Singim International Corp.", + [3]byte{0, 17, 123}: "Büchi Labortechnik AG", + [3]byte{0, 17, 124}: "e-zy.net", + [3]byte{0, 17, 125}: "ZMD America, Inc.", + [3]byte{0, 17, 126}: "Midmark Corp", + [3]byte{0, 17, 127}: "Neotune Information Technology Corporation,.LTD", + [3]byte{0, 17, 128}: "ARRIS Group, Inc.", + [3]byte{0, 17, 129}: "InterEnergy Co.Ltd,", + [3]byte{0, 17, 130}: "IMI Norgren Ltd", + [3]byte{0, 17, 131}: "Datalogic ADC, Inc.", + [3]byte{0, 17, 132}: "Humo Laboratory,Ltd.", + [3]byte{0, 17, 133}: "Hewlett Packard", + [3]byte{0, 17, 134}: "Prime Systems, Inc.", + [3]byte{0, 17, 135}: "Category Solutions, Inc", + [3]byte{0, 17, 136}: "Enterasys", + [3]byte{0, 17, 137}: "Aerotech Inc", + [3]byte{0, 17, 138}: "Viewtran Technology Limited", + [3]byte{0, 17, 139}: "Alcatel-Lucent Enterprise", + [3]byte{0, 17, 140}: "Missouri Department of Transportation", + [3]byte{0, 17, 141}: "Hanchang System Corp.", + [3]byte{0, 17, 142}: "Halytech Mace", + [3]byte{0, 17, 143}: "EUTECH INSTRUMENTS PTE. LTD.", + [3]byte{0, 17, 144}: "Digital Design Corporation", + [3]byte{0, 17, 145}: "CTS-Clima Temperatur Systeme GmbH", + [3]byte{0, 17, 146}: "Cisco Systems, Inc", + [3]byte{0, 17, 147}: "Cisco Systems, Inc", + [3]byte{0, 17, 148}: "Chi Mei Communication Systems, Inc.", + [3]byte{0, 17, 149}: "D-Link Corporation", + [3]byte{0, 17, 150}: "Actuality Systems, Inc.", + [3]byte{0, 17, 151}: "Monitoring Technologies Limited", + [3]byte{0, 17, 152}: "Prism Media Products Limited", + [3]byte{0, 17, 153}: "2wcom Systems GmbH", + [3]byte{0, 17, 154}: "Alkeria srl", + [3]byte{0, 17, 155}: "Telesynergy Research Inc.", + [3]byte{0, 17, 156}: "EP&T Energy", + [3]byte{0, 17, 157}: "Diginfo Technology Corporation", + [3]byte{0, 17, 158}: "Solectron Brazil", + [3]byte{0, 17, 159}: "Nokia Danmark A/S", + [3]byte{0, 17, 160}: "Vtech Engineering Canada Ltd", + [3]byte{0, 17, 161}: "VISION NETWARE CO.,LTD", + [3]byte{0, 17, 162}: "Manufacturing Technology Inc", + [3]byte{0, 17, 163}: "LanReady Technologies Inc.", + [3]byte{0, 17, 164}: "JStream Technologies Inc.", + [3]byte{0, 17, 165}: "Fortuna Electronic Corp.", + [3]byte{0, 17, 166}: "Sypixx Networks", + [3]byte{0, 17, 167}: "Infilco Degremont Inc.", + [3]byte{0, 17, 168}: "Quest Technologies", + [3]byte{0, 17, 169}: "MOIMSTONE Co., LTD", + [3]byte{0, 17, 170}: "Uniclass Technology, Co., LTD", + [3]byte{0, 17, 171}: "TRUSTABLE TECHNOLOGY CO.,LTD.", + [3]byte{0, 17, 172}: "Simtec Electronics", + [3]byte{0, 17, 173}: "Shanghai Ruijie Technology", + [3]byte{0, 17, 174}: "ARRIS Group, Inc.", + [3]byte{0, 17, 175}: "Medialink-i,Inc", + [3]byte{0, 17, 176}: "Fortelink Inc.", + [3]byte{0, 17, 177}: "BlueExpert Technology Corp.", + [3]byte{0, 17, 178}: "2001 Technology Inc.", + [3]byte{0, 17, 179}: "YOSHIMIYA CO.,LTD.", + [3]byte{0, 17, 180}: "Westermo Teleindustri AB", + [3]byte{0, 17, 181}: "Shenzhen Powercom Co.,Ltd", + [3]byte{0, 17, 182}: "Open Systems International", + [3]byte{0, 17, 183}: "Octalix B.V.", + [3]byte{0, 17, 184}: "Liebherr - Elektronik GmbH", + [3]byte{0, 17, 185}: "Inner Range Pty. Ltd.", + [3]byte{0, 17, 186}: "Elexol Pty Ltd", + [3]byte{0, 17, 187}: "Cisco Systems, Inc", + [3]byte{0, 17, 188}: "Cisco Systems, Inc", + [3]byte{0, 17, 189}: "Bombardier Transportation", + [3]byte{0, 17, 190}: "AGP Telecom Co. Ltd", + [3]byte{0, 17, 191}: "AESYS S.p.A.", + [3]byte{0, 17, 192}: "Aday Technology Inc", + [3]byte{0, 17, 193}: "4P MOBILE DATA PROCESSING", + [3]byte{0, 17, 194}: "United Fiber Optic Communication", + [3]byte{0, 17, 195}: "Transceiving System Technology Corporation", + [3]byte{0, 17, 196}: "Terminales de Telecomunicacion Terrestre, S.L.", + [3]byte{0, 17, 197}: "TEN Technology", + [3]byte{0, 17, 198}: "Seagate Technology", + [3]byte{0, 17, 199}: "Raymarine UK Ltd", + [3]byte{0, 17, 200}: "Powercom Co., Ltd.", + [3]byte{0, 17, 201}: "MTT Corporation", + [3]byte{0, 17, 202}: "Long Range Systems, Inc.", + [3]byte{0, 17, 203}: "Jacobsons AB", + [3]byte{0, 17, 204}: "Guangzhou Jinpeng Group Co.,Ltd.", + [3]byte{0, 17, 205}: "Axsun Technologies", + [3]byte{0, 17, 206}: "Ubisense Limited", + [3]byte{0, 17, 207}: "Thrane & Thrane A/S", + [3]byte{0, 17, 208}: "Tandberg Data ASA", + [3]byte{0, 17, 209}: "Soft Imaging System GmbH", + [3]byte{0, 17, 210}: "Perception Digital Ltd", + [3]byte{0, 17, 211}: "NextGenTel Holding ASA", + [3]byte{0, 17, 212}: "NetEnrich, Inc", + [3]byte{0, 17, 213}: "Hangzhou Sunyard System Engineering Co.,Ltd.", + [3]byte{0, 17, 214}: "HandEra, Inc.", + [3]byte{0, 17, 215}: "eWerks Inc", + [3]byte{0, 17, 216}: "ASUSTek COMPUTER INC.", + [3]byte{0, 17, 217}: "TiVo", + [3]byte{0, 17, 218}: "Vivaas Technology Inc.", + [3]byte{0, 17, 219}: "Land-Cellular Corporation", + [3]byte{0, 17, 220}: "Glunz & Jensen", + [3]byte{0, 17, 221}: "FROMUS TEC. Co., Ltd.", + [3]byte{0, 17, 222}: "EURILOGIC", + [3]byte{0, 17, 223}: "Current Energy", + [3]byte{0, 17, 224}: "U-MEDIA Communications, Inc.", + [3]byte{0, 17, 225}: "Arcelik A.S", + [3]byte{0, 17, 226}: "Hua Jung Components Co., Ltd.", + [3]byte{0, 17, 227}: "Thomson, Inc.", + [3]byte{0, 17, 228}: "Danelec Electronics A/S", + [3]byte{0, 17, 229}: "KCodes Corporation", + [3]byte{0, 17, 230}: "Scientific Atlanta", + [3]byte{0, 17, 231}: "WORLDSAT - Texas de France", + [3]byte{0, 17, 232}: "Tixi.Com", + [3]byte{0, 17, 233}: "STARNEX CO., LTD.", + [3]byte{0, 17, 234}: "IWICS Inc.", + [3]byte{0, 17, 235}: "Innovative Integration", + [3]byte{0, 17, 236}: "AVIX INC.", + [3]byte{0, 17, 237}: "802 Global", + [3]byte{0, 17, 238}: "Estari, Inc.", + [3]byte{0, 17, 239}: "Conitec Datensysteme GmbH", + [3]byte{0, 17, 240}: "Wideful Limited", + [3]byte{0, 17, 241}: "QinetiQ Ltd", + [3]byte{0, 17, 242}: "Institute of Network Technologies", + [3]byte{0, 17, 243}: "NeoMedia Europe AG", + [3]byte{0, 17, 244}: "woori-net", + [3]byte{0, 17, 245}: "ASKEY COMPUTER CORP", + [3]byte{0, 17, 246}: "Asia Pacific Microsystems , Inc.", + [3]byte{0, 17, 247}: "Shenzhen Forward Industry Co., Ltd", + [3]byte{0, 17, 248}: "AIRAYA Corp", + [3]byte{0, 17, 249}: "Nortel Networks", + [3]byte{0, 17, 250}: "Rane Corporation", + [3]byte{0, 17, 251}: "Heidelberg Engineering GmbH", + [3]byte{0, 17, 252}: "HARTING Electronics GmbH", + [3]byte{0, 17, 253}: "KORG INC.", + [3]byte{0, 17, 254}: "Keiyo System Research, Inc.", + [3]byte{0, 17, 255}: "Digitro Tecnologia Ltda", + [3]byte{0, 18, 0}: "Cisco Systems, Inc", + [3]byte{0, 18, 1}: "Cisco Systems, Inc", + [3]byte{0, 18, 2}: "Decrane Aerospace - Audio International Inc.", + [3]byte{0, 18, 3}: "ActivNetworks", + [3]byte{0, 18, 4}: "u10 Networks, Inc.", + [3]byte{0, 18, 5}: "Terrasat Communications, Inc.", + [3]byte{0, 18, 6}: "iQuest (NZ) Ltd", + [3]byte{0, 18, 7}: "Head Strong International Limited", + [3]byte{0, 18, 8}: "Gantner Instruments GmbH", + [3]byte{0, 18, 9}: "Fastrax Ltd", + [3]byte{0, 18, 10}: "Emerson Climate Technologies GmbH", + [3]byte{0, 18, 11}: "Chinasys Technologies Limited", + [3]byte{0, 18, 12}: "CE-Infosys Pte Ltd", + [3]byte{0, 18, 13}: "Advanced Telecommunication Technologies, Inc.", + [3]byte{0, 18, 14}: "AboCom", + [3]byte{0, 18, 15}: "IEEE 802.3", + [3]byte{0, 18, 16}: "WideRay Corp", + [3]byte{0, 18, 17}: "Protechna Herbst GmbH & Co. KG", + [3]byte{0, 18, 18}: "PLUS Corporation", + [3]byte{0, 18, 19}: "Metrohm AG", + [3]byte{0, 18, 20}: "Koenig & Bauer AG", + [3]byte{0, 18, 21}: "iStor Networks, Inc.", + [3]byte{0, 18, 22}: "ICP Internet Communication Payment AG", + [3]byte{0, 18, 23}: "Cisco-Linksys, LLC", + [3]byte{0, 18, 24}: "ARUZE Corporation", + [3]byte{0, 18, 25}: "Ahead Communication Systems Inc", + [3]byte{0, 18, 26}: "Techno Soft Systemnics Inc.", + [3]byte{0, 18, 27}: "Sound Devices, LLC", + [3]byte{0, 18, 28}: "PARROT SA", + [3]byte{0, 18, 29}: "Netfabric Corporation", + [3]byte{0, 18, 30}: "Juniper Networks", + [3]byte{0, 18, 31}: "Harding Instruments", + [3]byte{0, 18, 32}: "Cadco Systems", + [3]byte{0, 18, 33}: "B.Braun Melsungen AG", + [3]byte{0, 18, 34}: "Skardin (UK) Ltd", + [3]byte{0, 18, 35}: "Pixim", + [3]byte{0, 18, 36}: "NexQL Corporation", + [3]byte{0, 18, 37}: "ARRIS Group, Inc.", + [3]byte{0, 18, 38}: "Japan Direx Corporation", + [3]byte{0, 18, 39}: "Franklin Electric Co., Inc.", + [3]byte{0, 18, 40}: "Data Ltd.", + [3]byte{0, 18, 41}: "BroadEasy Technologies Co.,Ltd", + [3]byte{0, 18, 42}: "VTech Telecommunications Ltd.", + [3]byte{0, 18, 43}: "Virbiage Pty Ltd", + [3]byte{0, 18, 44}: "Soenen Controls N.V.", + [3]byte{0, 18, 45}: "SiNett Corporation", + [3]byte{0, 18, 46}: "Signal Technology - AISD", + [3]byte{0, 18, 47}: "Sanei Electric Inc.", + [3]byte{0, 18, 48}: "Picaso Infocommunication CO., LTD.", + [3]byte{0, 18, 49}: "Motion Control Systems, Inc.", + [3]byte{0, 18, 50}: "LeWiz Communications Inc.", + [3]byte{0, 18, 51}: "JRC TOKKI Co.,Ltd.", + [3]byte{0, 18, 52}: "Camille Bauer", + [3]byte{0, 18, 53}: "Andrew Corporation", + [3]byte{0, 18, 54}: "ConSentry Networks", + [3]byte{0, 18, 55}: "Texas Instruments", + [3]byte{0, 18, 56}: "SetaBox Technology Co., Ltd.", + [3]byte{0, 18, 57}: "S Net Systems Inc.", + [3]byte{0, 18, 58}: "Posystech Inc., Co.", + [3]byte{0, 18, 59}: "KeRo Systems ApS", + [3]byte{0, 18, 60}: "Second Rule LLC", + [3]byte{0, 18, 61}: "GES Co, Ltd", + [3]byte{0, 18, 62}: "ERUNE technology Co., Ltd.", + [3]byte{0, 18, 63}: "Dell Inc.", + [3]byte{0, 18, 64}: "AMOI ELECTRONICS CO.,LTD", + [3]byte{0, 18, 65}: "a2i marketing center", + [3]byte{0, 18, 66}: "Millennial Net", + [3]byte{0, 18, 67}: "Cisco Systems, Inc", + [3]byte{0, 18, 68}: "Cisco Systems, Inc", + [3]byte{0, 18, 69}: "Zellweger Analytics, Inc.", + [3]byte{0, 18, 70}: "T.O.M TECHNOLOGY INC..", + [3]byte{0, 18, 71}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 18, 72}: "EMC Corporation (Kashya)", + [3]byte{0, 18, 73}: "Delta Elettronica S.p.A.", + [3]byte{0, 18, 74}: "Dedicated Devices, Inc.", + [3]byte{0, 18, 75}: "Texas Instruments", + [3]byte{0, 18, 76}: "BBWM Corporation", + [3]byte{0, 18, 77}: "Inducon BV", + [3]byte{0, 18, 78}: "XAC AUTOMATION CORP.", + [3]byte{0, 18, 79}: "Pentair Thermal Management", + [3]byte{0, 18, 80}: "Tokyo Aircaft Instrument Co., Ltd.", + [3]byte{0, 18, 81}: "SILINK", + [3]byte{0, 18, 82}: "Citronix, LLC", + [3]byte{0, 18, 83}: "AudioDev AB", + [3]byte{0, 18, 84}: "Spectra Technologies Holdings Company Ltd", + [3]byte{0, 18, 85}: "NetEffect Incorporated", + [3]byte{0, 18, 86}: "LG INFORMATION & COMM.", + [3]byte{0, 18, 87}: "LeapComm Communication Technologies Inc.", + [3]byte{0, 18, 88}: "Activis Polska", + [3]byte{0, 18, 89}: "THERMO ELECTRON KARLSRUHE", + [3]byte{0, 18, 90}: "Microsoft Corporation", + [3]byte{0, 18, 91}: "KAIMEI ELECTRONI", + [3]byte{0, 18, 92}: "Green Hills Software, Inc.", + [3]byte{0, 18, 93}: "CyberNet Inc.", + [3]byte{0, 18, 94}: "CAEN", + [3]byte{0, 18, 95}: "AWIND Inc.", + [3]byte{0, 18, 96}: "Stanton Magnetics,inc.", + [3]byte{0, 18, 97}: "Adaptix, Inc", + [3]byte{0, 18, 98}: "Nokia Danmark A/S", + [3]byte{0, 18, 99}: "Data Voice Technologies GmbH", + [3]byte{0, 18, 100}: "daum electronic gmbh", + [3]byte{0, 18, 101}: "Enerdyne Technologies, Inc.", + [3]byte{0, 18, 102}: "Swisscom Hospitality Services SA", + [3]byte{0, 18, 103}: "Panasonic Corporation", + [3]byte{0, 18, 104}: "IPS d.o.o.", + [3]byte{0, 18, 105}: "Value Electronics", + [3]byte{0, 18, 106}: "OPTOELECTRONICS Co., Ltd.", + [3]byte{0, 18, 107}: "Ascalade Communications Limited", + [3]byte{0, 18, 108}: "Visonic Technologies 1993 Ltd.", + [3]byte{0, 18, 109}: "University of California, Berkeley", + [3]byte{0, 18, 110}: "Seidel Elektronik GmbH Nfg.KG", + [3]byte{0, 18, 111}: "Rayson Technology Co., Ltd.", + [3]byte{0, 18, 112}: "NGES Denro Systems", + [3]byte{0, 18, 113}: "Measurement Computing Corp", + [3]byte{0, 18, 114}: "Redux Communications Ltd.", + [3]byte{0, 18, 115}: "Stoke Inc", + [3]byte{0, 18, 116}: "NIT lab", + [3]byte{0, 18, 117}: "Sentilla Corporation", + [3]byte{0, 18, 118}: "CG Power Systems Ireland Limited", + [3]byte{0, 18, 119}: "Korenix Technologies Co., Ltd.", + [3]byte{0, 18, 120}: "International Bar Code", + [3]byte{0, 18, 121}: "Hewlett Packard", + [3]byte{0, 18, 122}: "Sanyu Industry Co.,Ltd.", + [3]byte{0, 18, 123}: "VIA Networking Technologies, Inc.", + [3]byte{0, 18, 124}: "SWEGON AB", + [3]byte{0, 18, 125}: "MobileAria", + [3]byte{0, 18, 126}: "Digital Lifestyles Group, Inc.", + [3]byte{0, 18, 127}: "Cisco Systems, Inc", + [3]byte{0, 18, 128}: "Cisco Systems, Inc", + [3]byte{0, 18, 129}: "March Networks S.p.A.", + [3]byte{0, 18, 130}: "Qovia", + [3]byte{0, 18, 131}: "Nortel Networks", + [3]byte{0, 18, 132}: "Lab33 Srl", + [3]byte{0, 18, 133}: "Gizmondo Europe Ltd", + [3]byte{0, 18, 134}: "ENDEVCO CORP", + [3]byte{0, 18, 135}: "Digital Everywhere Unterhaltungselektronik GmbH", + [3]byte{0, 18, 136}: "2Wire Inc", + [3]byte{0, 18, 137}: "Advance Sterilization Products", + [3]byte{0, 18, 138}: "ARRIS Group, Inc.", + [3]byte{0, 18, 139}: "Sensory Networks Inc", + [3]byte{0, 18, 140}: "Woodward Governor", + [3]byte{0, 18, 141}: "STB Datenservice GmbH", + [3]byte{0, 18, 142}: "Q-Free ASA", + [3]byte{0, 18, 143}: "Montilio", + [3]byte{0, 18, 144}: "KYOWA Electric & Machinery Corp.", + [3]byte{0, 18, 145}: "KWS Computersysteme GmbH", + [3]byte{0, 18, 146}: "Griffin Technology", + [3]byte{0, 18, 147}: "GE Energy", + [3]byte{0, 18, 148}: "SUMITOMO ELECTRIC DEVICE INNOVATIONS, INC", + [3]byte{0, 18, 149}: "Aiware Inc.", + [3]byte{0, 18, 150}: "Addlogix", + [3]byte{0, 18, 151}: "O2Micro, Inc.", + [3]byte{0, 18, 152}: "MICO ELECTRIC(SHENZHEN) LIMITED", + [3]byte{0, 18, 153}: "Ktech Telecommunications Inc", + [3]byte{0, 18, 154}: "IRT Electronics Pty Ltd", + [3]byte{0, 18, 155}: "E2S Electronic Engineering Solutions, S.L.", + [3]byte{0, 18, 156}: "Yulinet", + [3]byte{0, 18, 157}: "First International Computer do Brasil", + [3]byte{0, 18, 158}: "Surf Communications Inc.", + [3]byte{0, 18, 159}: "RAE Systems", + [3]byte{0, 18, 160}: "NeoMeridian Sdn Bhd", + [3]byte{0, 18, 161}: "BluePacket Communications Co., Ltd.", + [3]byte{0, 18, 162}: "VITA", + [3]byte{0, 18, 163}: "Trust International B.V.", + [3]byte{0, 18, 164}: "ThingMagic, LLC", + [3]byte{0, 18, 165}: "Stargen, Inc.", + [3]byte{0, 18, 166}: "Dolby Australia", + [3]byte{0, 18, 167}: "ISR TECHNOLOGIES Inc", + [3]byte{0, 18, 168}: "intec GmbH", + [3]byte{0, 18, 169}: "3Com Ltd", + [3]byte{0, 18, 170}: "IEE, Inc.", + [3]byte{0, 18, 171}: "WiLife, Inc.", + [3]byte{0, 18, 172}: "ONTIMETEK INC.", + [3]byte{0, 18, 173}: "IDS GmbH", + [3]byte{0, 18, 174}: "HLS HARD-LINE Solutions Inc.", + [3]byte{0, 18, 175}: "ELPRO Technologies", + [3]byte{0, 18, 176}: "Efore Oyj (Plc)", + [3]byte{0, 18, 177}: "Dai Nippon Printing Co., Ltd", + [3]byte{0, 18, 178}: "AVOLITES LTD.", + [3]byte{0, 18, 179}: "Advance Wireless Technology Corp.", + [3]byte{0, 18, 180}: "Work Microwave GmbH", + [3]byte{0, 18, 181}: "Vialta, Inc.", + [3]byte{0, 18, 182}: "Santa Barbara Infrared, Inc.", + [3]byte{0, 18, 183}: "PTW Freiburg", + [3]byte{0, 18, 184}: "G2 Microsystems", + [3]byte{0, 18, 185}: "Fusion Digital Technology", + [3]byte{0, 18, 186}: "FSI Systems, Inc.", + [3]byte{0, 18, 187}: "Telecommunications Industry Association TR-41 Committee", + [3]byte{0, 18, 188}: "Echolab LLC", + [3]byte{0, 18, 189}: "Avantec Manufacturing Limited", + [3]byte{0, 18, 190}: "Astek Corporation", + [3]byte{0, 18, 191}: "Arcadyan Technology Corporation", + [3]byte{0, 18, 192}: "HotLava Systems, Inc.", + [3]byte{0, 18, 193}: "Check Point Software Technologies", + [3]byte{0, 18, 194}: "Apex Electronics Factory", + [3]byte{0, 18, 195}: "WIT S.A.", + [3]byte{0, 18, 196}: "Viseon, Inc.", + [3]byte{0, 18, 197}: "V-Show Technology (China) Co.,Ltd", + [3]byte{0, 18, 198}: "TGC America, Inc", + [3]byte{0, 18, 199}: "SECURAY Technologies Ltd.Co.", + [3]byte{0, 18, 200}: "Perfect tech", + [3]byte{0, 18, 201}: "ARRIS Group, Inc.", + [3]byte{0, 18, 202}: "Mechatronic Brick Aps", + [3]byte{0, 18, 203}: "CSS Inc.", + [3]byte{0, 18, 204}: "Bitatek CO., LTD", + [3]byte{0, 18, 205}: "ASEM SpA", + [3]byte{0, 18, 206}: "Advanced Cybernetics Group", + [3]byte{0, 18, 207}: "Accton Technology Corp", + [3]byte{0, 18, 208}: "Gossen-Metrawatt-GmbH", + [3]byte{0, 18, 209}: "Texas Instruments", + [3]byte{0, 18, 210}: "Texas Instruments", + [3]byte{0, 18, 211}: "Zetta Systems, Inc.", + [3]byte{0, 18, 212}: "Princeton Technology, Ltd", + [3]byte{0, 18, 213}: "Motion Reality Inc.", + [3]byte{0, 18, 214}: "Jiangsu Yitong High-Tech Co.,Ltd", + [3]byte{0, 18, 215}: "Invento Networks, Inc.", + [3]byte{0, 18, 216}: "International Games System Co., Ltd.", + [3]byte{0, 18, 217}: "Cisco Systems, Inc", + [3]byte{0, 18, 218}: "Cisco Systems, Inc", + [3]byte{0, 18, 219}: "ZIEHL industrie-elektronik GmbH + Co KG", + [3]byte{0, 18, 220}: "SunCorp Industrial Limited", + [3]byte{0, 18, 221}: "Shengqu Information Technology (Shanghai) Co., Ltd.", + [3]byte{0, 18, 222}: "Radio Components Sweden AB", + [3]byte{0, 18, 223}: "Novomatic AG", + [3]byte{0, 18, 224}: "Codan Limited", + [3]byte{0, 18, 225}: "Alliant Networks, Inc", + [3]byte{0, 18, 226}: "ALAXALA Networks Corporation", + [3]byte{0, 18, 227}: "Agat-RT, Ltd.", + [3]byte{0, 18, 228}: "ZIEHL industrie-electronik GmbH + Co KG", + [3]byte{0, 18, 229}: "Time America, Inc.", + [3]byte{0, 18, 230}: "SPECTEC COMPUTER CO., LTD.", + [3]byte{0, 18, 231}: "Projectek Networking Electronics Corp.", + [3]byte{0, 18, 232}: "Fraunhofer IMS", + [3]byte{0, 18, 233}: "Abbey Systems Ltd", + [3]byte{0, 18, 234}: "Trane", + [3]byte{0, 18, 235}: "PDH Solutions, LLC", + [3]byte{0, 18, 236}: "Movacolor b.v.", + [3]byte{0, 18, 237}: "AVG Advanced Technologies", + [3]byte{0, 18, 238}: "Sony Mobile Communications AB", + [3]byte{0, 18, 239}: "OneAccess SA", + [3]byte{0, 18, 240}: "Intel Corporate", + [3]byte{0, 18, 241}: "IFOTEC", + [3]byte{0, 18, 242}: "Brocade Communications Systems, Inc.", + [3]byte{0, 18, 243}: "connectBlue AB", + [3]byte{0, 18, 244}: "Belco International Co.,Ltd.", + [3]byte{0, 18, 245}: "Imarda New Zealand Limited", + [3]byte{0, 18, 246}: "MDK CO.,LTD.", + [3]byte{0, 18, 247}: "Xiamen Xinglian Electronics Co., Ltd.", + [3]byte{0, 18, 248}: "WNI Resources, LLC", + [3]byte{0, 18, 249}: "URYU SEISAKU, LTD.", + [3]byte{0, 18, 250}: "THX LTD", + [3]byte{0, 18, 251}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 18, 252}: "PLANET System Co.,LTD", + [3]byte{0, 18, 253}: "OPTIMUS IC S.A.", + [3]byte{0, 18, 254}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{0, 18, 255}: "Lely Industries N.V.", + [3]byte{0, 19, 0}: "IT-FACTORY, INC.", + [3]byte{0, 19, 1}: "IronGate S.L.", + [3]byte{0, 19, 2}: "Intel Corporate", + [3]byte{0, 19, 3}: "GateConnect", + [3]byte{0, 19, 4}: "Flaircomm Technologies Co. LTD", + [3]byte{0, 19, 5}: "Epicom, Inc.", + [3]byte{0, 19, 6}: "Always On Wireless", + [3]byte{0, 19, 7}: "Paravirtual Corporation", + [3]byte{0, 19, 8}: "Nuvera Fuel Cells", + [3]byte{0, 19, 9}: "Ocean Broadband Networks", + [3]byte{0, 19, 10}: "Nortel Networks", + [3]byte{0, 19, 11}: "Mextal B.V.", + [3]byte{0, 19, 12}: "HF System Corporation", + [3]byte{0, 19, 13}: "GALILEO AVIONICA", + [3]byte{0, 19, 14}: "Focusrite Audio Engineering Limited", + [3]byte{0, 19, 15}: "EGEMEN Bilgisayar Muh San ve Tic LTD STI", + [3]byte{0, 19, 16}: "Cisco-Linksys, LLC", + [3]byte{0, 19, 17}: "ARRIS Group, Inc.", + [3]byte{0, 19, 18}: "Amedia Networks Inc.", + [3]byte{0, 19, 19}: "GuangZhou Post & Telecom Equipment ltd", + [3]byte{0, 19, 20}: "Asiamajor Inc.", + [3]byte{0, 19, 21}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 19, 22}: "L-S-B Broadcast Technologies GmbH", + [3]byte{0, 19, 23}: "GN Netcom A/S", + [3]byte{0, 19, 24}: "DGSTATION Co., Ltd.", + [3]byte{0, 19, 25}: "Cisco Systems, Inc", + [3]byte{0, 19, 26}: "Cisco Systems, Inc", + [3]byte{0, 19, 27}: "BeCell Innovations Corp.", + [3]byte{0, 19, 28}: "LiteTouch, Inc.", + [3]byte{0, 19, 29}: "Scanvaegt International A/S", + [3]byte{0, 19, 30}: "Peiker acustic GmbH & Co. KG", + [3]byte{0, 19, 31}: "NxtPhase T&D, Corp.", + [3]byte{0, 19, 32}: "Intel Corporate", + [3]byte{0, 19, 33}: "Hewlett Packard", + [3]byte{0, 19, 34}: "DAQ Electronics, Inc.", + [3]byte{0, 19, 35}: "Cap Co., Ltd.", + [3]byte{0, 19, 36}: "Schneider Electric Ultra Terminal", + [3]byte{0, 19, 37}: "Cortina Systems Inc", + [3]byte{0, 19, 38}: "ECM Systems Ltd", + [3]byte{0, 19, 39}: "Data Acquisitions limited", + [3]byte{0, 19, 40}: "Westech Korea Inc.,", + [3]byte{0, 19, 41}: "VSST Co., LTD", + [3]byte{0, 19, 42}: "Sitronics Telecom Solutions", + [3]byte{0, 19, 43}: "Phoenix Digital", + [3]byte{0, 19, 44}: "MAZ Brandenburg GmbH", + [3]byte{0, 19, 45}: "iWise Communications", + [3]byte{0, 19, 46}: "ITian Coporation", + [3]byte{0, 19, 47}: "Interactek", + [3]byte{0, 19, 48}: "EURO PROTECTION SURVEILLANCE", + [3]byte{0, 19, 49}: "CellPoint Connect", + [3]byte{0, 19, 50}: "Beijing Topsec Network Security Technology Co., Ltd.", + [3]byte{0, 19, 51}: "BaudTec Corporation", + [3]byte{0, 19, 52}: "Arkados, Inc.", + [3]byte{0, 19, 53}: "VS Industry Berhad", + [3]byte{0, 19, 54}: "Tianjin 712 Communication Broadcasting co., ltd.", + [3]byte{0, 19, 55}: "Orient Power Home Network Ltd.", + [3]byte{0, 19, 56}: "FRESENIUS-VIAL", + [3]byte{0, 19, 57}: "CCV Deutschland GmbH", + [3]byte{0, 19, 58}: "VadaTech Inc.", + [3]byte{0, 19, 59}: "Speed Dragon Multimedia Limited", + [3]byte{0, 19, 60}: "QUINTRON SYSTEMS INC.", + [3]byte{0, 19, 61}: "Micro Memory Curtiss Wright Co", + [3]byte{0, 19, 62}: "MetaSwitch", + [3]byte{0, 19, 63}: "Eppendorf Instrumente GmbH", + [3]byte{0, 19, 64}: "AD.EL s.r.l.", + [3]byte{0, 19, 65}: "Shandong New Beiyang Information Technology Co.,Ltd", + [3]byte{0, 19, 66}: "Vision Research, Inc.", + [3]byte{0, 19, 67}: "Matsushita Electronic Components (Europe) GmbH", + [3]byte{0, 19, 68}: "Fargo Electronics Inc.", + [3]byte{0, 19, 69}: "Eaton Corporation", + [3]byte{0, 19, 70}: "D-Link Corporation", + [3]byte{0, 19, 71}: "Red Lion Controls, LP", + [3]byte{0, 19, 72}: "Artila Electronics Co., Ltd.", + [3]byte{0, 19, 73}: "ZyXEL Communications Corporation", + [3]byte{0, 19, 74}: "Engim, Inc.", + [3]byte{0, 19, 75}: "ToGoldenNet Technology Inc.", + [3]byte{0, 19, 76}: "YDT Technology International", + [3]byte{0, 19, 77}: "Inepro BV", + [3]byte{0, 19, 78}: "Valox Systems, Inc.", + [3]byte{0, 19, 79}: "Tranzeo Wireless Technologies Inc.", + [3]byte{0, 19, 80}: "Silver Spring Networks, Inc", + [3]byte{0, 19, 81}: "Niles Audio Corporation", + [3]byte{0, 19, 82}: "Naztec, Inc.", + [3]byte{0, 19, 83}: "HYDAC Filtertechnik GMBH", + [3]byte{0, 19, 84}: "Zcomax Technologies, Inc.", + [3]byte{0, 19, 85}: "TOMEN Cyber-business Solutions, Inc.", + [3]byte{0, 19, 86}: "FLIR Radiation Inc", + [3]byte{0, 19, 87}: "Soyal Technology Co., Ltd.", + [3]byte{0, 19, 88}: "Realm Systems, Inc.", + [3]byte{0, 19, 89}: "ProTelevision Technologies A/S", + [3]byte{0, 19, 90}: "Project T&E Limited", + [3]byte{0, 19, 91}: "PanelLink Cinema, LLC", + [3]byte{0, 19, 92}: "OnSite Systems, Inc.", + [3]byte{0, 19, 93}: "NTTPC Communications, Inc.", + [3]byte{0, 19, 94}: "EAB/RWI/K", + [3]byte{0, 19, 95}: "Cisco Systems, Inc", + [3]byte{0, 19, 96}: "Cisco Systems, Inc", + [3]byte{0, 19, 97}: "Biospace Co., Ltd.", + [3]byte{0, 19, 98}: "ShinHeung Precision Co., Ltd.", + [3]byte{0, 19, 99}: "Verascape, Inc.", + [3]byte{0, 19, 100}: "Paradigm Technology Inc..", + [3]byte{0, 19, 101}: "Nortel Networks", + [3]byte{0, 19, 102}: "Neturity Technologies Inc.", + [3]byte{0, 19, 103}: "Narayon. Co., Ltd.", + [3]byte{0, 19, 104}: "Saab Danmark A/S", + [3]byte{0, 19, 105}: "Honda Electron Co., LED.", + [3]byte{0, 19, 106}: "Hach Lange Sarl", + [3]byte{0, 19, 107}: "E-TEC", + [3]byte{0, 19, 108}: "TomTom", + [3]byte{0, 19, 109}: "Tentaculus AB", + [3]byte{0, 19, 110}: "Techmetro Corp.", + [3]byte{0, 19, 111}: "PacketMotion, Inc.", + [3]byte{0, 19, 112}: "Nokia Danmark A/S", + [3]byte{0, 19, 113}: "ARRIS Group, Inc.", + [3]byte{0, 19, 114}: "Dell Inc.", + [3]byte{0, 19, 115}: "BLwave Electronics Co., Ltd", + [3]byte{0, 19, 116}: "Atheros Communications, Inc.", + [3]byte{0, 19, 117}: "American Security Products Co.", + [3]byte{0, 19, 118}: "Tabor Electronics Ltd.", + [3]byte{0, 19, 119}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 19, 120}: "Qsan Technology, Inc.", + [3]byte{0, 19, 121}: "PONDER INFORMATION INDUSTRIES LTD.", + [3]byte{0, 19, 122}: "Netvox Technology Co., Ltd.", + [3]byte{0, 19, 123}: "Movon Corporation", + [3]byte{0, 19, 124}: "Kaicom co., Ltd.", + [3]byte{0, 19, 125}: "Dynalab, Inc.", + [3]byte{0, 19, 126}: "CorEdge Networks, Inc.", + [3]byte{0, 19, 127}: "Cisco Systems, Inc", + [3]byte{0, 19, 128}: "Cisco Systems, Inc", + [3]byte{0, 19, 129}: "CHIPS & Systems, Inc.", + [3]byte{0, 19, 130}: "Cetacea Networks Corporation", + [3]byte{0, 19, 131}: "Application Technologies and Engineering Research Laboratory", + [3]byte{0, 19, 132}: "Advanced Motion Controls", + [3]byte{0, 19, 133}: "Add-On Technology Co., LTD.", + [3]byte{0, 19, 134}: "ABB Inc./Totalflow", + [3]byte{0, 19, 135}: "27M Technologies AB", + [3]byte{0, 19, 136}: "WiMedia Alliance", + [3]byte{0, 19, 137}: "Redes de Telefonía Móvil S.A.", + [3]byte{0, 19, 138}: "QINGDAO GOERTEK ELECTRONICS CO.,LTD.", + [3]byte{0, 19, 139}: "Phantom Technologies LLC", + [3]byte{0, 19, 140}: "Kumyoung.Co.Ltd", + [3]byte{0, 19, 141}: "Kinghold", + [3]byte{0, 19, 142}: "FOAB Elektronik AB", + [3]byte{0, 19, 143}: "Asiarock Technology Limited", + [3]byte{0, 19, 144}: "Termtek Computer Co., Ltd", + [3]byte{0, 19, 145}: "OUEN CO.,LTD.", + [3]byte{0, 19, 146}: "Ruckus Wireless", + [3]byte{0, 19, 147}: "Panta Systems, Inc.", + [3]byte{0, 19, 148}: "Infohand Co.,Ltd", + [3]byte{0, 19, 149}: "congatec AG", + [3]byte{0, 19, 150}: "Acbel Polytech Inc.", + [3]byte{0, 19, 151}: "Oracle Corporation ", + [3]byte{0, 19, 152}: "TrafficSim Co.,Ltd", + [3]byte{0, 19, 153}: "STAC Corporation.", + [3]byte{0, 19, 154}: "K-ubique ID Corp.", + [3]byte{0, 19, 155}: "ioIMAGE Ltd.", + [3]byte{0, 19, 156}: "Exavera Technologies, Inc.", + [3]byte{0, 19, 157}: "Marvell Hispana S.L.", + [3]byte{0, 19, 158}: "Ciara Technologies Inc.", + [3]byte{0, 19, 159}: "Electronics Design Services, Co., Ltd.", + [3]byte{0, 19, 160}: "ALGOSYSTEM Co., Ltd.", + [3]byte{0, 19, 161}: "Crow Electronic Engeneering", + [3]byte{0, 19, 162}: "MaxStream, Inc", + [3]byte{0, 19, 163}: "Siemens Com CPE Devices", + [3]byte{0, 19, 164}: "KeyEye Communications", + [3]byte{0, 19, 165}: "General Solutions, LTD.", + [3]byte{0, 19, 166}: "Extricom Ltd", + [3]byte{0, 19, 167}: "BATTELLE MEMORIAL INSTITUTE", + [3]byte{0, 19, 168}: "Tanisys Technology", + [3]byte{0, 19, 169}: "Sony Corporation", + [3]byte{0, 19, 170}: "ALS & TEC Ltd.", + [3]byte{0, 19, 171}: "Telemotive AG", + [3]byte{0, 19, 172}: "Sunmyung Electronics Co., LTD", + [3]byte{0, 19, 173}: "Sendo Ltd", + [3]byte{0, 19, 174}: "Radiance Technologies, Inc.", + [3]byte{0, 19, 175}: "NUMA Technology,Inc.", + [3]byte{0, 19, 176}: "Jablotron", + [3]byte{0, 19, 177}: "Intelligent Control Systems (Asia) Pte Ltd", + [3]byte{0, 19, 178}: "Carallon Limited", + [3]byte{0, 19, 179}: "Ecom Communications Technology Co., Ltd.", + [3]byte{0, 19, 180}: "Appear TV", + [3]byte{0, 19, 181}: "Wavesat", + [3]byte{0, 19, 182}: "Sling Media, Inc.", + [3]byte{0, 19, 183}: "Scantech ID", + [3]byte{0, 19, 184}: "RyCo Electronic Systems Limited", + [3]byte{0, 19, 185}: "BM SPA", + [3]byte{0, 19, 186}: "ReadyLinks Inc", + [3]byte{0, 19, 187}: "Smartvue Corporation", + [3]byte{0, 19, 188}: "Artimi Ltd", + [3]byte{0, 19, 189}: "HYMATOM SA", + [3]byte{0, 19, 190}: "Virtual Conexions", + [3]byte{0, 19, 191}: "Media System Planning Corp.", + [3]byte{0, 19, 192}: "Trix Tecnologia Ltda.", + [3]byte{0, 19, 193}: "Asoka USA Corporation", + [3]byte{0, 19, 194}: "WACOM Co.,Ltd", + [3]byte{0, 19, 195}: "Cisco Systems, Inc", + [3]byte{0, 19, 196}: "Cisco Systems, Inc", + [3]byte{0, 19, 197}: "LIGHTRON FIBER-OPTIC DEVICES INC.", + [3]byte{0, 19, 198}: "OpenGear, Inc", + [3]byte{0, 19, 199}: "IONOS Co.,Ltd.", + [3]byte{0, 19, 200}: "ADB Broadband Italia", + [3]byte{0, 19, 201}: "Beyond Achieve Enterprises Ltd.", + [3]byte{0, 19, 202}: "Pico Digital", + [3]byte{0, 19, 203}: "Zenitel Norway AS", + [3]byte{0, 19, 204}: "Tall Maple Systems", + [3]byte{0, 19, 205}: "MTI co. LTD", + [3]byte{0, 19, 206}: "Intel Corporate", + [3]byte{0, 19, 207}: "4Access Communications", + [3]byte{0, 19, 208}: "t+ Medical Ltd", + [3]byte{0, 19, 209}: "KIRK telecom A/S", + [3]byte{0, 19, 210}: "PAGE IBERICA, S.A.", + [3]byte{0, 19, 211}: "MICRO-STAR INTERNATIONAL CO., LTD.", + [3]byte{0, 19, 212}: "ASUSTek COMPUTER INC.", + [3]byte{0, 19, 213}: "RuggedCom", + [3]byte{0, 19, 214}: "TII NETWORK TECHNOLOGIES, INC.", + [3]byte{0, 19, 215}: "SPIDCOM Technologies SA", + [3]byte{0, 19, 216}: "Princeton Instruments", + [3]byte{0, 19, 217}: "Matrix Product Development, Inc.", + [3]byte{0, 19, 218}: "Diskware Co., Ltd", + [3]byte{0, 19, 219}: "SHOEI Electric Co.,Ltd", + [3]byte{0, 19, 220}: "IBTEK INC.", + [3]byte{0, 19, 221}: "Abbott Diagnostics", + [3]byte{0, 19, 222}: "Adapt4, LLC", + [3]byte{0, 19, 223}: "Ryvor Corp.", + [3]byte{0, 19, 224}: "Murata Manufacturing Co., Ltd.", + [3]byte{0, 19, 225}: "Iprobe AB", + [3]byte{0, 19, 226}: "GeoVision Inc.", + [3]byte{0, 19, 227}: "CoVi Technologies, Inc.", + [3]byte{0, 19, 228}: "YANGJAE SYSTEMS CORP.", + [3]byte{0, 19, 229}: "TENOSYS, INC.", + [3]byte{0, 19, 230}: "Technolution", + [3]byte{0, 19, 231}: "Halcro", + [3]byte{0, 19, 232}: "Intel Corporate", + [3]byte{0, 19, 233}: "VeriWave, Inc.", + [3]byte{0, 19, 234}: "Kamstrup A/S", + [3]byte{0, 19, 235}: "Sysmaster Corporation", + [3]byte{0, 19, 236}: "Netsnapper Technologies SARL", + [3]byte{0, 19, 237}: "PSIA", + [3]byte{0, 19, 238}: "JBX Designs Inc.", + [3]byte{0, 19, 239}: "Kingjon Digital Technology Co.,Ltd", + [3]byte{0, 19, 240}: "Wavefront Semiconductor", + [3]byte{0, 19, 241}: "AMOD Technology Co., Ltd.", + [3]byte{0, 19, 242}: "Klas Ltd", + [3]byte{0, 19, 243}: "Giga-byte Communications Inc.", + [3]byte{0, 19, 244}: "Psitek (Pty) Ltd", + [3]byte{0, 19, 245}: "Akimbi Systems", + [3]byte{0, 19, 246}: "Cintech", + [3]byte{0, 19, 247}: "SMC Networks, Inc.", + [3]byte{0, 19, 248}: "Dex Security Solutions", + [3]byte{0, 19, 249}: "Cavera Systems", + [3]byte{0, 19, 250}: "LifeSize Communications, Inc", + [3]byte{0, 19, 251}: "RKC INSTRUMENT INC.", + [3]byte{0, 19, 252}: "SiCortex, Inc", + [3]byte{0, 19, 253}: "Nokia Danmark A/S", + [3]byte{0, 19, 254}: "GRANDTEC ELECTRONIC CORP.", + [3]byte{0, 19, 255}: "Dage-MTI of MC, Inc.", + [3]byte{0, 20, 0}: "MINERVA KOREA CO., LTD", + [3]byte{0, 20, 1}: "Rivertree Networks Corp.", + [3]byte{0, 20, 2}: "kk-electronic a/s", + [3]byte{0, 20, 3}: "Renasis, LLC", + [3]byte{0, 20, 4}: "ARRIS Group, Inc.", + [3]byte{0, 20, 5}: "OpenIB, Inc.", + [3]byte{0, 20, 6}: "Go Networks", + [3]byte{0, 20, 7}: "Sperian Protection Instrumentation", + [3]byte{0, 20, 8}: "Eka Systems Inc.", + [3]byte{0, 20, 9}: "MAGNETI MARELLI S.E. S.p.A.", + [3]byte{0, 20, 10}: "WEPIO Co., Ltd.", + [3]byte{0, 20, 11}: "FIRST INTERNATIONAL COMPUTER, INC.", + [3]byte{0, 20, 12}: "GKB CCTV CO., LTD.", + [3]byte{0, 20, 13}: "Nortel Networks", + [3]byte{0, 20, 14}: "Nortel Networks", + [3]byte{0, 20, 15}: "Federal State Unitary Enterprise Leningrad R&D Institute of", + [3]byte{0, 20, 16}: "Suzhou Keda Technology CO.,Ltd", + [3]byte{0, 20, 17}: "Deutschmann Automation GmbH & Co. KG", + [3]byte{0, 20, 18}: "S-TEC electronics AG", + [3]byte{0, 20, 19}: "Trebing & Himstedt Prozeßautomation GmbH & Co. KG", + [3]byte{0, 20, 20}: "Jumpnode Systems LLC.", + [3]byte{0, 20, 21}: "Intec Automation inc.", + [3]byte{0, 20, 22}: "Scosche Industries, Inc.", + [3]byte{0, 20, 23}: "RSE Informations Technologie GmbH", + [3]byte{0, 20, 24}: "C4Line", + [3]byte{0, 20, 25}: "SIDSA", + [3]byte{0, 20, 26}: "DEICY CORPORATION", + [3]byte{0, 20, 27}: "Cisco Systems, Inc", + [3]byte{0, 20, 28}: "Cisco Systems, Inc", + [3]byte{0, 20, 29}: "LTi DRIVES GmbH", + [3]byte{0, 20, 30}: "P.A. Semi, Inc.", + [3]byte{0, 20, 31}: "SunKwang Electronics Co., Ltd", + [3]byte{0, 20, 32}: "G-Links networking company", + [3]byte{0, 20, 33}: "Total Wireless Technologies Pte. Ltd.", + [3]byte{0, 20, 34}: "Dell Inc.", + [3]byte{0, 20, 35}: "J-S Co. NEUROCOM", + [3]byte{0, 20, 36}: "Merry Electrics CO., LTD.", + [3]byte{0, 20, 37}: "Galactic Computing Corp.", + [3]byte{0, 20, 38}: "NL Technology", + [3]byte{0, 20, 39}: "JazzMutant", + [3]byte{0, 20, 40}: "Vocollect Inc", + [3]byte{0, 20, 41}: "V Center Technologies Co., Ltd.", + [3]byte{0, 20, 42}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 20, 43}: "Edata Communication Inc.", + [3]byte{0, 20, 44}: "Koncept International, Inc.", + [3]byte{0, 20, 45}: "Toradex AG", + [3]byte{0, 20, 46}: "77 Elektronika Kft.", + [3]byte{0, 20, 47}: "Savvius", + [3]byte{0, 20, 48}: "ViPowER, Inc", + [3]byte{0, 20, 49}: "PDL Electronics Ltd", + [3]byte{0, 20, 50}: "Tarallax Wireless, Inc.", + [3]byte{0, 20, 51}: "Empower Technologies(Canada) Inc.", + [3]byte{0, 20, 52}: "Keri Systems, Inc", + [3]byte{0, 20, 53}: "CityCom Corp.", + [3]byte{0, 20, 54}: "Qwerty Elektronik AB", + [3]byte{0, 20, 55}: "GSTeletech Co.,Ltd.", + [3]byte{0, 20, 56}: "Hewlett Packard Enterprise", + [3]byte{0, 20, 57}: "Blonder Tongue Laboratories, Inc.", + [3]byte{0, 20, 58}: "RAYTALK INTERNATIONAL SRL", + [3]byte{0, 20, 59}: "Sensovation AG", + [3]byte{0, 20, 60}: "Rheinmetall Canada Inc.", + [3]byte{0, 20, 61}: "Aevoe Inc.", + [3]byte{0, 20, 62}: "AirLink Communications, Inc.", + [3]byte{0, 20, 63}: "Hotway Technology Corporation", + [3]byte{0, 20, 64}: "ATOMIC Corporation", + [3]byte{0, 20, 65}: "Innovation Sound Technology Co., LTD.", + [3]byte{0, 20, 66}: "ATTO CORPORATION", + [3]byte{0, 20, 67}: "Consultronics Europe Ltd", + [3]byte{0, 20, 68}: "Grundfos Holding", + [3]byte{0, 20, 69}: "Telefon-Gradnja d.o.o.", + [3]byte{0, 20, 70}: "SuperVision Solutions LLC", + [3]byte{0, 20, 71}: "BOAZ Inc.", + [3]byte{0, 20, 72}: "Inventec Multimedia & Telecom Corporation", + [3]byte{0, 20, 73}: "Sichuan Changhong Electric Ltd.", + [3]byte{0, 20, 74}: "Taiwan Thick-Film Ind. Corp.", + [3]byte{0, 20, 75}: "Hifn, Inc.", + [3]byte{0, 20, 76}: "General Meters Corp.", + [3]byte{0, 20, 77}: "Intelligent Systems", + [3]byte{0, 20, 78}: "SRISA", + [3]byte{0, 20, 79}: "Oracle Corporation ", + [3]byte{0, 20, 80}: "Heim Systems GmbH", + [3]byte{0, 20, 81}: "Apple, Inc.", + [3]byte{0, 20, 82}: "CALCULEX,INC.", + [3]byte{0, 20, 83}: "ADVANTECH TECHNOLOGIES CO.,LTD", + [3]byte{0, 20, 84}: "Symwave", + [3]byte{0, 20, 85}: "Coder Electronics Corporation", + [3]byte{0, 20, 86}: "Edge Products", + [3]byte{0, 20, 87}: "T-VIPS AS", + [3]byte{0, 20, 88}: "HS Automatic ApS", + [3]byte{0, 20, 89}: "Moram Co., Ltd.", + [3]byte{0, 20, 90}: "Neratec Solutions AG", + [3]byte{0, 20, 91}: "SeekerNet Inc.", + [3]byte{0, 20, 92}: "Intronics B.V.", + [3]byte{0, 20, 93}: "WJ Communications, Inc.", + [3]byte{0, 20, 94}: "IBM Corp", + [3]byte{0, 20, 95}: "ADITEC CO. LTD", + [3]byte{0, 20, 96}: "Kyocera Wireless Corp.", + [3]byte{0, 20, 97}: "CORONA CORPORATION", + [3]byte{0, 20, 98}: "Digiwell Technology, inc", + [3]byte{0, 20, 99}: "IDCS N.V.", + [3]byte{0, 20, 100}: "Cryptosoft", + [3]byte{0, 20, 101}: "Novo Nordisk A/S", + [3]byte{0, 20, 102}: "Kleinhenz Elektronik GmbH", + [3]byte{0, 20, 103}: "ArrowSpan Inc.", + [3]byte{0, 20, 104}: "CelPlan International, Inc.", + [3]byte{0, 20, 105}: "Cisco Systems, Inc", + [3]byte{0, 20, 106}: "Cisco Systems, Inc", + [3]byte{0, 20, 107}: "Anagran, Inc.", + [3]byte{0, 20, 108}: "NETGEAR", + [3]byte{0, 20, 109}: "RF Technologies", + [3]byte{0, 20, 110}: "H. Stoll GmbH & Co. KG", + [3]byte{0, 20, 111}: "Kohler Co", + [3]byte{0, 20, 112}: "Prokom Software SA", + [3]byte{0, 20, 113}: "Eastern Asia Technology Limited", + [3]byte{0, 20, 114}: "China Broadband Wireless IP Standard Group", + [3]byte{0, 20, 115}: "Bookham Inc", + [3]byte{0, 20, 116}: "K40 Electronics", + [3]byte{0, 20, 117}: "Wiline Networks, Inc.", + [3]byte{0, 20, 118}: "MultiCom Industries Limited", + [3]byte{0, 20, 119}: "Nertec Inc.", + [3]byte{0, 20, 120}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 20, 121}: "NEC Magnus Communications,Ltd.", + [3]byte{0, 20, 122}: "Eubus GmbH", + [3]byte{0, 20, 123}: "Iteris, Inc.", + [3]byte{0, 20, 124}: "3Com Ltd", + [3]byte{0, 20, 125}: "Aeon Digital International", + [3]byte{0, 20, 126}: "InnerWireless", + [3]byte{0, 20, 127}: "Thomson Telecom Belgium", + [3]byte{0, 20, 128}: "Hitachi-LG Data Storage Korea, Inc", + [3]byte{0, 20, 129}: "Multilink Inc", + [3]byte{0, 20, 130}: "Aurora Networks", + [3]byte{0, 20, 131}: "eXS Inc.", + [3]byte{0, 20, 132}: "Cermate Technologies Inc.", + [3]byte{0, 20, 133}: "Giga-Byte", + [3]byte{0, 20, 134}: "Echo Digital Audio Corporation", + [3]byte{0, 20, 135}: "American Technology Integrators", + [3]byte{0, 20, 136}: "Akorri", + [3]byte{0, 20, 137}: "B15402100 - JANDEI, S.L.", + [3]byte{0, 20, 138}: "Elin Ebg Traction Gmbh", + [3]byte{0, 20, 139}: "Globo Electronic GmbH & Co. KG", + [3]byte{0, 20, 140}: "General Dynamics Mission Systems", + [3]byte{0, 20, 141}: "Cubic Defense Simulation Systems", + [3]byte{0, 20, 142}: "Tele Power Inc.", + [3]byte{0, 20, 143}: "Protronic (Far East) Ltd.", + [3]byte{0, 20, 144}: "ASP Corporation", + [3]byte{0, 20, 145}: "Daniels Electronics Ltd. dbo Codan Rado Communications", + [3]byte{0, 20, 146}: "Liteon, Mobile Media Solution SBU", + [3]byte{0, 20, 147}: "Systimax Solutions", + [3]byte{0, 20, 148}: "ESU AG", + [3]byte{0, 20, 149}: "2Wire Inc", + [3]byte{0, 20, 150}: "Phonic Corp.", + [3]byte{0, 20, 151}: "ZHIYUAN Eletronics co.,ltd.", + [3]byte{0, 20, 152}: "Viking Design Technology", + [3]byte{0, 20, 153}: "Helicomm Inc", + [3]byte{0, 20, 154}: "ARRIS Group, Inc.", + [3]byte{0, 20, 155}: "Nokota Communications, LLC", + [3]byte{0, 20, 156}: "HF Company", + [3]byte{0, 20, 157}: "Sound ID Inc.", + [3]byte{0, 20, 158}: "UbONE Co., Ltd", + [3]byte{0, 20, 159}: "System and Chips, Inc.", + [3]byte{0, 20, 160}: "Accsense, Inc.", + [3]byte{0, 20, 161}: "Synchronous Communication Corp", + [3]byte{0, 20, 162}: "Core Micro Systems Inc.", + [3]byte{0, 20, 163}: "Vitelec BV", + [3]byte{0, 20, 164}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 20, 165}: "Gemtek Technology Co., Ltd.", + [3]byte{0, 20, 166}: "Teranetics, Inc.", + [3]byte{0, 20, 167}: "Nokia Danmark A/S", + [3]byte{0, 20, 168}: "Cisco Systems, Inc", + [3]byte{0, 20, 169}: "Cisco Systems, Inc", + [3]byte{0, 20, 170}: "Ashly Audio, Inc.", + [3]byte{0, 20, 171}: "Senhai Electronic Technology Co., Ltd.", + [3]byte{0, 20, 172}: "Bountiful WiFi", + [3]byte{0, 20, 173}: "Gassner Wiege- und Meßtechnik GmbH", + [3]byte{0, 20, 174}: "Wizlogics Co., Ltd.", + [3]byte{0, 20, 175}: "Datasym POS Inc.", + [3]byte{0, 20, 176}: "Naeil Community", + [3]byte{0, 20, 177}: "Axell Wireless Limited", + [3]byte{0, 20, 178}: "mCubelogics Corporation", + [3]byte{0, 20, 179}: "CoreStar International Corp", + [3]byte{0, 20, 180}: "General Dynamics United Kingdom Ltd", + [3]byte{0, 20, 181}: "PHYSIOMETRIX,INC", + [3]byte{0, 20, 182}: "Enswer Technology Inc.", + [3]byte{0, 20, 183}: "AR Infotek Inc.", + [3]byte{0, 20, 184}: "Hill-Rom", + [3]byte{0, 20, 185}: "MSTAR SEMICONDUCTOR", + [3]byte{0, 20, 186}: "Carvers SA de CV", + [3]byte{0, 20, 187}: "Open Interface North America", + [3]byte{0, 20, 188}: "SYNECTIC TELECOM EXPORTS PVT. LTD.", + [3]byte{0, 20, 189}: "incNETWORKS, Inc", + [3]byte{0, 20, 190}: "Wink communication technology CO.LTD", + [3]byte{0, 20, 191}: "Cisco-Linksys, LLC", + [3]byte{0, 20, 192}: "Symstream Technology Group Ltd", + [3]byte{0, 20, 193}: "U.S. Robotics Corporation", + [3]byte{0, 20, 194}: "Hewlett Packard", + [3]byte{0, 20, 195}: "Seagate Technology", + [3]byte{0, 20, 196}: "Vitelcom Mobile Technology", + [3]byte{0, 20, 197}: "Alive Technologies Pty Ltd", + [3]byte{0, 20, 198}: "Quixant Ltd", + [3]byte{0, 20, 199}: "Nortel Networks", + [3]byte{0, 20, 200}: "Contemporary Research Corp", + [3]byte{0, 20, 201}: "Brocade Communications Systems, Inc.", + [3]byte{0, 20, 202}: "Key Radio Systems Limited", + [3]byte{0, 20, 203}: "LifeSync Corporation", + [3]byte{0, 20, 204}: "Zetec, Inc.", + [3]byte{0, 20, 205}: "DigitalZone Co., Ltd.", + [3]byte{0, 20, 206}: "NF CORPORATION", + [3]byte{0, 20, 207}: "INVISIO Communications", + [3]byte{0, 20, 208}: "BTI Systems Inc.", + [3]byte{0, 20, 209}: "TRENDnet, Inc.", + [3]byte{0, 20, 210}: "Kyuden Technosystems Corporation", + [3]byte{0, 20, 211}: "SEPSA", + [3]byte{0, 20, 212}: "K Technology Corporation", + [3]byte{0, 20, 213}: "Datang Telecom Technology CO. , LCD,Optical Communication Br", + [3]byte{0, 20, 214}: "Jeongmin Electronics Co.,Ltd.", + [3]byte{0, 20, 215}: "Datastore Technology Corp", + [3]byte{0, 20, 216}: "bio-logic SA", + [3]byte{0, 20, 217}: "IP Fabrics, Inc.", + [3]byte{0, 20, 218}: "Huntleigh Healthcare", + [3]byte{0, 20, 219}: "Elma Trenew Electronic GmbH", + [3]byte{0, 20, 220}: "Communication System Design & Manufacturing (CSDM)", + [3]byte{0, 20, 221}: "Covergence Inc.", + [3]byte{0, 20, 222}: "Sage Instruments Inc.", + [3]byte{0, 20, 223}: "HI-P Tech Corporation", + [3]byte{0, 20, 224}: "LET'S Corporation", + [3]byte{0, 20, 225}: "Data Display AG", + [3]byte{0, 20, 226}: "datacom systems inc.", + [3]byte{0, 20, 227}: "mm-lab GmbH", + [3]byte{0, 20, 228}: "infinias, LLC", + [3]byte{0, 20, 229}: "Alticast", + [3]byte{0, 20, 230}: "AIM Infrarotmodule GmbH", + [3]byte{0, 20, 231}: "Stolinx,. Inc", + [3]byte{0, 20, 232}: "ARRIS Group, Inc.", + [3]byte{0, 20, 233}: "Nortech International", + [3]byte{0, 20, 234}: "S Digm Inc. (Safe Paradigm Inc.)", + [3]byte{0, 20, 235}: "AwarePoint Corporation", + [3]byte{0, 20, 236}: "Acro Telecom", + [3]byte{0, 20, 237}: "Airak, Inc.", + [3]byte{0, 20, 238}: "Western Digital Technologies, Inc.", + [3]byte{0, 20, 239}: "TZero Technologies, Inc.", + [3]byte{0, 20, 240}: "Business Security OL AB", + [3]byte{0, 20, 241}: "Cisco Systems, Inc", + [3]byte{0, 20, 242}: "Cisco Systems, Inc", + [3]byte{0, 20, 243}: "ViXS Systems Inc", + [3]byte{0, 20, 244}: "DekTec Digital Video B.V.", + [3]byte{0, 20, 245}: "OSI Security Devices", + [3]byte{0, 20, 246}: "Juniper Networks", + [3]byte{0, 20, 247}: "CREVIS Co., LTD", + [3]byte{0, 20, 248}: "Scientific Atlanta", + [3]byte{0, 20, 249}: "Vantage Controls", + [3]byte{0, 20, 250}: "AsGa S.A.", + [3]byte{0, 20, 251}: "Technical Solutions Inc.", + [3]byte{0, 20, 252}: "Extandon, Inc.", + [3]byte{0, 20, 253}: "Thecus Technology Corp.", + [3]byte{0, 20, 254}: "Artech Electronics", + [3]byte{0, 20, 255}: "Precise Automation, Inc.", + [3]byte{0, 21, 0}: "Intel Corporate", + [3]byte{0, 21, 1}: "LexBox", + [3]byte{0, 21, 2}: "BETA tech", + [3]byte{0, 21, 3}: "PROFIcomms s.r.o.", + [3]byte{0, 21, 4}: "GAME PLUS CO., LTD.", + [3]byte{0, 21, 5}: "Actiontec Electronics, Inc", + [3]byte{0, 21, 6}: "Neo Photonics", + [3]byte{0, 21, 7}: "Renaissance Learning Inc", + [3]byte{0, 21, 8}: "Global Target Enterprise Inc", + [3]byte{0, 21, 9}: "Plus Technology Co., Ltd", + [3]byte{0, 21, 10}: "Sonoa Systems, Inc", + [3]byte{0, 21, 11}: "SAGE INFOTECH LTD.", + [3]byte{0, 21, 12}: "AVM GmbH", + [3]byte{0, 21, 13}: "Hoana Medical, Inc.", + [3]byte{0, 21, 14}: "OPENBRAIN TECHNOLOGIES CO., LTD.", + [3]byte{0, 21, 15}: "mingjong", + [3]byte{0, 21, 16}: "Techsphere Co., Ltd", + [3]byte{0, 21, 17}: "Data Center Systems", + [3]byte{0, 21, 18}: "Zurich University of Applied Sciences", + [3]byte{0, 21, 19}: "EFS sas", + [3]byte{0, 21, 20}: "Hu Zhou NAVA Networks&Electronics Ltd.", + [3]byte{0, 21, 21}: "Leipold+Co.GmbH", + [3]byte{0, 21, 22}: "URIEL SYSTEMS INC.", + [3]byte{0, 21, 23}: "Intel Corporate", + [3]byte{0, 21, 24}: "Shenzhen 10MOONS Technology Development CO.,Ltd", + [3]byte{0, 21, 25}: "StoreAge Networking Technologies", + [3]byte{0, 21, 26}: "Hunter Engineering Company", + [3]byte{0, 21, 27}: "Isilon Systems Inc.", + [3]byte{0, 21, 28}: "LENECO", + [3]byte{0, 21, 29}: "M2I CORPORATION", + [3]byte{0, 21, 30}: "Ethernet Powerlink Standardization Group (EPSG)", + [3]byte{0, 21, 31}: "Multivision Intelligent Surveillance (Hong Kong) Ltd", + [3]byte{0, 21, 32}: "Radiocrafts AS", + [3]byte{0, 21, 33}: "Horoquartz", + [3]byte{0, 21, 34}: "Dea Security", + [3]byte{0, 21, 35}: "Meteor Communications Corporation", + [3]byte{0, 21, 36}: "Numatics, Inc.", + [3]byte{0, 21, 37}: "Chamberlain Access Solutions", + [3]byte{0, 21, 38}: "Remote Technologies Inc", + [3]byte{0, 21, 39}: "Balboa Instruments", + [3]byte{0, 21, 40}: "Beacon Medical Products LLC d.b.a. BeaconMedaes", + [3]byte{0, 21, 41}: "N3 Corporation", + [3]byte{0, 21, 42}: "Nokia GmbH", + [3]byte{0, 21, 43}: "Cisco Systems, Inc", + [3]byte{0, 21, 44}: "Cisco Systems, Inc", + [3]byte{0, 21, 45}: "TenX Networks, LLC", + [3]byte{0, 21, 46}: "PacketHop, Inc.", + [3]byte{0, 21, 47}: "ARRIS Group, Inc.", + [3]byte{0, 21, 48}: "EMC Corporation", + [3]byte{0, 21, 49}: "KOCOM", + [3]byte{0, 21, 50}: "Consumer Technologies Group, LLC", + [3]byte{0, 21, 51}: "NADAM.CO.,LTD", + [3]byte{0, 21, 52}: "A Beltrónica-Companhia de Comunicações, Lda", + [3]byte{0, 21, 53}: "OTE Spa", + [3]byte{0, 21, 54}: "Powertech co.,Ltd", + [3]byte{0, 21, 55}: "Ventus Networks", + [3]byte{0, 21, 56}: "RFID, Inc.", + [3]byte{0, 21, 57}: "Technodrive srl", + [3]byte{0, 21, 58}: "Shenzhen Syscan Technology Co.,Ltd.", + [3]byte{0, 21, 59}: "EMH metering GmbH & Co. KG", + [3]byte{0, 21, 60}: "Kprotech Co., Ltd.", + [3]byte{0, 21, 61}: "ELIM PRODUCT CO.", + [3]byte{0, 21, 62}: "Q-Matic Sweden AB", + [3]byte{0, 21, 63}: "Alcatel Alenia Space Italia", + [3]byte{0, 21, 64}: "Nortel Networks", + [3]byte{0, 21, 65}: "StrataLight Communications, Inc.", + [3]byte{0, 21, 66}: "MICROHARD S.R.L.", + [3]byte{0, 21, 67}: "Aberdeen Test Center", + [3]byte{0, 21, 68}: "coM.s.a.t. AG", + [3]byte{0, 21, 69}: "SEECODE Co., Ltd.", + [3]byte{0, 21, 70}: "ITG Worldwide Sdn Bhd", + [3]byte{0, 21, 71}: "AiZen Solutions Inc.", + [3]byte{0, 21, 72}: "CUBE TECHNOLOGIES", + [3]byte{0, 21, 73}: "Dixtal Biomedica Ind. Com. Ltda", + [3]byte{0, 21, 74}: "WANSHIH ELECTRONIC CO., LTD", + [3]byte{0, 21, 75}: "Wonde Proud Technology Co., Ltd", + [3]byte{0, 21, 76}: "Saunders Electronics", + [3]byte{0, 21, 77}: "Netronome Systems, Inc.", + [3]byte{0, 21, 78}: "IEC", + [3]byte{0, 21, 79}: "one RF Technology", + [3]byte{0, 21, 80}: "Nits Technology Inc", + [3]byte{0, 21, 81}: "RadioPulse Inc.", + [3]byte{0, 21, 82}: "Wi-Gear Inc.", + [3]byte{0, 21, 83}: "Cytyc Corporation", + [3]byte{0, 21, 84}: "Atalum Wireless S.A.", + [3]byte{0, 21, 85}: "DFM GmbH", + [3]byte{0, 21, 86}: "Sagemcom Broadband SAS", + [3]byte{0, 21, 87}: "Olivetti", + [3]byte{0, 21, 88}: "FOXCONN", + [3]byte{0, 21, 89}: "Securaplane Technologies, Inc.", + [3]byte{0, 21, 90}: "DAINIPPON PHARMACEUTICAL CO., LTD.", + [3]byte{0, 21, 91}: "Sampo Corporation", + [3]byte{0, 21, 92}: "Dresser Wayne", + [3]byte{0, 21, 93}: "Microsoft Corporation", + [3]byte{0, 21, 94}: "Morgan Stanley", + [3]byte{0, 21, 95}: "GreenPeak Technologies", + [3]byte{0, 21, 96}: "Hewlett Packard", + [3]byte{0, 21, 97}: "JJPlus Corporation", + [3]byte{0, 21, 98}: "Cisco Systems, Inc", + [3]byte{0, 21, 99}: "Cisco Systems, Inc", + [3]byte{0, 21, 100}: "BEHRINGER Spezielle Studiotechnik GmbH", + [3]byte{0, 21, 101}: "XIAMEN YEALINK NETWORK TECHNOLOGY CO.,LTD", + [3]byte{0, 21, 102}: "A-First Technology Co., Ltd.", + [3]byte{0, 21, 103}: "RADWIN Inc.", + [3]byte{0, 21, 104}: "Dilithium Networks", + [3]byte{0, 21, 105}: "PECO II, Inc.", + [3]byte{0, 21, 106}: "DG2L Technologies Pvt. Ltd.", + [3]byte{0, 21, 107}: "Perfisans Networks Corp.", + [3]byte{0, 21, 108}: "SANE SYSTEM CO., LTD", + [3]byte{0, 21, 109}: "Ubiquiti Networks Inc.", + [3]byte{0, 21, 110}: "A. W. Communication Systems Ltd", + [3]byte{0, 21, 111}: "Xiranet Communications GmbH", + [3]byte{0, 21, 112}: "Zebra Technologies Inc", + [3]byte{0, 21, 113}: "Nolan Systems", + [3]byte{0, 21, 114}: "Red-Lemon", + [3]byte{0, 21, 115}: "NewSoft Technology Corporation", + [3]byte{0, 21, 116}: "Horizon Semiconductors Ltd.", + [3]byte{0, 21, 117}: "Nevis Networks Inc.", + [3]byte{0, 21, 118}: "LABiTec - Labor Biomedical Technologies GmbH", + [3]byte{0, 21, 119}: "Allied Telesis, Inc.", + [3]byte{0, 21, 120}: "Audio / Video Innovations", + [3]byte{0, 21, 121}: "Lunatone Industrielle Elektronik GmbH", + [3]byte{0, 21, 122}: "Telefin S.p.A.", + [3]byte{0, 21, 123}: "Leuze electronic GmbH + Co. KG", + [3]byte{0, 21, 124}: "Dave Networks, Inc.", + [3]byte{0, 21, 125}: "POSDATA", + [3]byte{0, 21, 126}: "Weidmüller Interface GmbH & Co. KG", + [3]byte{0, 21, 127}: "ChuanG International Holding CO.,LTD.", + [3]byte{0, 21, 128}: "U-WAY CORPORATION", + [3]byte{0, 21, 129}: "MAKUS Inc.", + [3]byte{0, 21, 130}: "Pulse Eight Limited", + [3]byte{0, 21, 131}: "IVT corporation", + [3]byte{0, 21, 132}: "Schenck Process GmbH", + [3]byte{0, 21, 133}: "Aonvision Technolopy Corp.", + [3]byte{0, 21, 134}: "Xiamen Overseas Chinese Electronic Co., Ltd.", + [3]byte{0, 21, 135}: "Takenaka Seisakusho Co.,Ltd", + [3]byte{0, 21, 136}: "Salutica Allied Solutions Sdn Bhd", + [3]byte{0, 21, 137}: "D-MAX Technology Co.,Ltd", + [3]byte{0, 21, 138}: "SURECOM Technology Corp.", + [3]byte{0, 21, 139}: "Park Air Systems Ltd", + [3]byte{0, 21, 140}: "Liab ApS", + [3]byte{0, 21, 141}: "Jennic Ltd", + [3]byte{0, 21, 142}: "Plustek.INC", + [3]byte{0, 21, 143}: "NTT Advanced Technology Corporation", + [3]byte{0, 21, 144}: "Hectronic GmbH", + [3]byte{0, 21, 145}: "RLW Inc.", + [3]byte{0, 21, 146}: "Facom UK Ltd (Melksham)", + [3]byte{0, 21, 147}: "U4EA Technologies Inc.", + [3]byte{0, 21, 148}: "BIXOLON CO.,LTD", + [3]byte{0, 21, 149}: "Quester Tangent Corporation", + [3]byte{0, 21, 150}: "ARRIS Group, Inc.", + [3]byte{0, 21, 151}: "AETA AUDIO SYSTEMS", + [3]byte{0, 21, 152}: "Kolektor group", + [3]byte{0, 21, 153}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 21, 154}: "ARRIS Group, Inc.", + [3]byte{0, 21, 155}: "Nortel Networks", + [3]byte{0, 21, 156}: "B-KYUNG SYSTEM Co.,Ltd.", + [3]byte{0, 21, 157}: "Tripp Lite ", + [3]byte{0, 21, 158}: "Mad Catz Interactive Inc", + [3]byte{0, 21, 159}: "Terascala, Inc.", + [3]byte{0, 21, 160}: "Nokia Danmark A/S", + [3]byte{0, 21, 161}: "ECA-SINTERS", + [3]byte{0, 21, 162}: "ARRIS Group, Inc.", + [3]byte{0, 21, 163}: "ARRIS Group, Inc.", + [3]byte{0, 21, 164}: "ARRIS Group, Inc.", + [3]byte{0, 21, 165}: "DCI Co., Ltd.", + [3]byte{0, 21, 166}: "Digital Electronics Products Ltd.", + [3]byte{0, 21, 167}: "Robatech AG", + [3]byte{0, 21, 168}: "ARRIS Group, Inc.", + [3]byte{0, 21, 169}: "KWANG WOO I&C CO.,LTD", + [3]byte{0, 21, 170}: "Rextechnik International Co.,", + [3]byte{0, 21, 171}: "PRO CO SOUND INC", + [3]byte{0, 21, 172}: "Capelon AB", + [3]byte{0, 21, 173}: "Accedian Networks", + [3]byte{0, 21, 174}: "kyung il", + [3]byte{0, 21, 175}: "AzureWave Technology Inc.", + [3]byte{0, 21, 176}: "AUTOTELENET CO.,LTD", + [3]byte{0, 21, 177}: "Ambient Corporation", + [3]byte{0, 21, 178}: "Advanced Industrial Computer, Inc.", + [3]byte{0, 21, 179}: "Caretech AB", + [3]byte{0, 21, 180}: "Polymap Wireless LLC", + [3]byte{0, 21, 181}: "CI Network Corp.", + [3]byte{0, 21, 182}: "ShinMaywa Industries, Ltd.", + [3]byte{0, 21, 183}: "Toshiba", + [3]byte{0, 21, 184}: "Tahoe", + [3]byte{0, 21, 185}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 21, 186}: "iba AG", + [3]byte{0, 21, 187}: "SMA Solar Technology AG", + [3]byte{0, 21, 188}: "Develco", + [3]byte{0, 21, 189}: "Group 4 Technology Ltd", + [3]byte{0, 21, 190}: "Iqua Ltd.", + [3]byte{0, 21, 191}: "technicob", + [3]byte{0, 21, 192}: "DIGITAL TELEMEDIA CO.,LTD.", + [3]byte{0, 21, 193}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 21, 194}: "3M Germany", + [3]byte{0, 21, 195}: "Ruf Telematik AG", + [3]byte{0, 21, 196}: "FLOVEL CO., LTD.", + [3]byte{0, 21, 197}: "Dell Inc.", + [3]byte{0, 21, 198}: "Cisco Systems, Inc", + [3]byte{0, 21, 199}: "Cisco Systems, Inc", + [3]byte{0, 21, 200}: "FlexiPanel Ltd", + [3]byte{0, 21, 201}: "Gumstix, Inc", + [3]byte{0, 21, 202}: "TeraRecon, Inc.", + [3]byte{0, 21, 203}: "Surf Communication Solutions Ltd.", + [3]byte{0, 21, 204}: "UQUEST, LTD.", + [3]byte{0, 21, 205}: "Exartech International Corp.", + [3]byte{0, 21, 206}: "ARRIS Group, Inc.", + [3]byte{0, 21, 207}: "ARRIS Group, Inc.", + [3]byte{0, 21, 208}: "ARRIS Group, Inc.", + [3]byte{0, 21, 209}: "ARRIS Group, Inc.", + [3]byte{0, 21, 210}: "Xantech Corporation", + [3]byte{0, 21, 211}: "Pantech&Curitel Communications, Inc.", + [3]byte{0, 21, 212}: "Emitor AB", + [3]byte{0, 21, 213}: "NICEVT", + [3]byte{0, 21, 214}: "OSLiNK Sp. z o.o.", + [3]byte{0, 21, 215}: "Reti Corporation", + [3]byte{0, 21, 216}: "Interlink Electronics", + [3]byte{0, 21, 217}: "PKC Electronics Oy", + [3]byte{0, 21, 218}: "IRITEL A.D.", + [3]byte{0, 21, 219}: "Canesta Inc.", + [3]byte{0, 21, 220}: "KT&C Co., Ltd.", + [3]byte{0, 21, 221}: "IP Control Systems Ltd.", + [3]byte{0, 21, 222}: "Nokia Danmark A/S", + [3]byte{0, 21, 223}: "Clivet S.p.A.", + [3]byte{0, 21, 224}: "Ericsson", + [3]byte{0, 21, 225}: "Picochip Ltd", + [3]byte{0, 21, 226}: "Dr.Ing. Herbert Knauer GmbH", + [3]byte{0, 21, 227}: "Dream Technologies Corporation", + [3]byte{0, 21, 228}: "Zimmer Elektromedizin", + [3]byte{0, 21, 229}: "Cheertek Inc.", + [3]byte{0, 21, 230}: "MOBILE TECHNIKA Inc.", + [3]byte{0, 21, 231}: "Quantec Tontechnik", + [3]byte{0, 21, 232}: "Nortel Networks", + [3]byte{0, 21, 233}: "D-Link Corporation", + [3]byte{0, 21, 234}: "Tellumat (Pty) Ltd", + [3]byte{0, 21, 235}: "zte corporation", + [3]byte{0, 21, 236}: "Boca Devices LLC", + [3]byte{0, 21, 237}: "Fulcrum Microsystems, Inc.", + [3]byte{0, 21, 238}: "Omnex Control Systems", + [3]byte{0, 21, 239}: "NEC TOKIN Corporation", + [3]byte{0, 21, 240}: "EGO BV", + [3]byte{0, 21, 241}: "KYLINK Communications Corp.", + [3]byte{0, 21, 242}: "ASUSTek COMPUTER INC.", + [3]byte{0, 21, 243}: "PELTOR AB", + [3]byte{0, 21, 244}: "Eventide", + [3]byte{0, 21, 245}: "Sustainable Energy Systems", + [3]byte{0, 21, 246}: "SCIENCE AND ENGINEERING SERVICES, INC.", + [3]byte{0, 21, 247}: "Wintecronics Ltd.", + [3]byte{0, 21, 248}: "Kingtronics Industrial Co. Ltd.", + [3]byte{0, 21, 249}: "Cisco Systems, Inc", + [3]byte{0, 21, 250}: "Cisco Systems, Inc", + [3]byte{0, 21, 251}: "setex schermuly textile computer gmbh", + [3]byte{0, 21, 252}: "Littelfuse Startco", + [3]byte{0, 21, 253}: "Complete Media Systems", + [3]byte{0, 21, 254}: "SCHILLING ROBOTICS LLC", + [3]byte{0, 21, 255}: "Novatel Wireless Solutions, Inc.", + [3]byte{0, 22, 0}: "CelleBrite Mobile Synchronization", + [3]byte{0, 22, 1}: "BUFFALO.INC", + [3]byte{0, 22, 2}: "CEYON TECHNOLOGY CO.,LTD.", + [3]byte{0, 22, 3}: "COOLKSKY Co., LTD", + [3]byte{0, 22, 4}: "Sigpro", + [3]byte{0, 22, 5}: "YORKVILLE SOUND INC.", + [3]byte{0, 22, 6}: "Ideal Industries", + [3]byte{0, 22, 7}: "Curves International Inc.", + [3]byte{0, 22, 8}: "Sequans Communications", + [3]byte{0, 22, 9}: "Unitech electronics co., ltd.", + [3]byte{0, 22, 10}: "SWEEX Europe BV", + [3]byte{0, 22, 11}: "TVWorks LLC", + [3]byte{0, 22, 12}: "LPL DEVELOPMENT S.A. DE C.V", + [3]byte{0, 22, 13}: "Be Here Corporation", + [3]byte{0, 22, 14}: "Optica Technologies Inc.", + [3]byte{0, 22, 15}: "BADGER METER INC", + [3]byte{0, 22, 16}: "Carina Technology", + [3]byte{0, 22, 17}: "Altecon Srl", + [3]byte{0, 22, 18}: "Otsuka Electronics Co., Ltd.", + [3]byte{0, 22, 19}: "LibreStream Technologies Inc.", + [3]byte{0, 22, 20}: "Picosecond Pulse Labs", + [3]byte{0, 22, 21}: "Nittan Company, Limited", + [3]byte{0, 22, 22}: "BROWAN COMMUNICATION INC.", + [3]byte{0, 22, 23}: "MSI", + [3]byte{0, 22, 24}: "HIVION Co., Ltd.", + [3]byte{0, 22, 25}: "Lancelan Technologies S.L.", + [3]byte{0, 22, 26}: "Dametric AB", + [3]byte{0, 22, 27}: "Micronet Corporation", + [3]byte{0, 22, 28}: "e:cue", + [3]byte{0, 22, 29}: "Innovative Wireless Technologies, Inc.", + [3]byte{0, 22, 30}: "Woojinnet", + [3]byte{0, 22, 31}: "SUNWAVETEC Co., Ltd.", + [3]byte{0, 22, 32}: "Sony Mobile Communications AB", + [3]byte{0, 22, 33}: "Colorado Vnet", + [3]byte{0, 22, 34}: "BBH SYSTEMS GMBH", + [3]byte{0, 22, 35}: "Interval Media", + [3]byte{0, 22, 36}: "Teneros, Inc.", + [3]byte{0, 22, 37}: "Impinj, Inc.", + [3]byte{0, 22, 38}: "ARRIS Group, Inc.", + [3]byte{0, 22, 39}: "embedded-logic DESIGN AND MORE GmbH", + [3]byte{0, 22, 40}: "Magicard Ltd", + [3]byte{0, 22, 41}: "Nivus GmbH", + [3]byte{0, 22, 42}: "Antik computers & communications s.r.o.", + [3]byte{0, 22, 43}: "Togami Electric Mfg.co.,Ltd.", + [3]byte{0, 22, 44}: "Xanboo", + [3]byte{0, 22, 45}: "STNet Co., Ltd.", + [3]byte{0, 22, 46}: "Space Shuttle Hi-Tech Co., Ltd.", + [3]byte{0, 22, 47}: "Geutebrück GmbH", + [3]byte{0, 22, 48}: "Vativ Technologies", + [3]byte{0, 22, 49}: "Xteam", + [3]byte{0, 22, 50}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 22, 51}: "Oxford Diagnostics Ltd.", + [3]byte{0, 22, 52}: "Mathtech, Inc.", + [3]byte{0, 22, 53}: "Hewlett Packard", + [3]byte{0, 22, 54}: "QUANTA COMPUTER INC.", + [3]byte{0, 22, 55}: "CITEL SpA", + [3]byte{0, 22, 56}: "TECOM Co., Ltd.", + [3]byte{0, 22, 57}: "Ubiquam Co., Ltd.", + [3]byte{0, 22, 58}: "YVES TECHNOLOGY CO., LTD.", + [3]byte{0, 22, 59}: "VertexRSI/General Dynamics", + [3]byte{0, 22, 60}: "Rebox B.V.", + [3]byte{0, 22, 61}: "Tsinghua Tongfang Legend Silicon Tech. Co., Ltd.", + [3]byte{0, 22, 62}: "Xensource, Inc.", + [3]byte{0, 22, 63}: "CReTE SYSTEMS Inc.", + [3]byte{0, 22, 64}: "Asmobile Communication Inc.", + [3]byte{0, 22, 65}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{0, 22, 66}: "Pangolin", + [3]byte{0, 22, 67}: "Sunhillo Corporation", + [3]byte{0, 22, 68}: "LITE-ON Technology Corp.", + [3]byte{0, 22, 69}: "Power Distribution, Inc.", + [3]byte{0, 22, 70}: "Cisco Systems, Inc", + [3]byte{0, 22, 71}: "Cisco Systems, Inc", + [3]byte{0, 22, 72}: "SSD Company Limited", + [3]byte{0, 22, 73}: "SetOne GmbH", + [3]byte{0, 22, 74}: "Vibration Technology Limited", + [3]byte{0, 22, 75}: "Quorion Data Systems GmbH", + [3]byte{0, 22, 76}: "PLANET INT Co., Ltd", + [3]byte{0, 22, 77}: "Alcatel-Lucent IPD", + [3]byte{0, 22, 78}: "Nokia Danmark A/S", + [3]byte{0, 22, 79}: "World Ethnic Broadcastin Inc.", + [3]byte{0, 22, 80}: "Kratos EPD ", + [3]byte{0, 22, 81}: "Exeo Systems", + [3]byte{0, 22, 82}: "Hoatech Technologies, Inc.", + [3]byte{0, 22, 83}: "LEGO System A/S IE Electronics Division", + [3]byte{0, 22, 84}: "Flex-P Industries Sdn. Bhd.", + [3]byte{0, 22, 85}: "FUHO TECHNOLOGY Co., LTD", + [3]byte{0, 22, 86}: "Nintendo Co., Ltd.", + [3]byte{0, 22, 87}: "Aegate Ltd", + [3]byte{0, 22, 88}: "Fusiontech Technologies Inc.", + [3]byte{0, 22, 89}: "Z.M.P. RADWAG", + [3]byte{0, 22, 90}: "Harman Specialty Group", + [3]byte{0, 22, 91}: "Grip Audio", + [3]byte{0, 22, 92}: "Trackflow Ltd", + [3]byte{0, 22, 93}: "AirDefense, Inc.", + [3]byte{0, 22, 94}: "Precision I/O", + [3]byte{0, 22, 95}: "Fairmount Automation", + [3]byte{0, 22, 96}: "Nortel Networks", + [3]byte{0, 22, 97}: "Novatium Solutions (P) Ltd", + [3]byte{0, 22, 98}: "Liyuh Technology Ltd.", + [3]byte{0, 22, 99}: "KBT Mobile", + [3]byte{0, 22, 100}: "Prod-El SpA", + [3]byte{0, 22, 101}: "Cellon France", + [3]byte{0, 22, 102}: "Quantier Communication Inc.", + [3]byte{0, 22, 103}: "A-TEC Subsystem INC.", + [3]byte{0, 22, 104}: "Eishin Electronics", + [3]byte{0, 22, 105}: "MRV Communication (Networks) LTD", + [3]byte{0, 22, 106}: "TPS", + [3]byte{0, 22, 107}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 22, 108}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 22, 109}: "Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd", + [3]byte{0, 22, 110}: "Arbitron Inc.", + [3]byte{0, 22, 111}: "Intel Corporate", + [3]byte{0, 22, 112}: "SKNET Corporation", + [3]byte{0, 22, 113}: "Symphox Information Co.", + [3]byte{0, 22, 114}: "Zenway enterprise ltd", + [3]byte{0, 22, 115}: "Bury GmbH & Co. KG", + [3]byte{0, 22, 116}: "EuroCB (Phils.), Inc.", + [3]byte{0, 22, 117}: "ARRIS Group, Inc.", + [3]byte{0, 22, 118}: "Intel Corporate", + [3]byte{0, 22, 119}: "Bihl + Wiedemann GmbH", + [3]byte{0, 22, 120}: "SHENZHEN BAOAN GAOKE ELECTRONICS CO., LTD", + [3]byte{0, 22, 121}: "eOn Communications", + [3]byte{0, 22, 122}: "Skyworth Overseas Development Ltd.", + [3]byte{0, 22, 123}: "Haver&Boecker", + [3]byte{0, 22, 124}: "iRex Technologies BV", + [3]byte{0, 22, 125}: "Sky-Line Information Co., Ltd.", + [3]byte{0, 22, 126}: "DIBOSS.CO.,LTD", + [3]byte{0, 22, 127}: "Bluebird Soft Inc.", + [3]byte{0, 22, 128}: "Bally Gaming + Systems", + [3]byte{0, 22, 129}: "Vector Informatik GmbH", + [3]byte{0, 22, 130}: "Pro Dex, Inc", + [3]byte{0, 22, 131}: "WEBIO International Co.,.Ltd.", + [3]byte{0, 22, 132}: "Donjin Co.,Ltd.", + [3]byte{0, 22, 133}: "Elisa Oyj", + [3]byte{0, 22, 134}: "Karl Storz Imaging", + [3]byte{0, 22, 135}: "Chubb CSC-Vendor AP", + [3]byte{0, 22, 136}: "ServerEngines LLC", + [3]byte{0, 22, 137}: "Pilkor Electronics Co., Ltd", + [3]byte{0, 22, 138}: "id-Confirm Inc", + [3]byte{0, 22, 139}: "Paralan Corporation", + [3]byte{0, 22, 140}: "DSL Partner AS", + [3]byte{0, 22, 141}: "KORWIN CO., Ltd.", + [3]byte{0, 22, 142}: "Vimicro corporation", + [3]byte{0, 22, 143}: "GN Netcom A/S", + [3]byte{0, 22, 144}: "J-TEK INCORPORATION", + [3]byte{0, 22, 145}: "Moser-Baer AG", + [3]byte{0, 22, 146}: "Scientific-Atlanta, Inc.", + [3]byte{0, 22, 147}: "PowerLink Technology Inc.", + [3]byte{0, 22, 148}: "Sennheiser Communications A/S", + [3]byte{0, 22, 149}: "AVC Technology (International) Limited", + [3]byte{0, 22, 150}: "QDI Technology (H.K.) Limited", + [3]byte{0, 22, 151}: "NEC Corporation", + [3]byte{0, 22, 152}: "T&A Mobile Phones", + [3]byte{0, 22, 153}: "Tonic DVB Marketing Ltd", + [3]byte{0, 22, 154}: "Quadrics Ltd", + [3]byte{0, 22, 155}: "Alstom Transport", + [3]byte{0, 22, 156}: "Cisco Systems, Inc", + [3]byte{0, 22, 157}: "Cisco Systems, Inc", + [3]byte{0, 22, 158}: "TV One Ltd", + [3]byte{0, 22, 159}: "Vimtron Electronics Co., Ltd.", + [3]byte{0, 22, 160}: "Auto-Maskin", + [3]byte{0, 22, 161}: "3Leaf Networks", + [3]byte{0, 22, 162}: "CentraLite Systems, Inc.", + [3]byte{0, 22, 163}: "Ingeteam Transmission&Distribution, S.A.", + [3]byte{0, 22, 164}: "Ezurio Ltd", + [3]byte{0, 22, 165}: "Tandberg Storage ASA", + [3]byte{0, 22, 166}: "Dovado FZ-LLC", + [3]byte{0, 22, 167}: "AWETA G&P", + [3]byte{0, 22, 168}: "CWT CO., LTD.", + [3]byte{0, 22, 169}: "2EI", + [3]byte{0, 22, 170}: "Kei Communication Technology Inc.", + [3]byte{0, 22, 171}: "Dansensor A/S", + [3]byte{0, 22, 172}: "Toho Technology Corp.", + [3]byte{0, 22, 173}: "BT-Links Company Limited", + [3]byte{0, 22, 174}: "INVENTEL", + [3]byte{0, 22, 175}: "Shenzhen Union Networks Equipment Co.,Ltd.", + [3]byte{0, 22, 176}: "VK Corporation", + [3]byte{0, 22, 177}: "KBS", + [3]byte{0, 22, 178}: "DriveCam Inc", + [3]byte{0, 22, 179}: "Photonicbridges (China) Co., Ltd.", + [3]byte{0, 22, 180}: "Private", + [3]byte{0, 22, 181}: "ARRIS Group, Inc.", + [3]byte{0, 22, 182}: "Cisco-Linksys, LLC", + [3]byte{0, 22, 183}: "Seoul Commtech", + [3]byte{0, 22, 184}: "Sony Mobile Communications AB", + [3]byte{0, 22, 185}: "ProCurve Networking by HP", + [3]byte{0, 22, 186}: "WEATHERNEWS INC.", + [3]byte{0, 22, 187}: "Law-Chain Computer Technology Co Ltd", + [3]byte{0, 22, 188}: "Nokia Danmark A/S", + [3]byte{0, 22, 189}: "ATI Industrial Automation", + [3]byte{0, 22, 190}: "INFRANET, Inc.", + [3]byte{0, 22, 191}: "PaloDEx Group Oy", + [3]byte{0, 22, 192}: "Semtech Corporation", + [3]byte{0, 22, 193}: "Eleksen Ltd", + [3]byte{0, 22, 194}: "Avtec Systems Inc", + [3]byte{0, 22, 195}: "BA Systems Inc", + [3]byte{0, 22, 196}: "SiRF Technology, Inc.", + [3]byte{0, 22, 197}: "Shenzhen Xing Feng Industry Co.,Ltd", + [3]byte{0, 22, 198}: "North Atlantic Industries", + [3]byte{0, 22, 199}: "Cisco Systems, Inc", + [3]byte{0, 22, 200}: "Cisco Systems, Inc", + [3]byte{0, 22, 201}: "NAT Seattle, Inc.", + [3]byte{0, 22, 202}: "Nortel Networks", + [3]byte{0, 22, 203}: "Apple, Inc.", + [3]byte{0, 22, 204}: "Xcute Mobile Corp.", + [3]byte{0, 22, 205}: "HIJI HIGH-TECH CO., LTD.", + [3]byte{0, 22, 206}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 22, 207}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 22, 208}: "ATech elektronika d.o.o.", + [3]byte{0, 22, 209}: "ZAT a.s.", + [3]byte{0, 22, 210}: "Caspian", + [3]byte{0, 22, 211}: "Wistron Corporation", + [3]byte{0, 22, 212}: "Compal Communications, Inc.", + [3]byte{0, 22, 213}: "Synccom Co., Ltd", + [3]byte{0, 22, 214}: "TDA Tech Pty Ltd", + [3]byte{0, 22, 215}: "Sunways AG", + [3]byte{0, 22, 216}: "Senea AB", + [3]byte{0, 22, 217}: "NINGBO BIRD CO.,LTD.", + [3]byte{0, 22, 218}: "Futronic Technology Co. Ltd.", + [3]byte{0, 22, 219}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 22, 220}: "ARCHOS", + [3]byte{0, 22, 221}: "Gigabeam Corporation", + [3]byte{0, 22, 222}: "FAST Inc", + [3]byte{0, 22, 223}: "Lundinova AB", + [3]byte{0, 22, 224}: "3Com Ltd", + [3]byte{0, 22, 225}: "SiliconStor, Inc.", + [3]byte{0, 22, 226}: "American Fibertek, Inc.", + [3]byte{0, 22, 227}: "ASKEY COMPUTER CORP", + [3]byte{0, 22, 228}: "VANGUARD SECURITY ENGINEERING CORP.", + [3]byte{0, 22, 229}: "FORDLEY DEVELOPMENT LIMITED", + [3]byte{0, 22, 230}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{0, 22, 231}: "Dynamix Promotions Limited", + [3]byte{0, 22, 232}: "Sigma Designs, Inc.", + [3]byte{0, 22, 233}: "Tiba Medical Inc", + [3]byte{0, 22, 234}: "Intel Corporate", + [3]byte{0, 22, 235}: "Intel Corporate", + [3]byte{0, 22, 236}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 22, 237}: "Digital Safety Technologies, Inc", + [3]byte{0, 22, 238}: "Royaldigital Inc.", + [3]byte{0, 22, 239}: "Koko Fitness, Inc.", + [3]byte{0, 22, 240}: "Dell", + [3]byte{0, 22, 241}: "OmniSense, LLC", + [3]byte{0, 22, 242}: "Dmobile System Co., Ltd.", + [3]byte{0, 22, 243}: "CAST Information Co., Ltd", + [3]byte{0, 22, 244}: "Eidicom Co., Ltd.", + [3]byte{0, 22, 245}: "Dalian Golden Hualu Digital Technology Co.,Ltd", + [3]byte{0, 22, 246}: "Video Products Group", + [3]byte{0, 22, 247}: "L-3 Communications, Aviation Recorders", + [3]byte{0, 22, 248}: "AVIQTECH TECHNOLOGY CO., LTD.", + [3]byte{0, 22, 249}: "CETRTA POT, d.o.o., Kranj", + [3]byte{0, 22, 250}: "ECI Telecom Ltd.", + [3]byte{0, 22, 251}: "SHENZHEN MTC CO LTD", + [3]byte{0, 22, 252}: "TOHKEN CO.,LTD.", + [3]byte{0, 22, 253}: "Jaty Electronics", + [3]byte{0, 22, 254}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 22, 255}: "Wamin Optocomm Mfg Corp", + [3]byte{0, 23, 0}: "ARRIS Group, Inc.", + [3]byte{0, 23, 1}: "KDE, Inc.", + [3]byte{0, 23, 2}: "Osung Midicom Co., Ltd", + [3]byte{0, 23, 3}: "MOSDAN Internation Co.,Ltd", + [3]byte{0, 23, 4}: "Shinco Electronics Group Co.,Ltd", + [3]byte{0, 23, 5}: "Methode Electronics", + [3]byte{0, 23, 6}: "Techfaithwireless Communication Technology Limited.", + [3]byte{0, 23, 7}: "InGrid, Inc", + [3]byte{0, 23, 8}: "Hewlett Packard", + [3]byte{0, 23, 9}: "Exalt Communications", + [3]byte{0, 23, 10}: "INEW DIGITAL COMPANY", + [3]byte{0, 23, 11}: "Contela, Inc.", + [3]byte{0, 23, 12}: "Twig Com Ltd.", + [3]byte{0, 23, 13}: "Dust Networks Inc.", + [3]byte{0, 23, 14}: "Cisco Systems, Inc", + [3]byte{0, 23, 15}: "Cisco Systems, Inc", + [3]byte{0, 23, 16}: "Casa Systems Inc.", + [3]byte{0, 23, 17}: "GE Healthcare Bio-Sciences AB", + [3]byte{0, 23, 18}: "ISCO International", + [3]byte{0, 23, 19}: "Tiger NetCom", + [3]byte{0, 23, 20}: "BR Controls Nederland bv", + [3]byte{0, 23, 21}: "Qstik", + [3]byte{0, 23, 22}: "Qno Technology Inc.", + [3]byte{0, 23, 23}: "Leica Geosystems AG", + [3]byte{0, 23, 24}: "Vansco Electronics Oy", + [3]byte{0, 23, 25}: "Audiocodes USA, Inc", + [3]byte{0, 23, 26}: "Winegard Company", + [3]byte{0, 23, 27}: "Innovation Lab Corp.", + [3]byte{0, 23, 28}: "NT MicroSystems, Inc.", + [3]byte{0, 23, 29}: "DIGIT", + [3]byte{0, 23, 30}: "Theo Benning GmbH & Co. KG", + [3]byte{0, 23, 31}: "IMV Corporation", + [3]byte{0, 23, 32}: "Image Sensing Systems, Inc.", + [3]byte{0, 23, 33}: "FITRE S.p.A.", + [3]byte{0, 23, 34}: "Hanazeder Electronic GmbH", + [3]byte{0, 23, 35}: "Summit Data Communications", + [3]byte{0, 23, 36}: "Studer Professional Audio GmbH", + [3]byte{0, 23, 37}: "Liquid Computing", + [3]byte{0, 23, 38}: "m2c Electronic Technology Ltd.", + [3]byte{0, 23, 39}: "Thermo Ramsey Italia s.r.l.", + [3]byte{0, 23, 40}: "Selex Communications", + [3]byte{0, 23, 41}: "Ubicod Co.LTD", + [3]byte{0, 23, 42}: "Proware Technology Corp.(By Unifosa)", + [3]byte{0, 23, 43}: "Global Technologies Inc.", + [3]byte{0, 23, 44}: "TAEJIN INFOTECH", + [3]byte{0, 23, 45}: "Axcen Photonics Corporation", + [3]byte{0, 23, 46}: "FXC Inc.", + [3]byte{0, 23, 47}: "NeuLion Incorporated", + [3]byte{0, 23, 48}: "Automation Electronics", + [3]byte{0, 23, 49}: "ASUSTek COMPUTER INC.", + [3]byte{0, 23, 50}: "Science-Technical Center RISSA", + [3]byte{0, 23, 51}: "SFR", + [3]byte{0, 23, 52}: "ADC Telecommunications", + [3]byte{0, 23, 53}: "Intel Wireless Network Group", + [3]byte{0, 23, 54}: "iiTron Inc.", + [3]byte{0, 23, 55}: "Industrie Dial Face S.p.A.", + [3]byte{0, 23, 56}: "International Business Machines", + [3]byte{0, 23, 57}: "Bright Headphone Electronics Company", + [3]byte{0, 23, 58}: "Reach Systems Inc.", + [3]byte{0, 23, 59}: "Cisco Systems, Inc", + [3]byte{0, 23, 60}: "Extreme Engineering Solutions", + [3]byte{0, 23, 61}: "Neology", + [3]byte{0, 23, 62}: "LeucotronEquipamentos Ltda.", + [3]byte{0, 23, 63}: "Belkin International Inc.", + [3]byte{0, 23, 64}: "Bluberi Gaming Technologies Inc", + [3]byte{0, 23, 65}: "DEFIDEV", + [3]byte{0, 23, 66}: "FUJITSU LIMITED", + [3]byte{0, 23, 67}: "Deck Srl", + [3]byte{0, 23, 68}: "Araneo Ltd.", + [3]byte{0, 23, 69}: "INNOTZ CO., Ltd", + [3]byte{0, 23, 70}: "Freedom9 Inc.", + [3]byte{0, 23, 71}: "Trimble", + [3]byte{0, 23, 72}: "Neokoros Brasil Ltda", + [3]byte{0, 23, 73}: "HYUNDAE YONG-O-SA CO.,LTD", + [3]byte{0, 23, 74}: "SOCOMEC", + [3]byte{0, 23, 75}: "Nokia Danmark A/S", + [3]byte{0, 23, 76}: "Millipore", + [3]byte{0, 23, 77}: "DYNAMIC NETWORK FACTORY, INC.", + [3]byte{0, 23, 78}: "Parama-tech Co.,Ltd.", + [3]byte{0, 23, 79}: "iCatch Inc.", + [3]byte{0, 23, 80}: "GSI Group, MicroE Systems", + [3]byte{0, 23, 81}: "Online Corporation", + [3]byte{0, 23, 82}: "DAGS, Inc", + [3]byte{0, 23, 83}: "nFore Technology Inc.", + [3]byte{0, 23, 84}: "Arkino HiTOP Corporation Limited", + [3]byte{0, 23, 85}: "GE Security", + [3]byte{0, 23, 86}: "Vinci Labs Oy", + [3]byte{0, 23, 87}: "RIX TECHNOLOGY LIMITED", + [3]byte{0, 23, 88}: "ThruVision Ltd", + [3]byte{0, 23, 89}: "Cisco Systems, Inc", + [3]byte{0, 23, 90}: "Cisco Systems, Inc", + [3]byte{0, 23, 91}: "ACS Solutions Switzerland Ltd.", + [3]byte{0, 23, 92}: "SHARP CORPORATION", + [3]byte{0, 23, 93}: "Dongseo system.", + [3]byte{0, 23, 94}: "Zed-3", + [3]byte{0, 23, 95}: "XENOLINK Communications Co., Ltd.", + [3]byte{0, 23, 96}: "Naito Densei Machida MFG.CO.,LTD", + [3]byte{0, 23, 97}: "Private", + [3]byte{0, 23, 98}: "Solar Technology, Inc.", + [3]byte{0, 23, 99}: "Essentia S.p.A.", + [3]byte{0, 23, 100}: "ATMedia GmbH", + [3]byte{0, 23, 101}: "Nortel Networks", + [3]byte{0, 23, 102}: "Accense Technology, Inc.", + [3]byte{0, 23, 103}: "Earforce AS", + [3]byte{0, 23, 104}: "Zinwave Ltd", + [3]byte{0, 23, 105}: "Cymphonix Corp", + [3]byte{0, 23, 106}: "Avago Technologies", + [3]byte{0, 23, 107}: "Kiyon, Inc.", + [3]byte{0, 23, 108}: "Pivot3, Inc.", + [3]byte{0, 23, 109}: "CORE CORPORATION", + [3]byte{0, 23, 110}: "DUCATI SISTEMI", + [3]byte{0, 23, 111}: "PAX Computer Technology(Shenzhen) Ltd.", + [3]byte{0, 23, 112}: "Arti Industrial Electronics Ltd.", + [3]byte{0, 23, 113}: "APD Communications Ltd", + [3]byte{0, 23, 114}: "ASTRO Strobel Kommunikationssysteme GmbH", + [3]byte{0, 23, 115}: "Laketune Technologies Co. Ltd", + [3]byte{0, 23, 116}: "Elesta GmbH", + [3]byte{0, 23, 117}: "TTE Germany GmbH", + [3]byte{0, 23, 118}: "Meso Scale Diagnostics, LLC", + [3]byte{0, 23, 119}: "Obsidian Research Corporation", + [3]byte{0, 23, 120}: "Central Music Co.", + [3]byte{0, 23, 121}: "QuickTel", + [3]byte{0, 23, 122}: "ASSA ABLOY AB", + [3]byte{0, 23, 123}: "Azalea Networks inc", + [3]byte{0, 23, 124}: "Smartlink Network Systems Limited", + [3]byte{0, 23, 125}: "IDT Technology Limited", + [3]byte{0, 23, 126}: "Meshcom Technologies Inc.", + [3]byte{0, 23, 127}: "Worldsmart Retech", + [3]byte{0, 23, 128}: "Applied Biosystems B.V.", + [3]byte{0, 23, 129}: "Greystone Data System, Inc.", + [3]byte{0, 23, 130}: "LoBenn Inc.", + [3]byte{0, 23, 131}: "Texas Instruments", + [3]byte{0, 23, 132}: "ARRIS Group, Inc.", + [3]byte{0, 23, 133}: "Sparr Electronics Ltd", + [3]byte{0, 23, 134}: "wisembed", + [3]byte{0, 23, 135}: "Brother, Brother & Sons ApS", + [3]byte{0, 23, 136}: "Philips Lighting BV", + [3]byte{0, 23, 137}: "Zenitron Corporation", + [3]byte{0, 23, 138}: "DARTS TECHNOLOGIES CORP.", + [3]byte{0, 23, 139}: "Teledyne Technologies Incorporated", + [3]byte{0, 23, 140}: "Independent Witness, Inc", + [3]byte{0, 23, 141}: "Checkpoint Systems, Inc.", + [3]byte{0, 23, 142}: "Gunnebo Cash Automation AB", + [3]byte{0, 23, 143}: "NINGBO YIDONG ELECTRONIC CO.,LTD.", + [3]byte{0, 23, 144}: "HYUNDAI DIGITECH Co, Ltd.", + [3]byte{0, 23, 145}: "LinTech GmbH", + [3]byte{0, 23, 146}: "Falcom Wireless Comunications Gmbh", + [3]byte{0, 23, 147}: "Tigi Corporation", + [3]byte{0, 23, 148}: "Cisco Systems, Inc", + [3]byte{0, 23, 149}: "Cisco Systems, Inc", + [3]byte{0, 23, 150}: "Rittmeyer AG", + [3]byte{0, 23, 151}: "Telsy Elettronica S.p.A.", + [3]byte{0, 23, 152}: "Azonic Technology Co., LTD", + [3]byte{0, 23, 153}: "SmarTire Systems Inc.", + [3]byte{0, 23, 154}: "D-Link Corporation", + [3]byte{0, 23, 155}: "CHANT SINCERE CO.,LTD", + [3]byte{0, 23, 156}: "DEPRAG SCHULZ GMBH u. CO.", + [3]byte{0, 23, 157}: "Kelman Limited", + [3]byte{0, 23, 158}: "Sirit Inc", + [3]byte{0, 23, 159}: "Apricorn", + [3]byte{0, 23, 160}: "RoboTech srl", + [3]byte{0, 23, 161}: "3soft inc.", + [3]byte{0, 23, 162}: "Camrivox Ltd.", + [3]byte{0, 23, 163}: "MIX s.r.l.", + [3]byte{0, 23, 164}: "Hewlett Packard", + [3]byte{0, 23, 165}: "Ralink Technology Corp", + [3]byte{0, 23, 166}: "YOSIN ELECTRONICS CO., LTD.", + [3]byte{0, 23, 167}: "Mobile Computing Promotion Consortium", + [3]byte{0, 23, 168}: "EDM Corporation", + [3]byte{0, 23, 169}: "Sentivision", + [3]byte{0, 23, 170}: "elab-experience inc.", + [3]byte{0, 23, 171}: "Nintendo Co., Ltd.", + [3]byte{0, 23, 172}: "O'Neil Product Development Inc.", + [3]byte{0, 23, 173}: "AceNet Corporation", + [3]byte{0, 23, 174}: "GAI-Tronics", + [3]byte{0, 23, 175}: "Enermet", + [3]byte{0, 23, 176}: "Nokia Danmark A/S", + [3]byte{0, 23, 177}: "ACIST Medical Systems, Inc.", + [3]byte{0, 23, 178}: "SK Telesys", + [3]byte{0, 23, 179}: "Aftek Infosys Limited", + [3]byte{0, 23, 180}: "Remote Security Systems, LLC", + [3]byte{0, 23, 181}: "Peerless Systems Corporation", + [3]byte{0, 23, 182}: "Aquantia", + [3]byte{0, 23, 183}: "Tonze Technology Co.", + [3]byte{0, 23, 184}: "NOVATRON CO., LTD.", + [3]byte{0, 23, 185}: "Gambro Lundia AB", + [3]byte{0, 23, 186}: "SEDO CO., LTD.", + [3]byte{0, 23, 187}: "Syrinx Industrial Electronics", + [3]byte{0, 23, 188}: "Touchtunes Music Corporation", + [3]byte{0, 23, 189}: "Tibetsystem", + [3]byte{0, 23, 190}: "Tratec Telecom B.V.", + [3]byte{0, 23, 191}: "Coherent Research Limited", + [3]byte{0, 23, 192}: "PureTech Systems, Inc.", + [3]byte{0, 23, 193}: "CM Precision Technology LTD.", + [3]byte{0, 23, 194}: "ADB Broadband Italia", + [3]byte{0, 23, 195}: "KTF Technologies Inc.", + [3]byte{0, 23, 196}: "Quanta Microsystems, INC.", + [3]byte{0, 23, 197}: "SonicWALL", + [3]byte{0, 23, 198}: "Cross Match Technologies Inc", + [3]byte{0, 23, 199}: "MARA Systems Consulting AB", + [3]byte{0, 23, 200}: "KYOCERA Document Solutions Inc.", + [3]byte{0, 23, 201}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 23, 202}: "Qisda Corporation", + [3]byte{0, 23, 203}: "Juniper Networks", + [3]byte{0, 23, 204}: "Alcatel-Lucent", + [3]byte{0, 23, 205}: "CEC Wireless R&D Ltd.", + [3]byte{0, 23, 206}: "Screen Service Spa", + [3]byte{0, 23, 207}: "iMCA-GmbH", + [3]byte{0, 23, 208}: "Opticom Communications, LLC", + [3]byte{0, 23, 209}: "Nortel Networks", + [3]byte{0, 23, 210}: "THINLINX PTY LTD", + [3]byte{0, 23, 211}: "Etymotic Research, Inc.", + [3]byte{0, 23, 212}: "Monsoon Multimedia, Inc", + [3]byte{0, 23, 213}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 23, 214}: "Bluechips Microhouse Co.,Ltd.", + [3]byte{0, 23, 215}: "ION Geophysical Corporation Inc.", + [3]byte{0, 23, 216}: "Magnum Semiconductor, Inc.", + [3]byte{0, 23, 217}: "AAI Corporation", + [3]byte{0, 23, 218}: "Spans Logic", + [3]byte{0, 23, 219}: "CANKO TECHNOLOGIES INC.", + [3]byte{0, 23, 220}: "DAEMYUNG ZERO1", + [3]byte{0, 23, 221}: "Clipsal Australia", + [3]byte{0, 23, 222}: "Advantage Six Ltd", + [3]byte{0, 23, 223}: "Cisco Systems, Inc", + [3]byte{0, 23, 224}: "Cisco Systems, Inc", + [3]byte{0, 23, 225}: "DACOS Technologies Co., Ltd.", + [3]byte{0, 23, 226}: "ARRIS Group, Inc.", + [3]byte{0, 23, 227}: "Texas Instruments", + [3]byte{0, 23, 228}: "Texas Instruments", + [3]byte{0, 23, 229}: "Texas Instruments", + [3]byte{0, 23, 230}: "Texas Instruments", + [3]byte{0, 23, 231}: "Texas Instruments", + [3]byte{0, 23, 232}: "Texas Instruments", + [3]byte{0, 23, 233}: "Texas Instruments", + [3]byte{0, 23, 234}: "Texas Instruments", + [3]byte{0, 23, 235}: "Texas Instruments", + [3]byte{0, 23, 236}: "Texas Instruments", + [3]byte{0, 23, 237}: "WooJooIT Ltd.", + [3]byte{0, 23, 238}: "ARRIS Group, Inc.", + [3]byte{0, 23, 239}: "IBM Corp", + [3]byte{0, 23, 240}: "SZCOM Broadband Network Technology Co.,Ltd", + [3]byte{0, 23, 241}: "Renu Electronics Pvt Ltd", + [3]byte{0, 23, 242}: "Apple, Inc.", + [3]byte{0, 23, 243}: "Harris Corparation", + [3]byte{0, 23, 244}: "ZERON ALLIANCE", + [3]byte{0, 23, 245}: "LIG NEOPTEK", + [3]byte{0, 23, 246}: "Pyramid Meriden Inc.", + [3]byte{0, 23, 247}: "CEM Solutions Pvt Ltd", + [3]byte{0, 23, 248}: "Motech Industries Inc.", + [3]byte{0, 23, 249}: "Forcom Sp. z o.o.", + [3]byte{0, 23, 250}: "Microsoft Corporation", + [3]byte{0, 23, 251}: "FA", + [3]byte{0, 23, 252}: "Suprema Inc.", + [3]byte{0, 23, 253}: "Amulet Hotkey", + [3]byte{0, 23, 254}: "TALOS SYSTEM INC.", + [3]byte{0, 23, 255}: "PLAYLINE Co.,Ltd.", + [3]byte{0, 24, 0}: "UNIGRAND LTD", + [3]byte{0, 24, 1}: "Actiontec Electronics, Inc", + [3]byte{0, 24, 2}: "Alpha Networks Inc.", + [3]byte{0, 24, 3}: "ArcSoft Shanghai Co. LTD", + [3]byte{0, 24, 4}: "E-TEK DIGITAL TECHNOLOGY LIMITED", + [3]byte{0, 24, 5}: "Beijing InHand Networking Technology Co.,Ltd.", + [3]byte{0, 24, 6}: "Hokkei Industries Co., Ltd.", + [3]byte{0, 24, 7}: "Fanstel Corp.", + [3]byte{0, 24, 8}: "SightLogix, Inc.", + [3]byte{0, 24, 9}: "CRESYN", + [3]byte{0, 24, 10}: "Meraki, Inc.", + [3]byte{0, 24, 11}: "Brilliant Telecommunications", + [3]byte{0, 24, 12}: "Optelian Access Networks", + [3]byte{0, 24, 13}: "Terabytes Server Storage Tech Corp", + [3]byte{0, 24, 14}: "Avega Systems", + [3]byte{0, 24, 15}: "Nokia Danmark A/S", + [3]byte{0, 24, 16}: "IPTrade S.A.", + [3]byte{0, 24, 17}: "Neuros Technology International, LLC.", + [3]byte{0, 24, 18}: "Beijing Xinwei Telecom Technology Co., Ltd.", + [3]byte{0, 24, 19}: "Sony Mobile Communications AB", + [3]byte{0, 24, 20}: "Mitutoyo Corporation", + [3]byte{0, 24, 21}: "GZ Technologies, Inc.", + [3]byte{0, 24, 22}: "Ubixon Co., Ltd.", + [3]byte{0, 24, 23}: "D. E. Shaw Research, LLC", + [3]byte{0, 24, 24}: "Cisco Systems, Inc", + [3]byte{0, 24, 25}: "Cisco Systems, Inc", + [3]byte{0, 24, 26}: "AVerMedia Information Inc.", + [3]byte{0, 24, 27}: "TaiJin Metal Co., Ltd.", + [3]byte{0, 24, 28}: "Exterity Limited", + [3]byte{0, 24, 29}: "ASIA ELECTRONICS CO.,LTD", + [3]byte{0, 24, 30}: "GDX Technologies Ltd.", + [3]byte{0, 24, 31}: "Palmmicro Communications", + [3]byte{0, 24, 32}: "w5networks", + [3]byte{0, 24, 33}: "SINDORICOH", + [3]byte{0, 24, 34}: "CEC TELECOM CO.,LTD.", + [3]byte{0, 24, 35}: "Delta Electronics, Inc.", + [3]byte{0, 24, 36}: "Kimaldi Electronics, S.L.", + [3]byte{0, 24, 37}: "Private", + [3]byte{0, 24, 38}: "Cale Access AB", + [3]byte{0, 24, 39}: "NEC UNIFIED SOLUTIONS NEDERLAND B.V.", + [3]byte{0, 24, 40}: "e2v technologies (UK) ltd.", + [3]byte{0, 24, 41}: "Gatsometer", + [3]byte{0, 24, 42}: "Taiwan Video & Monitor", + [3]byte{0, 24, 43}: "Softier", + [3]byte{0, 24, 44}: "Ascend Networks, Inc.", + [3]byte{0, 24, 45}: "Artec Design", + [3]byte{0, 24, 46}: "XStreamHD", + [3]byte{0, 24, 47}: "Texas Instruments", + [3]byte{0, 24, 48}: "Texas Instruments", + [3]byte{0, 24, 49}: "Texas Instruments", + [3]byte{0, 24, 50}: "Texas Instruments", + [3]byte{0, 24, 51}: "Texas Instruments", + [3]byte{0, 24, 52}: "Texas Instruments", + [3]byte{0, 24, 53}: "Thoratec / ITC", + [3]byte{0, 24, 54}: "Reliance Electric Limited", + [3]byte{0, 24, 55}: "Universal ABIT Co., Ltd.", + [3]byte{0, 24, 56}: "PanAccess Communications,Inc.", + [3]byte{0, 24, 57}: "Cisco-Linksys, LLC", + [3]byte{0, 24, 58}: "Westell Technologies Inc.", + [3]byte{0, 24, 59}: "CENITS Co., Ltd.", + [3]byte{0, 24, 60}: "Encore Software Limited", + [3]byte{0, 24, 61}: "Vertex Link Corporation", + [3]byte{0, 24, 62}: "Digilent, Inc", + [3]byte{0, 24, 63}: "2Wire Inc", + [3]byte{0, 24, 64}: "3 Phoenix, Inc.", + [3]byte{0, 24, 65}: "High Tech Computer Corp", + [3]byte{0, 24, 66}: "Nokia Danmark A/S", + [3]byte{0, 24, 67}: "Dawevision Ltd", + [3]byte{0, 24, 68}: "Heads Up Technologies, Inc.", + [3]byte{0, 24, 69}: "Pulsar-Telecom LLC.", + [3]byte{0, 24, 70}: "Crypto S.A.", + [3]byte{0, 24, 71}: "AceNet Technology Inc.", + [3]byte{0, 24, 72}: "Vecima Networks Inc.", + [3]byte{0, 24, 73}: "Pigeon Point Systems LLC", + [3]byte{0, 24, 74}: "Catcher, Inc.", + [3]byte{0, 24, 75}: "Las Vegas Gaming, Inc.", + [3]byte{0, 24, 76}: "Bogen Communications", + [3]byte{0, 24, 77}: "NETGEAR", + [3]byte{0, 24, 78}: "Lianhe Technologies, Inc.", + [3]byte{0, 24, 79}: "8 Ways Technology Corp.", + [3]byte{0, 24, 80}: "Secfone Kft", + [3]byte{0, 24, 81}: "SWsoft", + [3]byte{0, 24, 82}: "StorLink Semiconductors, Inc.", + [3]byte{0, 24, 83}: "Atera Networks LTD.", + [3]byte{0, 24, 84}: "Argard Co., Ltd", + [3]byte{0, 24, 85}: "Aeromaritime Systembau GmbH", + [3]byte{0, 24, 86}: "EyeFi, Inc", + [3]byte{0, 24, 87}: "Unilever R&D", + [3]byte{0, 24, 88}: "TagMaster AB", + [3]byte{0, 24, 89}: "Strawberry Linux Co.,Ltd.", + [3]byte{0, 24, 90}: "uControl, Inc.", + [3]byte{0, 24, 91}: "Network Chemistry, Inc", + [3]byte{0, 24, 92}: "EDSLAB Technologies", + [3]byte{0, 24, 93}: "TAIGUEN TECHNOLOGY (SHEN-ZHEN) CO., LTD.", + [3]byte{0, 24, 94}: "Nexterm Inc.", + [3]byte{0, 24, 95}: "TAC Inc.", + [3]byte{0, 24, 96}: "SIM Technology Group Shanghai Simcom Ltd.,", + [3]byte{0, 24, 97}: "Ooma, Inc.", + [3]byte{0, 24, 98}: "Seagate Technology", + [3]byte{0, 24, 99}: "Veritech Electronics Limited", + [3]byte{0, 24, 100}: "Eaton Corporation", + [3]byte{0, 24, 101}: "Siemens Healthcare Diagnostics Manufacturing Ltd", + [3]byte{0, 24, 102}: "Leutron Vision", + [3]byte{0, 24, 103}: "Datalogic ADC", + [3]byte{0, 24, 104}: "Cisco SPVTG", + [3]byte{0, 24, 105}: "KINGJIM", + [3]byte{0, 24, 106}: "Global Link Digital Technology Co,.LTD", + [3]byte{0, 24, 107}: "Sambu Communics CO., LTD.", + [3]byte{0, 24, 108}: "Neonode AB", + [3]byte{0, 24, 109}: "Zhenjiang Sapphire Electronic Industry CO.", + [3]byte{0, 24, 110}: "3Com Ltd", + [3]byte{0, 24, 111}: "Setha Industria Eletronica LTDA", + [3]byte{0, 24, 112}: "E28 Shanghai Limited", + [3]byte{0, 24, 113}: "Hewlett Packard", + [3]byte{0, 24, 114}: "Expertise Engineering", + [3]byte{0, 24, 115}: "Cisco Systems, Inc", + [3]byte{0, 24, 116}: "Cisco Systems, Inc", + [3]byte{0, 24, 117}: "AnaCise Testnology Pte Ltd", + [3]byte{0, 24, 118}: "WowWee Ltd.", + [3]byte{0, 24, 119}: "Amplex A/S", + [3]byte{0, 24, 120}: "Mackware GmbH", + [3]byte{0, 24, 121}: "dSys", + [3]byte{0, 24, 122}: "Wiremold", + [3]byte{0, 24, 123}: "4NSYS Co. Ltd.", + [3]byte{0, 24, 124}: "INTERCROSS, LLC", + [3]byte{0, 24, 125}: "Armorlink shanghai Co. Ltd", + [3]byte{0, 24, 126}: "RGB Spectrum", + [3]byte{0, 24, 127}: "ZODIANET", + [3]byte{0, 24, 128}: "Maxim Integrated Products", + [3]byte{0, 24, 129}: "Buyang Electronics Industrial Co., Ltd", + [3]byte{0, 24, 130}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 24, 131}: "FORMOSA21 INC.", + [3]byte{0, 24, 132}: "Fon Technology S.L.", + [3]byte{0, 24, 133}: "Avigilon Corporation", + [3]byte{0, 24, 134}: "EL-TECH, INC.", + [3]byte{0, 24, 135}: "Metasystem SpA", + [3]byte{0, 24, 136}: "GOTIVE a.s.", + [3]byte{0, 24, 137}: "WinNet Solutions Limited", + [3]byte{0, 24, 138}: "Infinova LLC", + [3]byte{0, 24, 139}: "Dell Inc.", + [3]byte{0, 24, 140}: "Mobile Action Technology Inc.", + [3]byte{0, 24, 141}: "Nokia Danmark A/S", + [3]byte{0, 24, 142}: "Ekahau, Inc.", + [3]byte{0, 24, 143}: "Montgomery Technology, Inc.", + [3]byte{0, 24, 144}: "RadioCOM, s.r.o.", + [3]byte{0, 24, 145}: "Zhongshan General K-mate Electronics Co., Ltd", + [3]byte{0, 24, 146}: "ads-tec GmbH", + [3]byte{0, 24, 147}: "SHENZHEN PHOTON BROADBAND TECHNOLOGY CO.,LTD", + [3]byte{0, 24, 148}: "NPCore, Inc.", + [3]byte{0, 24, 149}: "Hansun Technologies Inc.", + [3]byte{0, 24, 150}: "Great Well Electronic LTD", + [3]byte{0, 24, 151}: "JESS-LINK PRODUCTS Co., LTD", + [3]byte{0, 24, 152}: "KINGSTATE ELECTRONICS CORPORATION", + [3]byte{0, 24, 153}: "ShenZhen jieshun Science&Technology Industry CO,LTD.", + [3]byte{0, 24, 154}: "HANA Micron Inc.", + [3]byte{0, 24, 155}: "Thomson Inc.", + [3]byte{0, 24, 156}: "Weldex Corporation", + [3]byte{0, 24, 157}: "Navcast Inc.", + [3]byte{0, 24, 158}: "OMNIKEY GmbH.", + [3]byte{0, 24, 159}: "Lenntek Corporation", + [3]byte{0, 24, 160}: "Cierma Ascenseurs", + [3]byte{0, 24, 161}: "Tiqit Computers, Inc.", + [3]byte{0, 24, 162}: "XIP Technology AB", + [3]byte{0, 24, 163}: "ZIPPY TECHNOLOGY CORP.", + [3]byte{0, 24, 164}: "ARRIS Group, Inc.", + [3]byte{0, 24, 165}: "ADigit Technologies Corp.", + [3]byte{0, 24, 166}: "Persistent Systems, LLC", + [3]byte{0, 24, 167}: "Yoggie Security Systems LTD.", + [3]byte{0, 24, 168}: "AnNeal Technology Inc.", + [3]byte{0, 24, 169}: "Ethernet Direct Corporation", + [3]byte{0, 24, 170}: "Protec Fire Detection plc", + [3]byte{0, 24, 171}: "BEIJING LHWT MICROELECTRONICS INC.", + [3]byte{0, 24, 172}: "Shanghai Jiao Da HISYS Technology Co. Ltd.", + [3]byte{0, 24, 173}: "NIDEC SANKYO CORPORATION", + [3]byte{0, 24, 174}: "TVT CO.,LTD", + [3]byte{0, 24, 175}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 24, 176}: "Nortel Networks", + [3]byte{0, 24, 177}: "IBM Corp", + [3]byte{0, 24, 178}: "ADEUNIS RF", + [3]byte{0, 24, 179}: "TEC WizHome Co., Ltd.", + [3]byte{0, 24, 180}: "Dawon Media Inc.", + [3]byte{0, 24, 181}: "Magna Carta", + [3]byte{0, 24, 182}: "S3C, Inc.", + [3]byte{0, 24, 183}: "D3 LED, LLC", + [3]byte{0, 24, 184}: "New Voice International AG", + [3]byte{0, 24, 185}: "Cisco Systems, Inc", + [3]byte{0, 24, 186}: "Cisco Systems, Inc", + [3]byte{0, 24, 187}: "Eliwell Controls srl", + [3]byte{0, 24, 188}: "ZAO NVP Bolid", + [3]byte{0, 24, 189}: "SHENZHEN DVBWORLD TECHNOLOGY CO., LTD.", + [3]byte{0, 24, 190}: "ANSA Corporation", + [3]byte{0, 24, 191}: "Essence Technology Solution, Inc.", + [3]byte{0, 24, 192}: "ARRIS Group, Inc.", + [3]byte{0, 24, 193}: "Almitec Informática e Comércio", + [3]byte{0, 24, 194}: "Firetide, Inc", + [3]byte{0, 24, 195}: "CS Corporation", + [3]byte{0, 24, 196}: "Raba Technologies LLC", + [3]byte{0, 24, 197}: "Nokia Danmark A/S", + [3]byte{0, 24, 198}: "OPW Fuel Management Systems", + [3]byte{0, 24, 199}: "Real Time Automation", + [3]byte{0, 24, 200}: "ISONAS Inc.", + [3]byte{0, 24, 201}: "EOps Technology Limited", + [3]byte{0, 24, 202}: "Viprinet GmbH", + [3]byte{0, 24, 203}: "Tecobest Technology Limited", + [3]byte{0, 24, 204}: "AXIOHM SAS", + [3]byte{0, 24, 205}: "Erae Electronics Industry Co., Ltd", + [3]byte{0, 24, 206}: "Dreamtech Co., Ltd", + [3]byte{0, 24, 207}: "Baldor Electric Company", + [3]byte{0, 24, 208}: "AtRoad, A Trimble Company", + [3]byte{0, 24, 209}: "Siemens Home & Office Comm. Devices", + [3]byte{0, 24, 210}: "High-Gain Antennas LLC", + [3]byte{0, 24, 211}: "TEAMCAST", + [3]byte{0, 24, 212}: "Unified Display Interface SIG", + [3]byte{0, 24, 213}: "REIGNCOM", + [3]byte{0, 24, 214}: "Swirlnet A/S", + [3]byte{0, 24, 215}: "JAVAD GNSS, Inc.", + [3]byte{0, 24, 216}: "ARCH METER Corporation", + [3]byte{0, 24, 217}: "Santosha Internatonal, Inc", + [3]byte{0, 24, 218}: "AMBER wireless GmbH", + [3]byte{0, 24, 219}: "EPL Technology Ltd", + [3]byte{0, 24, 220}: "Prostar Co., Ltd.", + [3]byte{0, 24, 221}: "Silicondust Engineering Ltd", + [3]byte{0, 24, 222}: "Intel Corporate", + [3]byte{0, 24, 223}: "The Morey Corporation", + [3]byte{0, 24, 224}: "ANAVEO", + [3]byte{0, 24, 225}: "Verkerk Service Systemen", + [3]byte{0, 24, 226}: "Topdata Sistemas de Automacao Ltda", + [3]byte{0, 24, 227}: "Visualgate Systems, Inc.", + [3]byte{0, 24, 228}: "YIGUANG", + [3]byte{0, 24, 229}: "Adhoco AG", + [3]byte{0, 24, 230}: "Computer Hardware Design SIA", + [3]byte{0, 24, 231}: "Cameo Communications, INC.", + [3]byte{0, 24, 232}: "Hacetron Corporation", + [3]byte{0, 24, 233}: "Numata Corporation", + [3]byte{0, 24, 234}: "Alltec GmbH", + [3]byte{0, 24, 235}: "Blue Zen Enterprises Private Limited", + [3]byte{0, 24, 236}: "Welding Technology Corporation", + [3]byte{0, 24, 237}: "Accutech Ultrasystems Co., Ltd.", + [3]byte{0, 24, 238}: "Videology Imaging Solutions, Inc.", + [3]byte{0, 24, 239}: "Escape Communications, Inc.", + [3]byte{0, 24, 240}: "JOYTOTO Co., Ltd.", + [3]byte{0, 24, 241}: "Chunichi Denshi Co.,LTD.", + [3]byte{0, 24, 242}: "Beijing Tianyu Communication Equipment Co., Ltd", + [3]byte{0, 24, 243}: "ASUSTek COMPUTER INC.", + [3]byte{0, 24, 244}: "EO TECHNICS Co., Ltd.", + [3]byte{0, 24, 245}: "Shenzhen Streaming Video Technology Company Limited", + [3]byte{0, 24, 246}: "Thomson Telecom Belgium", + [3]byte{0, 24, 247}: "Kameleon Technologies", + [3]byte{0, 24, 248}: "Cisco-Linksys, LLC", + [3]byte{0, 24, 249}: "VVOND, Inc.", + [3]byte{0, 24, 250}: "Yushin Precision Equipment Co.,Ltd.", + [3]byte{0, 24, 251}: "Compro Technology", + [3]byte{0, 24, 252}: "Altec Electronic AG", + [3]byte{0, 24, 253}: "Optimal Technologies International Inc.", + [3]byte{0, 24, 254}: "Hewlett Packard", + [3]byte{0, 24, 255}: "PowerQuattro Co.", + [3]byte{0, 25, 0}: "Intelliverese - DBA Voicecom", + [3]byte{0, 25, 1}: "F1MEDIA", + [3]byte{0, 25, 2}: "Cambridge Consultants Ltd", + [3]byte{0, 25, 3}: "Bigfoot Networks Inc", + [3]byte{0, 25, 4}: "WB Electronics Sp. z o.o.", + [3]byte{0, 25, 5}: "SCHRACK Seconet AG", + [3]byte{0, 25, 6}: "Cisco Systems, Inc", + [3]byte{0, 25, 7}: "Cisco Systems, Inc", + [3]byte{0, 25, 8}: "Duaxes Corporation", + [3]byte{0, 25, 9}: "DEVI - Danfoss A/S", + [3]byte{0, 25, 10}: "HASWARE INC.", + [3]byte{0, 25, 11}: "Southern Vision Systems, Inc.", + [3]byte{0, 25, 12}: "Encore Electronics, Inc.", + [3]byte{0, 25, 13}: "IEEE 1394c", + [3]byte{0, 25, 14}: "Atech Technology Co., Ltd.", + [3]byte{0, 25, 15}: "Advansus Corp.", + [3]byte{0, 25, 16}: "Knick Elektronische Messgeraete GmbH & Co. KG", + [3]byte{0, 25, 17}: "Just In Mobile Information Technologies (Shanghai) Co., Ltd.", + [3]byte{0, 25, 18}: "Welcat Inc", + [3]byte{0, 25, 19}: "Chuang-Yi Network Equipment Co.Ltd.", + [3]byte{0, 25, 20}: "Winix Co., Ltd", + [3]byte{0, 25, 21}: "TECOM Co., Ltd.", + [3]byte{0, 25, 22}: "PayTec AG", + [3]byte{0, 25, 23}: "Posiflex Inc.", + [3]byte{0, 25, 24}: "Interactive Wear AG", + [3]byte{0, 25, 25}: "ASTEL Inc.", + [3]byte{0, 25, 26}: "IRLINK", + [3]byte{0, 25, 27}: "Sputnik Engineering AG", + [3]byte{0, 25, 28}: "Sensicast Systems", + [3]byte{0, 25, 29}: "Nintendo Co., Ltd.", + [3]byte{0, 25, 30}: "Beyondwiz Co., Ltd.", + [3]byte{0, 25, 31}: "Microlink communications Inc.", + [3]byte{0, 25, 32}: "KUME electric Co.,Ltd.", + [3]byte{0, 25, 33}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 25, 34}: "CM Comandos Lineares", + [3]byte{0, 25, 35}: "Phonex Korea Co., LTD.", + [3]byte{0, 25, 36}: "LBNL Engineering", + [3]byte{0, 25, 37}: "Intelicis Corporation", + [3]byte{0, 25, 38}: "BitsGen Co., Ltd.", + [3]byte{0, 25, 39}: "ImCoSys Ltd", + [3]byte{0, 25, 40}: "Siemens AG, Transportation Systems", + [3]byte{0, 25, 41}: "2M2B Montadora de Maquinas Bahia Brasil LTDA", + [3]byte{0, 25, 42}: "Antiope Associates", + [3]byte{0, 25, 43}: "Aclara RF Systems Inc.", + [3]byte{0, 25, 44}: "ARRIS Group, Inc.", + [3]byte{0, 25, 45}: "Nokia Corporation", + [3]byte{0, 25, 46}: "Spectral Instruments, Inc.", + [3]byte{0, 25, 47}: "Cisco Systems, Inc", + [3]byte{0, 25, 48}: "Cisco Systems, Inc", + [3]byte{0, 25, 49}: "Balluff GmbH", + [3]byte{0, 25, 50}: "Gude Analog- und Digialsysteme GmbH", + [3]byte{0, 25, 51}: "Strix Systems, Inc.", + [3]byte{0, 25, 52}: "TRENDON TOUCH TECHNOLOGY CORP.", + [3]byte{0, 25, 53}: "DUERR DENTAL AG", + [3]byte{0, 25, 54}: "STERLITE OPTICAL TECHNOLOGIES LIMITED", + [3]byte{0, 25, 55}: "CommerceGuard AB", + [3]byte{0, 25, 56}: "UMB Communications Co., Ltd.", + [3]byte{0, 25, 57}: "Gigamips", + [3]byte{0, 25, 58}: "OESOLUTIONS", + [3]byte{0, 25, 59}: "Wilibox Deliberant Group LLC", + [3]byte{0, 25, 60}: "HighPoint Technologies Incorporated", + [3]byte{0, 25, 61}: "GMC Guardian Mobility Corp.", + [3]byte{0, 25, 62}: "ADB Broadband Italia", + [3]byte{0, 25, 63}: "RDI technology(Shenzhen) Co.,LTD", + [3]byte{0, 25, 64}: "Rackable Systems", + [3]byte{0, 25, 65}: "Pitney Bowes, Inc", + [3]byte{0, 25, 66}: "ON SOFTWARE INTERNATIONAL LIMITED", + [3]byte{0, 25, 67}: "Belden", + [3]byte{0, 25, 68}: "Fossil Partners, L.P.", + [3]byte{0, 25, 69}: "RF COncepts, LLC", + [3]byte{0, 25, 70}: "Cianet Industria e Comercio S/A", + [3]byte{0, 25, 71}: "Cisco SPVTG", + [3]byte{0, 25, 72}: "AireSpider Networks", + [3]byte{0, 25, 73}: "TENTEL COMTECH CO., LTD.", + [3]byte{0, 25, 74}: "TESTO AG", + [3]byte{0, 25, 75}: "Sagemcom Broadband SAS", + [3]byte{0, 25, 76}: "Fujian Stelcom information & Technology CO.,Ltd", + [3]byte{0, 25, 77}: "Avago Technologies Sdn Bhd", + [3]byte{0, 25, 78}: "Ultra Electronics - TCS (Tactical Communication Systems)", + [3]byte{0, 25, 79}: "Nokia Danmark A/S", + [3]byte{0, 25, 80}: "Harman Multimedia", + [3]byte{0, 25, 81}: "NETCONS, s.r.o.", + [3]byte{0, 25, 82}: "ACOGITO Co., Ltd", + [3]byte{0, 25, 83}: "Chainleader Communications Corp.", + [3]byte{0, 25, 84}: "Leaf Corporation.", + [3]byte{0, 25, 85}: "Cisco Systems, Inc", + [3]byte{0, 25, 86}: "Cisco Systems, Inc", + [3]byte{0, 25, 87}: "Saafnet Canada Inc.", + [3]byte{0, 25, 88}: "Bluetooth SIG, Inc.", + [3]byte{0, 25, 89}: "Staccato Communications Inc.", + [3]byte{0, 25, 90}: "Jenaer Antriebstechnik GmbH", + [3]byte{0, 25, 91}: "D-Link Corporation", + [3]byte{0, 25, 92}: "Innotech Corporation", + [3]byte{0, 25, 93}: "ShenZhen XinHuaTong Opto Electronics Co.,Ltd", + [3]byte{0, 25, 94}: "ARRIS Group, Inc.", + [3]byte{0, 25, 95}: "Valemount Networks Corporation", + [3]byte{0, 25, 96}: "DoCoMo Systems, Inc.", + [3]byte{0, 25, 97}: "Blaupunkt Embedded Systems GmbH", + [3]byte{0, 25, 98}: "Commerciant, LP", + [3]byte{0, 25, 99}: "Sony Mobile Communications AB", + [3]byte{0, 25, 100}: "Doorking Inc.", + [3]byte{0, 25, 101}: "YuHua TelTech (ShangHai) Co., Ltd.", + [3]byte{0, 25, 102}: "Asiarock Technology Limited", + [3]byte{0, 25, 103}: "TELDAT Sp.J.", + [3]byte{0, 25, 104}: "Digital Video Networks(Shanghai) CO. LTD.", + [3]byte{0, 25, 105}: "Nortel Networks", + [3]byte{0, 25, 106}: "MikroM GmbH", + [3]byte{0, 25, 107}: "Danpex Corporation", + [3]byte{0, 25, 108}: "ETROVISION TECHNOLOGY", + [3]byte{0, 25, 109}: "Raybit Systems Korea, Inc", + [3]byte{0, 25, 110}: "Metacom (Pty) Ltd.", + [3]byte{0, 25, 111}: "SensoPart GmbH", + [3]byte{0, 25, 112}: "Z-Com, Inc.", + [3]byte{0, 25, 113}: "Guangzhou Unicomp Technology Co.,Ltd", + [3]byte{0, 25, 114}: "Plexus (Xiamen) Co.,ltd.", + [3]byte{0, 25, 115}: "Zeugma Systems", + [3]byte{0, 25, 116}: "16063", + [3]byte{0, 25, 117}: "Beijing Huisen networks technology Inc", + [3]byte{0, 25, 118}: "Xipher Technologies, LLC", + [3]byte{0, 25, 119}: "Aerohive Networks Inc.", + [3]byte{0, 25, 120}: "Datum Systems, Inc.", + [3]byte{0, 25, 121}: "Nokia Danmark A/S", + [3]byte{0, 25, 122}: "MAZeT GmbH", + [3]byte{0, 25, 123}: "Picotest Corp.", + [3]byte{0, 25, 124}: "Riedel Communications GmbH", + [3]byte{0, 25, 125}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 25, 126}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 25, 127}: "PLANTRONICS, INC.", + [3]byte{0, 25, 128}: "Gridpoint Systems", + [3]byte{0, 25, 129}: "Vivox Inc", + [3]byte{0, 25, 130}: "SmarDTV", + [3]byte{0, 25, 131}: "CCT R&D Limited", + [3]byte{0, 25, 132}: "ESTIC Corporation", + [3]byte{0, 25, 133}: "IT Watchdogs, Inc", + [3]byte{0, 25, 134}: "Cheng Hongjian", + [3]byte{0, 25, 135}: "Panasonic Mobile Communications Co., Ltd.", + [3]byte{0, 25, 136}: "Wi2Wi, Inc", + [3]byte{0, 25, 137}: "Sonitrol Corporation", + [3]byte{0, 25, 138}: "Northrop Grumman Systems Corp.", + [3]byte{0, 25, 139}: "Novera Optics Korea, Inc.", + [3]byte{0, 25, 140}: "iXSea", + [3]byte{0, 25, 141}: "Ocean Optics, Inc.", + [3]byte{0, 25, 142}: "Oticon A/S", + [3]byte{0, 25, 143}: "Alcatel Bell N.V.", + [3]byte{0, 25, 144}: "ELM DATA Co., Ltd.", + [3]byte{0, 25, 145}: "avinfo", + [3]byte{0, 25, 146}: "Adtran Inc", + [3]byte{0, 25, 147}: "Changshu Switchgear MFG. Co.,Ltd. (Former Changshu Switchgea", + [3]byte{0, 25, 148}: "Jorjin Technologies Inc.", + [3]byte{0, 25, 149}: "Jurong Hi-Tech (Suzhou)Co.ltd", + [3]byte{0, 25, 150}: "TurboChef Technologies Inc.", + [3]byte{0, 25, 151}: "Soft Device Sdn Bhd", + [3]byte{0, 25, 152}: "SATO CORPORATION", + [3]byte{0, 25, 153}: "Fujitsu Technology Solutions GmbH", + [3]byte{0, 25, 154}: "EDO-EVI", + [3]byte{0, 25, 155}: "Diversified Technical Systems, Inc.", + [3]byte{0, 25, 156}: "CTRING", + [3]byte{0, 25, 157}: "Vizio, Inc", + [3]byte{0, 25, 158}: "Nifty", + [3]byte{0, 25, 159}: "DKT A/S", + [3]byte{0, 25, 160}: "NIHON DATA SYSTENS, INC.", + [3]byte{0, 25, 161}: "LG INFORMATION & COMM.", + [3]byte{0, 25, 162}: "ORDYN TECHNOLOGIES", + [3]byte{0, 25, 163}: "asteel electronique atlantique", + [3]byte{0, 25, 164}: "Austar Technology (hang zhou) Co.,Ltd", + [3]byte{0, 25, 165}: "RadarFind Corporation", + [3]byte{0, 25, 166}: "ARRIS Group, Inc.", + [3]byte{0, 25, 167}: "ITU-T", + [3]byte{0, 25, 168}: "WiQuest Communications", + [3]byte{0, 25, 169}: "Cisco Systems, Inc", + [3]byte{0, 25, 170}: "Cisco Systems, Inc", + [3]byte{0, 25, 171}: "Raycom CO ., LTD", + [3]byte{0, 25, 172}: "GSP SYSTEMS Inc.", + [3]byte{0, 25, 173}: "BOBST SA", + [3]byte{0, 25, 174}: "Hopling Technologies b.v.", + [3]byte{0, 25, 175}: "Rigol Technologies, Inc.", + [3]byte{0, 25, 176}: "HanYang System", + [3]byte{0, 25, 177}: "Arrow7 Corporation", + [3]byte{0, 25, 178}: "XYnetsoft Co.,Ltd", + [3]byte{0, 25, 179}: "Stanford Research Systems", + [3]byte{0, 25, 180}: "Intellio Ltd", + [3]byte{0, 25, 181}: "Famar Fueguina S.A.", + [3]byte{0, 25, 182}: "Euro Emme s.r.l.", + [3]byte{0, 25, 183}: "Nokia Danmark A/S", + [3]byte{0, 25, 184}: "Boundary Devices", + [3]byte{0, 25, 185}: "Dell Inc.", + [3]byte{0, 25, 186}: "Paradox Security Systems Ltd", + [3]byte{0, 25, 187}: "Hewlett Packard", + [3]byte{0, 25, 188}: "ELECTRO CHANCE SRL", + [3]byte{0, 25, 189}: "New Media Life", + [3]byte{0, 25, 190}: "Altai Technologies Limited", + [3]byte{0, 25, 191}: "Citiway technology Co.,ltd", + [3]byte{0, 25, 192}: "ARRIS Group, Inc.", + [3]byte{0, 25, 193}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 25, 194}: "Equustek Solutions, Inc.", + [3]byte{0, 25, 195}: "Qualitrol", + [3]byte{0, 25, 196}: "Infocrypt Inc.", + [3]byte{0, 25, 197}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 25, 198}: "zte corporation", + [3]byte{0, 25, 199}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{0, 25, 200}: "AnyDATA Corporation", + [3]byte{0, 25, 201}: "S&C ELECTRIC COMPANY", + [3]byte{0, 25, 202}: "Broadata Communications, Inc", + [3]byte{0, 25, 203}: "ZyXEL Communications Corporation", + [3]byte{0, 25, 204}: "RCG (HK) Ltd", + [3]byte{0, 25, 205}: "Chengdu ethercom information technology Ltd.", + [3]byte{0, 25, 206}: "Progressive Gaming International", + [3]byte{0, 25, 207}: "SALICRU, S.A.", + [3]byte{0, 25, 208}: "Cathexis", + [3]byte{0, 25, 209}: "Intel Corporate", + [3]byte{0, 25, 210}: "Intel Corporate", + [3]byte{0, 25, 211}: "TRAK Microwave", + [3]byte{0, 25, 212}: "ICX Technologies", + [3]byte{0, 25, 213}: "IP Innovations, Inc.", + [3]byte{0, 25, 214}: "LS Cable and System Ltd.", + [3]byte{0, 25, 215}: "FORTUNETEK CO., LTD", + [3]byte{0, 25, 216}: "MAXFOR", + [3]byte{0, 25, 217}: "Zeutschel GmbH", + [3]byte{0, 25, 218}: "Welltrans O&E Technology Co. , Ltd.", + [3]byte{0, 25, 219}: "MICRO-STAR INTERNATIONAL CO., LTD.", + [3]byte{0, 25, 220}: "ENENSYS Technologies", + [3]byte{0, 25, 221}: "FEI-Zyfer, Inc.", + [3]byte{0, 25, 222}: "MOBITEK", + [3]byte{0, 25, 223}: "Thomson Inc.", + [3]byte{0, 25, 224}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 25, 225}: "Nortel Networks", + [3]byte{0, 25, 226}: "Juniper Networks", + [3]byte{0, 25, 227}: "Apple, Inc.", + [3]byte{0, 25, 228}: "2Wire Inc", + [3]byte{0, 25, 229}: "Lynx Studio Technology, Inc.", + [3]byte{0, 25, 230}: "TOYO MEDIC CO.,LTD.", + [3]byte{0, 25, 231}: "Cisco Systems, Inc", + [3]byte{0, 25, 232}: "Cisco Systems, Inc", + [3]byte{0, 25, 233}: "S-Information Technolgy, Co., Ltd.", + [3]byte{0, 25, 234}: "TeraMage Technologies Co., Ltd.", + [3]byte{0, 25, 235}: "Pyronix Ltd", + [3]byte{0, 25, 236}: "Sagamore Systems, Inc.", + [3]byte{0, 25, 237}: "Axesstel Inc.", + [3]byte{0, 25, 238}: "CARLO GAVAZZI CONTROLS SPA-Controls Division", + [3]byte{0, 25, 239}: "SHENZHEN LINNKING ELECTRONICS CO.,LTD", + [3]byte{0, 25, 240}: "UNIONMAN TECHNOLOGY CO.,LTD", + [3]byte{0, 25, 241}: "Star Communication Network Technology Co.,Ltd", + [3]byte{0, 25, 242}: "Teradyne K.K.", + [3]byte{0, 25, 243}: "Cetis, Inc", + [3]byte{0, 25, 244}: "Convergens Oy Ltd", + [3]byte{0, 25, 245}: "Imagination Technologies Ltd", + [3]byte{0, 25, 246}: "Acconet (PTE) Ltd", + [3]byte{0, 25, 247}: "Onset Computer Corporation", + [3]byte{0, 25, 248}: "Embedded Systems Design, Inc.", + [3]byte{0, 25, 249}: "TDK-Lambda", + [3]byte{0, 25, 250}: "Cable Vision Electronics CO., LTD.", + [3]byte{0, 25, 251}: "BSkyB Ltd", + [3]byte{0, 25, 252}: "PT. Ufoakses Sukses Luarbiasa", + [3]byte{0, 25, 253}: "Nintendo Co., Ltd.", + [3]byte{0, 25, 254}: "SHENZHEN SEECOMM TECHNOLOGY CO.,LTD.", + [3]byte{0, 25, 255}: "Finnzymes", + [3]byte{0, 26, 0}: "MATRIX INC.", + [3]byte{0, 26, 1}: "Smiths Medical", + [3]byte{0, 26, 2}: "SECURE CARE PRODUCTS, INC", + [3]byte{0, 26, 3}: "Angel Electronics Co., Ltd.", + [3]byte{0, 26, 4}: "Interay Solutions BV", + [3]byte{0, 26, 5}: "OPTIBASE LTD", + [3]byte{0, 26, 6}: "OpVista, Inc.", + [3]byte{0, 26, 7}: "Arecont Vision", + [3]byte{0, 26, 8}: "Simoco Ltd.", + [3]byte{0, 26, 9}: "Wayfarer Transit Systems Ltd", + [3]byte{0, 26, 10}: "Adaptive Micro-Ware Inc.", + [3]byte{0, 26, 11}: "BONA TECHNOLOGY INC.", + [3]byte{0, 26, 12}: "Swe-Dish Satellite Systems AB", + [3]byte{0, 26, 13}: "HandHeld entertainment, Inc.", + [3]byte{0, 26, 14}: "Cheng Uei Precision Industry Co.,Ltd", + [3]byte{0, 26, 15}: "Sistemas Avanzados de Control, S.A.", + [3]byte{0, 26, 16}: "LUCENT TRANS ELECTRONICS CO.,LTD", + [3]byte{0, 26, 17}: "Google, Inc.", + [3]byte{0, 26, 18}: "Essilor", + [3]byte{0, 26, 19}: "Wanlida Group Co., LTD", + [3]byte{0, 26, 20}: "Xin Hua Control Engineering Co.,Ltd.", + [3]byte{0, 26, 21}: "gemalto e-Payment", + [3]byte{0, 26, 22}: "Nokia Danmark A/S", + [3]byte{0, 26, 23}: "Teak Technologies, Inc.", + [3]byte{0, 26, 24}: "Advanced Simulation Technology inc.", + [3]byte{0, 26, 25}: "Computer Engineering Limited", + [3]byte{0, 26, 26}: "Gentex Corporation/Electro-Acoustic Products", + [3]byte{0, 26, 27}: "ARRIS Group, Inc.", + [3]byte{0, 26, 28}: "GT&T Engineering Pte Ltd", + [3]byte{0, 26, 29}: "PChome Online Inc.", + [3]byte{0, 26, 30}: "Aruba Networks", + [3]byte{0, 26, 31}: "Coastal Environmental Systems", + [3]byte{0, 26, 32}: "CMOTECH Co. Ltd.", + [3]byte{0, 26, 33}: "Brookhuis Applied Technologies BV", + [3]byte{0, 26, 34}: "eQ-3 Entwicklung GmbH", + [3]byte{0, 26, 35}: "Ice Qube, Inc", + [3]byte{0, 26, 36}: "Galaxy Telecom Technologies Ltd", + [3]byte{0, 26, 37}: "DELTA DORE", + [3]byte{0, 26, 38}: "Deltanode Solutions AB", + [3]byte{0, 26, 39}: "Ubistar", + [3]byte{0, 26, 40}: "ASWT Co., LTD. Taiwan Branch H.K.", + [3]byte{0, 26, 41}: "Johnson Outdoors Marine Electronics d/b/a Minnkota", + [3]byte{0, 26, 42}: "Arcadyan Technology Corporation", + [3]byte{0, 26, 43}: "Ayecom Technology Co., Ltd.", + [3]byte{0, 26, 44}: "SATEC Co.,LTD", + [3]byte{0, 26, 45}: "The Navvo Group", + [3]byte{0, 26, 46}: "Ziova Coporation", + [3]byte{0, 26, 47}: "Cisco Systems, Inc", + [3]byte{0, 26, 48}: "Cisco Systems, Inc", + [3]byte{0, 26, 49}: "SCAN COIN Industries AB", + [3]byte{0, 26, 50}: "ACTIVA MULTIMEDIA", + [3]byte{0, 26, 51}: "ASI Communications, Inc.", + [3]byte{0, 26, 52}: "Konka Group Co., Ltd.", + [3]byte{0, 26, 53}: "BARTEC GmbH", + [3]byte{0, 26, 54}: "Aipermon GmbH & Co. KG", + [3]byte{0, 26, 55}: "Lear Corporation", + [3]byte{0, 26, 56}: "Sanmina-SCI", + [3]byte{0, 26, 57}: "Merten GmbH&CoKG", + [3]byte{0, 26, 58}: "Dongahelecomm", + [3]byte{0, 26, 59}: "Doah Elecom Inc.", + [3]byte{0, 26, 60}: "Technowave Ltd.", + [3]byte{0, 26, 61}: "Ajin Vision Co.,Ltd", + [3]byte{0, 26, 62}: "Faster Technology LLC", + [3]byte{0, 26, 63}: "intelbras", + [3]byte{0, 26, 64}: "A-FOUR TECH CO., LTD.", + [3]byte{0, 26, 65}: "INOCOVA Co.,Ltd", + [3]byte{0, 26, 66}: "Techcity Technology co., Ltd.", + [3]byte{0, 26, 67}: "Logical Link Communications", + [3]byte{0, 26, 68}: "JWTrading Co., Ltd", + [3]byte{0, 26, 69}: "GN Netcom A/S", + [3]byte{0, 26, 70}: "Digital Multimedia Technology Co., Ltd", + [3]byte{0, 26, 71}: "Agami Systems, Inc.", + [3]byte{0, 26, 72}: "Takacom Corporation", + [3]byte{0, 26, 73}: "Micro Vision Co.,LTD", + [3]byte{0, 26, 74}: "Qumranet Inc.", + [3]byte{0, 26, 75}: "Hewlett Packard", + [3]byte{0, 26, 76}: "Crossbow Technology, Inc", + [3]byte{0, 26, 77}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{0, 26, 78}: "NTI AG / LinMot", + [3]byte{0, 26, 79}: "AVM GmbH", + [3]byte{0, 26, 80}: "PheeNet Technology Corp.", + [3]byte{0, 26, 81}: "Alfred Mann Foundation", + [3]byte{0, 26, 82}: "Meshlinx Wireless Inc.", + [3]byte{0, 26, 83}: "Zylaya", + [3]byte{0, 26, 84}: "Hip Shing Electronics Ltd.", + [3]byte{0, 26, 85}: "ACA-Digital Corporation", + [3]byte{0, 26, 86}: "ViewTel Co,. Ltd.", + [3]byte{0, 26, 87}: "Matrix Design Group, LLC", + [3]byte{0, 26, 88}: "CCV Deutschland GmbH - Celectronic eHealth Div.", + [3]byte{0, 26, 89}: "Ircona", + [3]byte{0, 26, 90}: "Korea Electric Power Data Network (KDN) Co., Ltd", + [3]byte{0, 26, 91}: "NetCare Service Co., Ltd.", + [3]byte{0, 26, 92}: "Euchner GmbH+Co. KG", + [3]byte{0, 26, 93}: "Mobinnova Corp.", + [3]byte{0, 26, 94}: "Thincom Technology Co.,Ltd", + [3]byte{0, 26, 95}: "KitWorks.fi Ltd.", + [3]byte{0, 26, 96}: "Wave Electronics Co.,Ltd.", + [3]byte{0, 26, 97}: "PacStar Corp.", + [3]byte{0, 26, 98}: "Data Robotics, Incorporated", + [3]byte{0, 26, 99}: "Elster Solutions, LLC,", + [3]byte{0, 26, 100}: "IBM Corp", + [3]byte{0, 26, 101}: "Seluxit", + [3]byte{0, 26, 102}: "ARRIS Group, Inc.", + [3]byte{0, 26, 103}: "Infinite QL Sdn Bhd", + [3]byte{0, 26, 104}: "Weltec Enterprise Co., Ltd.", + [3]byte{0, 26, 105}: "Wuhan Yangtze Optical Technology CO.,Ltd.", + [3]byte{0, 26, 106}: "Tranzas, Inc.", + [3]byte{0, 26, 107}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{0, 26, 108}: "Cisco Systems, Inc", + [3]byte{0, 26, 109}: "Cisco Systems, Inc", + [3]byte{0, 26, 110}: "Impro Technologies", + [3]byte{0, 26, 111}: "MI.TEL s.r.l.", + [3]byte{0, 26, 112}: "Cisco-Linksys, LLC", + [3]byte{0, 26, 113}: "Diostech Co., Ltd.", + [3]byte{0, 26, 114}: "Mosart Semiconductor Corp.", + [3]byte{0, 26, 115}: "Gemtek Technology Co., Ltd.", + [3]byte{0, 26, 116}: "Procare International Co", + [3]byte{0, 26, 117}: "Sony Mobile Communications AB", + [3]byte{0, 26, 118}: "SDT information Technology Co.,LTD.", + [3]byte{0, 26, 119}: "ARRIS Group, Inc.", + [3]byte{0, 26, 120}: "ubtos", + [3]byte{0, 26, 121}: "TELECOMUNICATION TECHNOLOGIES LTD.", + [3]byte{0, 26, 122}: "Lismore Instruments Limited", + [3]byte{0, 26, 123}: "Teleco, Inc.", + [3]byte{0, 26, 124}: "Hirschmann Multimedia B.V.", + [3]byte{0, 26, 125}: "cyber-blue(HK)Ltd", + [3]byte{0, 26, 126}: "LN Srithai Comm Ltd.", + [3]byte{0, 26, 127}: "GCI Science & Technology Co.,LTD", + [3]byte{0, 26, 128}: "Sony Corporation", + [3]byte{0, 26, 129}: "Zelax", + [3]byte{0, 26, 130}: "PROBA Building Automation Co.,LTD", + [3]byte{0, 26, 131}: "Pegasus Technologies Inc.", + [3]byte{0, 26, 132}: "V One Multimedia Pte Ltd", + [3]byte{0, 26, 133}: "NV Michel Van de Wiele", + [3]byte{0, 26, 134}: "AdvancedIO Systems Inc", + [3]byte{0, 26, 135}: "Canhold International Limited", + [3]byte{0, 26, 136}: "Venergy,Co,Ltd", + [3]byte{0, 26, 137}: "Nokia Danmark A/S", + [3]byte{0, 26, 138}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 26, 139}: "CHUNIL ELECTRIC IND., CO.", + [3]byte{0, 26, 140}: "Sophos Ltd", + [3]byte{0, 26, 141}: "AVECS Bergen GmbH", + [3]byte{0, 26, 142}: "3Way Networks Ltd", + [3]byte{0, 26, 143}: "Nortel Networks", + [3]byte{0, 26, 144}: "Trópico Sistemas e Telecomunicações da Amazônia LTDA. ", + [3]byte{0, 26, 145}: "FusionDynamic Ltd.", + [3]byte{0, 26, 146}: "ASUSTek COMPUTER INC.", + [3]byte{0, 26, 147}: "ERCO Leuchten GmbH", + [3]byte{0, 26, 148}: "Votronic GmbH", + [3]byte{0, 26, 149}: "Hisense Mobile Communications Technoligy Co.,Ltd.", + [3]byte{0, 26, 150}: "ECLER S.A.", + [3]byte{0, 26, 151}: "fitivision technology Inc.", + [3]byte{0, 26, 152}: "Asotel Communication Limited Taiwan Branch", + [3]byte{0, 26, 153}: "Smarty (HZ) Information Electronics Co., Ltd", + [3]byte{0, 26, 154}: "Skyworth Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{0, 26, 155}: "ADEC & Parter AG", + [3]byte{0, 26, 156}: "RightHand Technologies, Inc.", + [3]byte{0, 26, 157}: "Skipper Wireless, Inc.", + [3]byte{0, 26, 158}: "ICON Digital International Limited", + [3]byte{0, 26, 159}: "A-Link Ltd", + [3]byte{0, 26, 160}: "Dell Inc.", + [3]byte{0, 26, 161}: "Cisco Systems, Inc", + [3]byte{0, 26, 162}: "Cisco Systems, Inc", + [3]byte{0, 26, 163}: "DELORME", + [3]byte{0, 26, 164}: "Future University-Hakodate", + [3]byte{0, 26, 165}: "BRN Phoenix", + [3]byte{0, 26, 166}: "Telefunken Radio Communication Systems GmbH &CO.KG", + [3]byte{0, 26, 167}: "Torian Wireless", + [3]byte{0, 26, 168}: "Mamiya Digital Imaging Co., Ltd.", + [3]byte{0, 26, 169}: "FUJIAN STAR-NET COMMUNICATION CO.,LTD", + [3]byte{0, 26, 170}: "Analogic Corp.", + [3]byte{0, 26, 171}: "eWings s.r.l.", + [3]byte{0, 26, 172}: "Corelatus AB", + [3]byte{0, 26, 173}: "ARRIS Group, Inc.", + [3]byte{0, 26, 174}: "Savant Systems LLC", + [3]byte{0, 26, 175}: "BLUSENS TECHNOLOGY", + [3]byte{0, 26, 176}: "Signal Networks Pvt. Ltd.,", + [3]byte{0, 26, 177}: "Asia Pacific Satellite Industries Co., Ltd.", + [3]byte{0, 26, 178}: "Cyber Solutions Inc.", + [3]byte{0, 26, 179}: "VISIONITE INC.", + [3]byte{0, 26, 180}: "FFEI Ltd.", + [3]byte{0, 26, 181}: "Home Network System", + [3]byte{0, 26, 182}: "Texas Instruments", + [3]byte{0, 26, 183}: "Ethos Networks LTD.", + [3]byte{0, 26, 184}: "Anseri Corporation", + [3]byte{0, 26, 185}: "PMC", + [3]byte{0, 26, 186}: "Caton Overseas Limited", + [3]byte{0, 26, 187}: "Fontal Technology Incorporation", + [3]byte{0, 26, 188}: "U4EA Technologies Ltd", + [3]byte{0, 26, 189}: "Impatica Inc.", + [3]byte{0, 26, 190}: "COMPUTER HI-TECH INC.", + [3]byte{0, 26, 191}: "TRUMPF Laser Marking Systems AG", + [3]byte{0, 26, 192}: "JOYBIEN TECHNOLOGIES CO., LTD.", + [3]byte{0, 26, 193}: "3Com Ltd", + [3]byte{0, 26, 194}: "YEC Co.,Ltd.", + [3]byte{0, 26, 195}: "Scientific-Atlanta, Inc", + [3]byte{0, 26, 196}: "2Wire Inc", + [3]byte{0, 26, 197}: "BreakingPoint Systems, Inc.", + [3]byte{0, 26, 198}: "Micro Control Designs", + [3]byte{0, 26, 199}: "UNIPOINT", + [3]byte{0, 26, 200}: "ISL (Instrumentation Scientifique de Laboratoire)", + [3]byte{0, 26, 201}: "SUZUKEN CO.,LTD", + [3]byte{0, 26, 202}: "Tilera Corporation", + [3]byte{0, 26, 203}: "Autocom Products Ltd", + [3]byte{0, 26, 204}: "Celestial Semiconductor, Ltd", + [3]byte{0, 26, 205}: "Tidel Engineering LP", + [3]byte{0, 26, 206}: "YUPITERU CORPORATION", + [3]byte{0, 26, 207}: "C.T. ELETTRONICA", + [3]byte{0, 26, 208}: "Albis Technologies AG", + [3]byte{0, 26, 209}: "FARGO CO., LTD.", + [3]byte{0, 26, 210}: "Eletronica Nitron Ltda", + [3]byte{0, 26, 211}: "Vamp Ltd.", + [3]byte{0, 26, 212}: "iPOX Technology Co., Ltd.", + [3]byte{0, 26, 213}: "KMC CHAIN INDUSTRIAL CO., LTD.", + [3]byte{0, 26, 214}: "JIAGNSU AETNA ELECTRIC CO.,LTD", + [3]byte{0, 26, 215}: "Christie Digital Systems, Inc.", + [3]byte{0, 26, 216}: "AlsterAero GmbH", + [3]byte{0, 26, 217}: "International Broadband Electric Communications, Inc.", + [3]byte{0, 26, 218}: "Biz-2-Me Inc.", + [3]byte{0, 26, 219}: "ARRIS Group, Inc.", + [3]byte{0, 26, 220}: "Nokia Danmark A/S", + [3]byte{0, 26, 221}: "PePWave Ltd", + [3]byte{0, 26, 222}: "ARRIS Group, Inc.", + [3]byte{0, 26, 223}: "Interactivetv Pty Limited", + [3]byte{0, 26, 224}: "Mythology Tech Express Inc.", + [3]byte{0, 26, 225}: "EDGE ACCESS INC", + [3]byte{0, 26, 226}: "Cisco Systems, Inc", + [3]byte{0, 26, 227}: "Cisco Systems, Inc", + [3]byte{0, 26, 228}: "Medicis Technologies Corporation", + [3]byte{0, 26, 229}: "Mvox Technologies Inc.", + [3]byte{0, 26, 230}: "Atlanta Advanced Communications Holdings Limited", + [3]byte{0, 26, 231}: "Aztek Networks, Inc.", + [3]byte{0, 26, 232}: "Unify Software and Solutions GmbH & Co. KG", + [3]byte{0, 26, 233}: "Nintendo Co., Ltd.", + [3]byte{0, 26, 234}: "Radio Terminal Systems Pty Ltd", + [3]byte{0, 26, 235}: "Allied Telesis R&D Center K.K.", + [3]byte{0, 26, 236}: "Keumbee Electronics Co.,Ltd.", + [3]byte{0, 26, 237}: "INCOTEC GmbH", + [3]byte{0, 26, 238}: "Shenztech Ltd", + [3]byte{0, 26, 239}: "Loopcomm Technology, Inc.", + [3]byte{0, 26, 240}: "Alcatel-Lucent IPD", + [3]byte{0, 26, 241}: "Embedded Artists AB", + [3]byte{0, 26, 242}: "Dynavisions Schweiz AG", + [3]byte{0, 26, 243}: "Samyoung Electronics", + [3]byte{0, 26, 244}: "Handreamnet", + [3]byte{0, 26, 245}: "PENTAONE. CO., LTD.", + [3]byte{0, 26, 246}: "Woven Systems, Inc.", + [3]byte{0, 26, 247}: "dataschalt e+a GmbH", + [3]byte{0, 26, 248}: "Copley Controls Corporation", + [3]byte{0, 26, 249}: "AeroVIronment (AV Inc)", + [3]byte{0, 26, 250}: "Welch Allyn, Inc.", + [3]byte{0, 26, 251}: "Joby Inc.", + [3]byte{0, 26, 252}: "ModusLink Corporation", + [3]byte{0, 26, 253}: "EVOLIS", + [3]byte{0, 26, 254}: "SOFACREAL", + [3]byte{0, 26, 255}: "Wizyoung Tech.", + [3]byte{0, 27, 0}: "Neopost Technologies", + [3]byte{0, 27, 1}: "Applied Radio Technologies", + [3]byte{0, 27, 2}: "ED Co.Ltd", + [3]byte{0, 27, 3}: "Action Technology (SZ) Co., Ltd", + [3]byte{0, 27, 4}: "Affinity International S.p.a", + [3]byte{0, 27, 5}: "YMC AG", + [3]byte{0, 27, 6}: "Ateliers R. LAUMONIER", + [3]byte{0, 27, 7}: "Mendocino Software", + [3]byte{0, 27, 8}: "Danfoss Drives A/S", + [3]byte{0, 27, 9}: "Matrix Telecom Pvt. Ltd.", + [3]byte{0, 27, 10}: "Intelligent Distributed Controls Ltd", + [3]byte{0, 27, 11}: "Phidgets Inc.", + [3]byte{0, 27, 12}: "Cisco Systems, Inc", + [3]byte{0, 27, 13}: "Cisco Systems, Inc", + [3]byte{0, 27, 14}: "InoTec GmbH Organisationssysteme", + [3]byte{0, 27, 15}: "Petratec", + [3]byte{0, 27, 16}: "ShenZhen Kang Hui Technology Co.,ltd", + [3]byte{0, 27, 17}: "D-Link Corporation", + [3]byte{0, 27, 18}: "Apprion", + [3]byte{0, 27, 19}: "Icron Technologies Corporation", + [3]byte{0, 27, 20}: "Carex Lighting Equipment Factory", + [3]byte{0, 27, 21}: "Voxtel, Inc.", + [3]byte{0, 27, 22}: "Celtro Ltd.", + [3]byte{0, 27, 23}: "Palo Alto Networks", + [3]byte{0, 27, 24}: "Tsuken Electric Ind. Co.,Ltd", + [3]byte{0, 27, 25}: "IEEE I&M Society TC9", + [3]byte{0, 27, 26}: "e-trees Japan, Inc.", + [3]byte{0, 27, 27}: "Siemens AG,", + [3]byte{0, 27, 28}: "Coherent", + [3]byte{0, 27, 29}: "Phoenix International Co., Ltd", + [3]byte{0, 27, 30}: "HART Communication Foundation", + [3]byte{0, 27, 31}: "DELTA - Danish Electronics, Light & Acoustics", + [3]byte{0, 27, 32}: "TPine Technology", + [3]byte{0, 27, 33}: "Intel Corporate", + [3]byte{0, 27, 34}: "Palit Microsystems ( H.K.) Ltd.", + [3]byte{0, 27, 35}: "SimpleComTools", + [3]byte{0, 27, 36}: "QUANTA COMPUTER INC.", + [3]byte{0, 27, 37}: "Nortel Networks", + [3]byte{0, 27, 38}: "RON-Telecom ZAO", + [3]byte{0, 27, 39}: "Merlin CSI", + [3]byte{0, 27, 40}: "POLYGON, JSC", + [3]byte{0, 27, 41}: "Avantis.Co.,Ltd", + [3]byte{0, 27, 42}: "Cisco Systems, Inc", + [3]byte{0, 27, 43}: "Cisco Systems, Inc", + [3]byte{0, 27, 44}: "ATRON electronic GmbH", + [3]byte{0, 27, 45}: "Med-Eng Systems Inc.", + [3]byte{0, 27, 46}: "Sinkyo Electron Inc", + [3]byte{0, 27, 47}: "NETGEAR", + [3]byte{0, 27, 48}: "Solitech Inc.", + [3]byte{0, 27, 49}: "Neural Image. Co. Ltd.", + [3]byte{0, 27, 50}: "QLogic Corporation", + [3]byte{0, 27, 51}: "Nokia Danmark A/S", + [3]byte{0, 27, 52}: "Focus System Inc.", + [3]byte{0, 27, 53}: "ChongQing JINOU Science & Technology Development CO.,Ltd", + [3]byte{0, 27, 54}: "Tsubata Engineering Co.,Ltd. (Head Office)", + [3]byte{0, 27, 55}: "Computec Oy", + [3]byte{0, 27, 56}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{0, 27, 57}: "Proxicast", + [3]byte{0, 27, 58}: "SIMS Corp.", + [3]byte{0, 27, 59}: "Yi-Qing CO., LTD", + [3]byte{0, 27, 60}: "Software Technologies Group,Inc.", + [3]byte{0, 27, 61}: "EuroTel Spa", + [3]byte{0, 27, 62}: "Curtis, Inc.", + [3]byte{0, 27, 63}: "ProCurve Networking by HP", + [3]byte{0, 27, 64}: "Network Automation mxc AB", + [3]byte{0, 27, 65}: "General Infinity Co.,Ltd.", + [3]byte{0, 27, 66}: "Wise & Blue", + [3]byte{0, 27, 67}: "Beijing DG Telecommunications equipment Co.,Ltd", + [3]byte{0, 27, 68}: "SanDisk Corporation", + [3]byte{0, 27, 69}: "ABB AS, Division Automation Products", + [3]byte{0, 27, 70}: "Blueone Technology Co.,Ltd", + [3]byte{0, 27, 71}: "Futarque A/S", + [3]byte{0, 27, 72}: "Shenzhen Lantech Electronics Co., Ltd.", + [3]byte{0, 27, 73}: "Roberts Radio limited", + [3]byte{0, 27, 74}: "W&W Communications, Inc.", + [3]byte{0, 27, 75}: "SANION Co., Ltd.", + [3]byte{0, 27, 76}: "Signtech", + [3]byte{0, 27, 77}: "Areca Technology Corporation", + [3]byte{0, 27, 78}: "Navman New Zealand", + [3]byte{0, 27, 79}: "Avaya Inc", + [3]byte{0, 27, 80}: "Nizhny Novgorod Factory named after M.Frunze, FSUE (NZiF)", + [3]byte{0, 27, 81}: "Vector Technology Corp.", + [3]byte{0, 27, 82}: "ARRIS Group, Inc.", + [3]byte{0, 27, 83}: "Cisco Systems, Inc", + [3]byte{0, 27, 84}: "Cisco Systems, Inc", + [3]byte{0, 27, 85}: "Hurco Automation Ltd.", + [3]byte{0, 27, 86}: "Tehuti Networks Ltd.", + [3]byte{0, 27, 87}: "SEMINDIA SYSTEMS PRIVATE LIMITED", + [3]byte{0, 27, 88}: "ACE CAD Enterprise Co., Ltd.", + [3]byte{0, 27, 89}: "Sony Mobile Communications AB", + [3]byte{0, 27, 90}: "Apollo Imaging Technologies, Inc.", + [3]byte{0, 27, 91}: "2Wire Inc", + [3]byte{0, 27, 92}: "Azuretec Co., Ltd.", + [3]byte{0, 27, 93}: "Vololink Pty Ltd", + [3]byte{0, 27, 94}: "BPL Limited", + [3]byte{0, 27, 95}: "Alien Technology", + [3]byte{0, 27, 96}: "NAVIGON AG", + [3]byte{0, 27, 97}: "Digital Acoustics, LLC", + [3]byte{0, 27, 98}: "JHT Optoelectronics Co.,Ltd.", + [3]byte{0, 27, 99}: "Apple, Inc.", + [3]byte{0, 27, 100}: "IsaacLandKorea Co., Ltd,", + [3]byte{0, 27, 101}: "China Gridcom Co., Ltd", + [3]byte{0, 27, 102}: "Sennheiser electronic GmbH & Co. KG", + [3]byte{0, 27, 103}: "Cisco Systems Inc", + [3]byte{0, 27, 104}: "Modnnet Co., Ltd", + [3]byte{0, 27, 105}: "Equaline Corporation", + [3]byte{0, 27, 106}: "Powerwave Technologies Sweden AB", + [3]byte{0, 27, 107}: "Swyx Solutions AG", + [3]byte{0, 27, 108}: "LookX Digital Media BV", + [3]byte{0, 27, 109}: "Midtronics, Inc.", + [3]byte{0, 27, 110}: "Anue Systems, Inc.", + [3]byte{0, 27, 111}: "Teletrak Ltd", + [3]byte{0, 27, 112}: "IRI Ubiteq, INC.", + [3]byte{0, 27, 113}: "Telular Corp.", + [3]byte{0, 27, 114}: "Sicep s.p.a.", + [3]byte{0, 27, 115}: "DTL Broadcast Ltd", + [3]byte{0, 27, 116}: "MiraLink Corporation", + [3]byte{0, 27, 117}: "Hypermedia Systems", + [3]byte{0, 27, 118}: "Ripcode, Inc.", + [3]byte{0, 27, 119}: "Intel Corporate", + [3]byte{0, 27, 120}: "Hewlett Packard", + [3]byte{0, 27, 121}: "FAIVELEY TRANSPORT", + [3]byte{0, 27, 122}: "Nintendo Co., Ltd.", + [3]byte{0, 27, 123}: "The Tintometer Ltd", + [3]byte{0, 27, 124}: "A & R Cambridge", + [3]byte{0, 27, 125}: "CXR Anderson Jacobson", + [3]byte{0, 27, 126}: "Beckmann GmbH", + [3]byte{0, 27, 127}: "TMN Technologies Telecomunicacoes Ltda", + [3]byte{0, 27, 128}: "LORD Corporation", + [3]byte{0, 27, 129}: "DATAQ Instruments, Inc.", + [3]byte{0, 27, 130}: "Taiwan Semiconductor Co., Ltd.", + [3]byte{0, 27, 131}: "Finsoft Ltd", + [3]byte{0, 27, 132}: "Scan Engineering Telecom", + [3]byte{0, 27, 133}: "MAN Diesel SE", + [3]byte{0, 27, 134}: "Bosch Access Systems GmbH", + [3]byte{0, 27, 135}: "Deepsound Tech. Co., Ltd", + [3]byte{0, 27, 136}: "Divinet Access Technologies Ltd", + [3]byte{0, 27, 137}: "EMZA Visual Sense Ltd.", + [3]byte{0, 27, 138}: "2M Electronic A/S", + [3]byte{0, 27, 139}: "NEC Platforms, Ltd.", + [3]byte{0, 27, 140}: "JMicron Technology Corp.", + [3]byte{0, 27, 141}: "Electronic Computer Systems, Inc.", + [3]byte{0, 27, 142}: "Hulu Sweden AB", + [3]byte{0, 27, 143}: "Cisco Systems, Inc", + [3]byte{0, 27, 144}: "Cisco Systems, Inc", + [3]byte{0, 27, 145}: "EFKON AG", + [3]byte{0, 27, 146}: "l-acoustics", + [3]byte{0, 27, 147}: "JC Decaux SA DNT", + [3]byte{0, 27, 148}: "T.E.M.A. S.p.A.", + [3]byte{0, 27, 149}: "VIDEO SYSTEMS SRL", + [3]byte{0, 27, 150}: "General Sensing", + [3]byte{0, 27, 151}: "Violin Technologies", + [3]byte{0, 27, 152}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 27, 153}: "KS System GmbH", + [3]byte{0, 27, 154}: "Apollo Fire Detectors Ltd", + [3]byte{0, 27, 155}: "Hose-McCann Communications", + [3]byte{0, 27, 156}: "SATEL sp. z o.o.", + [3]byte{0, 27, 157}: "Novus Security Sp. z o.o.", + [3]byte{0, 27, 158}: "ASKEY COMPUTER CORP", + [3]byte{0, 27, 159}: "Calyptech Pty Ltd", + [3]byte{0, 27, 160}: "Awox", + [3]byte{0, 27, 161}: "Åmic AB", + [3]byte{0, 27, 162}: "IDS Imaging Development Systems GmbH", + [3]byte{0, 27, 163}: "Flexit Group GmbH", + [3]byte{0, 27, 164}: "S.A.E Afikim", + [3]byte{0, 27, 165}: "MyungMin Systems, Inc.", + [3]byte{0, 27, 166}: "intotech inc.", + [3]byte{0, 27, 167}: "Lorica Solutions", + [3]byte{0, 27, 168}: "UBI&MOBI,.Inc", + [3]byte{0, 27, 169}: "Brother industries, LTD.", + [3]byte{0, 27, 170}: "XenICs nv", + [3]byte{0, 27, 171}: "Telchemy, Incorporated", + [3]byte{0, 27, 172}: "Curtiss Wright Controls Embedded Computing", + [3]byte{0, 27, 173}: "iControl Incorporated", + [3]byte{0, 27, 174}: "Micro Control Systems, Inc", + [3]byte{0, 27, 175}: "Nokia Danmark A/S", + [3]byte{0, 27, 176}: "BHARAT ELECTRONICS", + [3]byte{0, 27, 177}: "Wistron Neweb Corporation", + [3]byte{0, 27, 178}: "Intellect International NV", + [3]byte{0, 27, 179}: "Condalo GmbH", + [3]byte{0, 27, 180}: "Airvod Limited", + [3]byte{0, 27, 181}: "Cherry GmbH", + [3]byte{0, 27, 182}: "Bird Electronic Corp.", + [3]byte{0, 27, 183}: "Alta Heights Technology Corp.", + [3]byte{0, 27, 184}: "BLUEWAY ELECTRONIC CO;LTD", + [3]byte{0, 27, 185}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 27, 186}: "Nortel Networks", + [3]byte{0, 27, 187}: "RFTech Co.,Ltd", + [3]byte{0, 27, 188}: "Silver Peak Systems, Inc.", + [3]byte{0, 27, 189}: "FMC Kongsberg Subsea AS", + [3]byte{0, 27, 190}: "ICOP Digital", + [3]byte{0, 27, 191}: "Sagemcom Broadband SAS", + [3]byte{0, 27, 192}: "Juniper Networks", + [3]byte{0, 27, 193}: "HOLUX Technology, Inc.", + [3]byte{0, 27, 194}: "Integrated Control Technology Limitied", + [3]byte{0, 27, 195}: "Mobisolution Co.,Ltd", + [3]byte{0, 27, 196}: "Ultratec, Inc.", + [3]byte{0, 27, 197}: "IEEE Registration Authority", + [3]byte{0, 27, 198}: "Strato Rechenzentrum AG", + [3]byte{0, 27, 199}: "StarVedia Technology Inc.", + [3]byte{0, 27, 200}: "MIURA CO.,LTD", + [3]byte{0, 27, 201}: "FSN DISPLAY INC", + [3]byte{0, 27, 202}: "Beijing Run Technology LTD. Company", + [3]byte{0, 27, 203}: "PEMPEK SYSTEMS PTY LTD", + [3]byte{0, 27, 204}: "KINGTEK CCTV ALLIANCE CO., LTD.", + [3]byte{0, 27, 205}: "DAVISCOMMS (S) PTE LTD", + [3]byte{0, 27, 206}: "Measurement Devices Ltd", + [3]byte{0, 27, 207}: "Dataupia Corporation", + [3]byte{0, 27, 208}: "IDENTEC SOLUTIONS", + [3]byte{0, 27, 209}: "SOGESTMATIC", + [3]byte{0, 27, 210}: "ULTRA-X ASIA PACIFIC Inc.", + [3]byte{0, 27, 211}: "Panasonic Corp. AVC Company", + [3]byte{0, 27, 212}: "Cisco Systems, Inc", + [3]byte{0, 27, 213}: "Cisco Systems, Inc", + [3]byte{0, 27, 214}: "Kelvin Hughes Ltd", + [3]byte{0, 27, 215}: "Cisco SPVTG", + [3]byte{0, 27, 216}: "DVTel LTD", + [3]byte{0, 27, 217}: "Edgewater Computer Systems", + [3]byte{0, 27, 218}: "UTStarcom Inc", + [3]byte{0, 27, 219}: "Valeo VECS", + [3]byte{0, 27, 220}: "Vencer Co., Ltd.", + [3]byte{0, 27, 221}: "ARRIS Group, Inc.", + [3]byte{0, 27, 222}: "Renkus-Heinz, Inc.", + [3]byte{0, 27, 223}: "Iskra Sistemi d.d.", + [3]byte{0, 27, 224}: "TELENOT ELECTRONIC GmbH", + [3]byte{0, 27, 225}: "ViaLogy", + [3]byte{0, 27, 226}: "AhnLab,Inc.", + [3]byte{0, 27, 227}: "Health Hero Network, Inc.", + [3]byte{0, 27, 228}: "TOWNET SRL", + [3]byte{0, 27, 229}: "802automation Limited", + [3]byte{0, 27, 230}: "VR AG", + [3]byte{0, 27, 231}: "Postek Electronics Co., Ltd.", + [3]byte{0, 27, 232}: "Ultratronik GmbH", + [3]byte{0, 27, 233}: "Broadcom", + [3]byte{0, 27, 234}: "Nintendo Co., Ltd.", + [3]byte{0, 27, 235}: "DMP Electronics INC.", + [3]byte{0, 27, 236}: "Netio Technologies Co., Ltd", + [3]byte{0, 27, 237}: "Brocade Communications Systems, Inc.", + [3]byte{0, 27, 238}: "Nokia Danmark A/S", + [3]byte{0, 27, 239}: "Blossoms Digital Technology Co.,Ltd.", + [3]byte{0, 27, 240}: "Value Platforms Limited", + [3]byte{0, 27, 241}: "Nanjing SilverNet Software Co., Ltd.", + [3]byte{0, 27, 242}: "KWORLD COMPUTER CO., LTD", + [3]byte{0, 27, 243}: "TRANSRADIO SenderSysteme Berlin AG", + [3]byte{0, 27, 244}: "KENWIN INDUSTRIAL(HK) LTD.", + [3]byte{0, 27, 245}: "Tellink Sistemas de Telecomunicación S.L.", + [3]byte{0, 27, 246}: "CONWISE Technology Corporation Ltd.", + [3]byte{0, 27, 247}: "Lund IP Products AB", + [3]byte{0, 27, 248}: "Digitrax Inc.", + [3]byte{0, 27, 249}: "Intellitect Water Ltd", + [3]byte{0, 27, 250}: "G.i.N. mbH", + [3]byte{0, 27, 251}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 27, 252}: "ASUSTek COMPUTER INC.", + [3]byte{0, 27, 253}: "Dignsys Inc.", + [3]byte{0, 27, 254}: "Zavio Inc.", + [3]byte{0, 27, 255}: "Millennia Media inc.", + [3]byte{0, 28, 0}: "Entry Point, LLC", + [3]byte{0, 28, 1}: "ABB Oy Drives", + [3]byte{0, 28, 2}: "Pano Logic", + [3]byte{0, 28, 3}: "Betty TV Technology AG", + [3]byte{0, 28, 4}: "Airgain, Inc.", + [3]byte{0, 28, 5}: "Nonin Medical Inc.", + [3]byte{0, 28, 6}: "Siemens Numerical Control Ltd., Nanjing", + [3]byte{0, 28, 7}: "Cwlinux Limited", + [3]byte{0, 28, 8}: "Echo360, Inc.", + [3]byte{0, 28, 9}: "SAE Electronic Co.,Ltd.", + [3]byte{0, 28, 10}: "Shenzhen AEE Technology Co.,Ltd.", + [3]byte{0, 28, 11}: "SmartAnt Telecom", + [3]byte{0, 28, 12}: "TANITA Corporation", + [3]byte{0, 28, 13}: "G-Technology, Inc.", + [3]byte{0, 28, 14}: "Cisco Systems, Inc", + [3]byte{0, 28, 15}: "Cisco Systems, Inc", + [3]byte{0, 28, 16}: "Cisco-Linksys, LLC", + [3]byte{0, 28, 17}: "ARRIS Group, Inc.", + [3]byte{0, 28, 18}: "ARRIS Group, Inc.", + [3]byte{0, 28, 19}: "OPTSYS TECHNOLOGY CO., LTD.", + [3]byte{0, 28, 20}: "VMware, Inc.", + [3]byte{0, 28, 21}: "iPhotonix LLC", + [3]byte{0, 28, 22}: "ThyssenKrupp Elevator", + [3]byte{0, 28, 23}: "Nortel Networks", + [3]byte{0, 28, 24}: "Sicert S.r.L.", + [3]byte{0, 28, 25}: "secunet Security Networks AG", + [3]byte{0, 28, 26}: "Thomas Instrumentation, Inc", + [3]byte{0, 28, 27}: "Hyperstone GmbH", + [3]byte{0, 28, 28}: "Center Communication Systems GmbH", + [3]byte{0, 28, 29}: "CHENZHOU GOSPELL DIGITAL TECHNOLOGY CO.,LTD", + [3]byte{0, 28, 30}: "emtrion GmbH", + [3]byte{0, 28, 31}: "Quest Retail Technology Pty Ltd", + [3]byte{0, 28, 32}: "CLB Benelux", + [3]byte{0, 28, 33}: "Nucsafe Inc.", + [3]byte{0, 28, 34}: "Aeris Elettronica s.r.l.", + [3]byte{0, 28, 35}: "Dell Inc.", + [3]byte{0, 28, 36}: "Formosa Wireless Systems Corp.", + [3]byte{0, 28, 37}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 28, 38}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 28, 39}: "Sunell Electronics Co.", + [3]byte{0, 28, 40}: "Sphairon Technologies GmbH ", + [3]byte{0, 28, 41}: "CORE DIGITAL ELECTRONICS CO., LTD", + [3]byte{0, 28, 42}: "Envisacor Technologies Inc.", + [3]byte{0, 28, 43}: "Alertme.com Limited", + [3]byte{0, 28, 44}: "Synapse", + [3]byte{0, 28, 45}: "FlexRadio Systems", + [3]byte{0, 28, 46}: "HPN Supply Chain", + [3]byte{0, 28, 47}: "Pfister GmbH", + [3]byte{0, 28, 48}: "Mode Lighting (UK ) Ltd.", + [3]byte{0, 28, 49}: "Mobile XP Technology Co., LTD", + [3]byte{0, 28, 50}: "Telian Corporation", + [3]byte{0, 28, 51}: "Sutron", + [3]byte{0, 28, 52}: "HUEY CHIAO INTERNATIONAL CO., LTD.", + [3]byte{0, 28, 53}: "Nokia Danmark A/S", + [3]byte{0, 28, 54}: "iNEWiT NV", + [3]byte{0, 28, 55}: "Callpod, Inc.", + [3]byte{0, 28, 56}: "Bio-Rad Laboratories, Inc.", + [3]byte{0, 28, 57}: "S Netsystems Inc.", + [3]byte{0, 28, 58}: "Element Labs, Inc.", + [3]byte{0, 28, 59}: "AmRoad Technology Inc.", + [3]byte{0, 28, 60}: "Seon Design Inc.", + [3]byte{0, 28, 61}: "WaveStorm", + [3]byte{0, 28, 62}: "ECKey Corporation", + [3]byte{0, 28, 63}: "International Police Technologies, Inc.", + [3]byte{0, 28, 64}: "VDG-Security bv", + [3]byte{0, 28, 65}: "scemtec Transponder Technology GmbH", + [3]byte{0, 28, 66}: "Parallels, Inc.", + [3]byte{0, 28, 67}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 28, 68}: "Bosch Security Systems BV", + [3]byte{0, 28, 69}: "Chenbro Micom Co., Ltd.", + [3]byte{0, 28, 70}: "QTUM", + [3]byte{0, 28, 71}: "Hangzhou Hollysys Automation Co., Ltd", + [3]byte{0, 28, 72}: "WiDeFi, Inc.", + [3]byte{0, 28, 73}: "Zoltan Technology Inc.", + [3]byte{0, 28, 74}: "AVM GmbH", + [3]byte{0, 28, 75}: "Gener8, Inc.", + [3]byte{0, 28, 76}: "Petrotest Instruments", + [3]byte{0, 28, 77}: "Aplix IP Holdings Corporation", + [3]byte{0, 28, 78}: "TASA International Limited", + [3]byte{0, 28, 79}: "MACAB AB", + [3]byte{0, 28, 80}: "TCL Technoly Electronics (Huizhou) Co., Ltd.", + [3]byte{0, 28, 81}: "Celeno Communications", + [3]byte{0, 28, 82}: "VISIONEE SRL", + [3]byte{0, 28, 83}: "Synergy Lighting Controls", + [3]byte{0, 28, 84}: "Hillstone Networks Inc", + [3]byte{0, 28, 85}: "Shenzhen Kaifa Technology Co.", + [3]byte{0, 28, 86}: "Pado Systems, Inc.", + [3]byte{0, 28, 87}: "Cisco Systems, Inc", + [3]byte{0, 28, 88}: "Cisco Systems, Inc", + [3]byte{0, 28, 89}: "DEVON IT", + [3]byte{0, 28, 90}: "Advanced Relay Corporation", + [3]byte{0, 28, 91}: "Chubb Electronic Security Systems Ltd", + [3]byte{0, 28, 92}: "Integrated Medical Systems, Inc.", + [3]byte{0, 28, 93}: "Leica Microsystems", + [3]byte{0, 28, 94}: "ASTON France", + [3]byte{0, 28, 95}: "Winland Electronics, Inc.", + [3]byte{0, 28, 96}: "CSP Frontier Technologies,Inc.", + [3]byte{0, 28, 97}: "Galaxy Microsystems LImited", + [3]byte{0, 28, 98}: "LG Electronics (Mobile Communications)", + [3]byte{0, 28, 99}: "TRUEN", + [3]byte{0, 28, 100}: "Landis+Gyr", + [3]byte{0, 28, 101}: "JoeScan, Inc.", + [3]byte{0, 28, 102}: "UCAMP CO.,LTD", + [3]byte{0, 28, 103}: "Pumpkin Networks, Inc.", + [3]byte{0, 28, 104}: "Anhui Sun Create Electronics Co., Ltd", + [3]byte{0, 28, 105}: "Packet Vision Ltd", + [3]byte{0, 28, 106}: "Weiss Engineering Ltd.", + [3]byte{0, 28, 107}: "COVAX Co. Ltd", + [3]byte{0, 28, 108}: "30805", + [3]byte{0, 28, 109}: "KYOHRITSU ELECTRONIC INDUSTRY CO., LTD.", + [3]byte{0, 28, 110}: "Newbury Networks, Inc.", + [3]byte{0, 28, 111}: "Emfit Ltd", + [3]byte{0, 28, 112}: "NOVACOMM LTDA", + [3]byte{0, 28, 113}: "Emergent Electronics", + [3]byte{0, 28, 114}: "Mayer & Cie GmbH & Co KG", + [3]byte{0, 28, 115}: "Arista Networks, Inc.", + [3]byte{0, 28, 116}: "Syswan Technologies Inc.", + [3]byte{0, 28, 117}: "Segnet Ltd.", + [3]byte{0, 28, 118}: "The Wandsworth Group Ltd", + [3]byte{0, 28, 119}: "Prodys", + [3]byte{0, 28, 120}: "WYPLAY SAS", + [3]byte{0, 28, 121}: "Cohesive Financial Technologies LLC", + [3]byte{0, 28, 122}: "Perfectone Netware Company Ltd", + [3]byte{0, 28, 123}: "Castlenet Technology Inc.", + [3]byte{0, 28, 124}: "PERQ SYSTEMS CORPORATION", + [3]byte{0, 28, 125}: "Excelpoint Manufacturing Pte Ltd", + [3]byte{0, 28, 126}: "Toshiba", + [3]byte{0, 28, 127}: "Check Point Software Technologies", + [3]byte{0, 28, 128}: "New Business Division/Rhea-Information CO., LTD.", + [3]byte{0, 28, 129}: "NextGen Venturi LTD", + [3]byte{0, 28, 130}: "Genew Technologies", + [3]byte{0, 28, 131}: "New Level Telecom Co., Ltd.", + [3]byte{0, 28, 132}: "STL Solution Co.,Ltd.", + [3]byte{0, 28, 133}: "Eunicorn", + [3]byte{0, 28, 134}: "Cranite Systems, Inc.", + [3]byte{0, 28, 135}: "Uriver Inc.", + [3]byte{0, 28, 136}: "TRANSYSTEM INC.", + [3]byte{0, 28, 137}: "Force Communications, Inc.", + [3]byte{0, 28, 138}: "Cirrascale Corporation", + [3]byte{0, 28, 139}: "MJ Innovations Ltd.", + [3]byte{0, 28, 140}: "DIAL TECHNOLOGY LTD.", + [3]byte{0, 28, 141}: "Mesa Imaging", + [3]byte{0, 28, 142}: "Alcatel-Lucent IPD", + [3]byte{0, 28, 143}: "Advanced Electronic Design, Inc.", + [3]byte{0, 28, 144}: "Empacket Corporation", + [3]byte{0, 28, 145}: "Gefen Inc.", + [3]byte{0, 28, 146}: "Tervela", + [3]byte{0, 28, 147}: "ExaDigm Inc", + [3]byte{0, 28, 148}: "LI-COR Biosciences", + [3]byte{0, 28, 149}: "Opticomm Corporation", + [3]byte{0, 28, 150}: "Linkwise Technology Pte Ltd", + [3]byte{0, 28, 151}: "Enzytek Technology Inc.,", + [3]byte{0, 28, 152}: "LUCKY TECHNOLOGY (HK) COMPANY LIMITED", + [3]byte{0, 28, 153}: "Shunra Software Ltd.", + [3]byte{0, 28, 154}: "Nokia Danmark A/S", + [3]byte{0, 28, 155}: "FEIG ELECTRONIC GmbH", + [3]byte{0, 28, 156}: "Nortel Networks", + [3]byte{0, 28, 157}: "Liecthi AG", + [3]byte{0, 28, 158}: "Dualtech IT AB", + [3]byte{0, 28, 159}: "Razorstream, LLC", + [3]byte{0, 28, 160}: "Production Resource Group, LLC", + [3]byte{0, 28, 161}: "AKAMAI TECHNOLOGIES, INC.", + [3]byte{0, 28, 162}: "ADB Broadband Italia", + [3]byte{0, 28, 163}: "Terra", + [3]byte{0, 28, 164}: "Sony Mobile Communications AB", + [3]byte{0, 28, 165}: "Zygo Corporation", + [3]byte{0, 28, 166}: "Win4NET", + [3]byte{0, 28, 167}: "International Quartz Limited", + [3]byte{0, 28, 168}: "AirTies Wireless Networks", + [3]byte{0, 28, 169}: "Audiomatica Srl", + [3]byte{0, 28, 170}: "Bellon Pty Ltd", + [3]byte{0, 28, 171}: "Meyer Sound Laboratories, Inc.", + [3]byte{0, 28, 172}: "Qniq Technology Corp.", + [3]byte{0, 28, 173}: "Wuhan Telecommunication Devices Co.,Ltd", + [3]byte{0, 28, 174}: "WiChorus, Inc.", + [3]byte{0, 28, 175}: "Plato Networks Inc.", + [3]byte{0, 28, 176}: "Cisco Systems, Inc", + [3]byte{0, 28, 177}: "Cisco Systems, Inc", + [3]byte{0, 28, 178}: "BPT SPA", + [3]byte{0, 28, 179}: "Apple, Inc.", + [3]byte{0, 28, 180}: "Iridium Satellite LLC", + [3]byte{0, 28, 181}: "Neihua Network Technology Co.,LTD.(NHN)", + [3]byte{0, 28, 182}: "Duzon CNT Co., Ltd.", + [3]byte{0, 28, 183}: "USC DigiArk Corporation", + [3]byte{0, 28, 184}: "CBC Co., Ltd", + [3]byte{0, 28, 185}: "KWANG SUNG ELECTRONICS CO., LTD.", + [3]byte{0, 28, 186}: "VerScient, Inc.", + [3]byte{0, 28, 187}: "MusicianLink", + [3]byte{0, 28, 188}: "CastGrabber, LLC", + [3]byte{0, 28, 189}: "Ezze Mobile Tech., Inc.", + [3]byte{0, 28, 190}: "Nintendo Co., Ltd.", + [3]byte{0, 28, 191}: "Intel Corporate", + [3]byte{0, 28, 192}: "Intel Corporate", + [3]byte{0, 28, 193}: "ARRIS Group, Inc.", + [3]byte{0, 28, 194}: "Part II Research, Inc.", + [3]byte{0, 28, 195}: "ARRIS Group, Inc.", + [3]byte{0, 28, 196}: "Hewlett Packard", + [3]byte{0, 28, 197}: "3Com Ltd", + [3]byte{0, 28, 198}: "ProStor Systems", + [3]byte{0, 28, 199}: "Rembrandt Technologies, LLC d/b/a REMSTREAM", + [3]byte{0, 28, 200}: "INDUSTRONIC Industrie-Electronic GmbH & Co. KG", + [3]byte{0, 28, 201}: "Kaise Electronic Technology Co., Ltd.", + [3]byte{0, 28, 202}: "Shanghai Gaozhi Science & Technology Development Co.", + [3]byte{0, 28, 203}: "Forth Corporation Public Company Limited", + [3]byte{0, 28, 204}: "BlackBerry RTS", + [3]byte{0, 28, 205}: "Alektrona Corporation", + [3]byte{0, 28, 206}: "By Techdesign", + [3]byte{0, 28, 207}: "LIMETEK", + [3]byte{0, 28, 208}: "Circleone Co.,Ltd.", + [3]byte{0, 28, 209}: "Waves Audio LTD", + [3]byte{0, 28, 210}: "King Champion (Hong Kong) Limited", + [3]byte{0, 28, 211}: "ZP Engineering SEL", + [3]byte{0, 28, 212}: "Nokia Danmark A/S", + [3]byte{0, 28, 213}: "ZeeVee, Inc.", + [3]byte{0, 28, 214}: "Nokia Danmark A/S", + [3]byte{0, 28, 215}: "Harman/Becker Automotive Systems GmbH", + [3]byte{0, 28, 216}: "BlueAnt Wireless", + [3]byte{0, 28, 217}: "GlobalTop Technology Inc.", + [3]byte{0, 28, 218}: "Exegin Technologies Limited", + [3]byte{0, 28, 219}: "CARPOINT CO.,LTD", + [3]byte{0, 28, 220}: "Custom Computer Services, Inc.", + [3]byte{0, 28, 221}: "COWBELL ENGINEERING CO., LTD.", + [3]byte{0, 28, 222}: "Interactive Multimedia eXchange Inc.", + [3]byte{0, 28, 223}: "Belkin International Inc.", + [3]byte{0, 28, 224}: "DASAN TPS", + [3]byte{0, 28, 225}: "INDRA SISTEMAS, S.A.", + [3]byte{0, 28, 226}: "Attero Tech, LLC.", + [3]byte{0, 28, 227}: "Optimedical Systems", + [3]byte{0, 28, 228}: "EleSy JSC", + [3]byte{0, 28, 229}: "MBS Electronic Systems GmbH", + [3]byte{0, 28, 230}: "INNES", + [3]byte{0, 28, 231}: "Rocon PLC Research Centre", + [3]byte{0, 28, 232}: "Cummins Inc", + [3]byte{0, 28, 233}: "Galaxy Technology Limited", + [3]byte{0, 28, 234}: "Scientific-Atlanta, Inc", + [3]byte{0, 28, 235}: "Nortel Networks", + [3]byte{0, 28, 236}: "Mobilesoft (Aust.) Pty Ltd", + [3]byte{0, 28, 237}: "ENVIRONNEMENT SA", + [3]byte{0, 28, 238}: "SHARP Corporation", + [3]byte{0, 28, 239}: "Primax Electronics Ltd.", + [3]byte{0, 28, 240}: "D-Link Corporation", + [3]byte{0, 28, 241}: "SUPoX Technology Co. , LTD.", + [3]byte{0, 28, 242}: "Tenlon Technology Co.,Ltd.", + [3]byte{0, 28, 243}: "EVS BROADCAST EQUIPMENT", + [3]byte{0, 28, 244}: "Media Technology Systems Inc", + [3]byte{0, 28, 245}: "Wiseblue Technology Limited", + [3]byte{0, 28, 246}: "Cisco Systems, Inc", + [3]byte{0, 28, 247}: "AudioScience", + [3]byte{0, 28, 248}: "Parade Technologies, Ltd.", + [3]byte{0, 28, 249}: "Cisco Systems, Inc", + [3]byte{0, 28, 250}: "Alarm.com", + [3]byte{0, 28, 251}: "ARRIS Group, Inc.", + [3]byte{0, 28, 252}: "Sumitomo Electric Industries,Ltd", + [3]byte{0, 28, 253}: "Universal Electronics, Inc.", + [3]byte{0, 28, 254}: "Quartics Inc", + [3]byte{0, 28, 255}: "Napera Networks Inc", + [3]byte{0, 29, 0}: "Brivo Systems, LLC", + [3]byte{0, 29, 1}: "Neptune Digital", + [3]byte{0, 29, 2}: "Cybertech Telecom Development", + [3]byte{0, 29, 3}: "Design Solutions Inc.", + [3]byte{0, 29, 4}: "Zipit Wireless, Inc.", + [3]byte{0, 29, 5}: "Eaton Corporation", + [3]byte{0, 29, 6}: "HM Electronics, Inc.", + [3]byte{0, 29, 7}: "Shenzhen Sang Fei Consumer Communications Co.,Ltd", + [3]byte{0, 29, 8}: "Jiangsu Yinhe Electronics Co.,Ltd.", + [3]byte{0, 29, 9}: "Dell Inc.", + [3]byte{0, 29, 10}: "Davis Instruments, Inc.", + [3]byte{0, 29, 11}: "Power Standards Lab", + [3]byte{0, 29, 12}: "MobileCompia", + [3]byte{0, 29, 13}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 29, 14}: "Agapha Technology co., Ltd.", + [3]byte{0, 29, 15}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 29, 16}: "LightHaus Logic, Inc.", + [3]byte{0, 29, 17}: "Analogue & Micro Ltd", + [3]byte{0, 29, 18}: "ROHM CO., LTD.", + [3]byte{0, 29, 19}: "NextGTV", + [3]byte{0, 29, 20}: "SPERADTONE INFORMATION TECHNOLOGY LIMITED", + [3]byte{0, 29, 21}: "Shenzhen Dolphin Electronic Co., Ltd", + [3]byte{0, 29, 22}: "SFR", + [3]byte{0, 29, 23}: "Digital Sky Corporation", + [3]byte{0, 29, 24}: "Power Innovation GmbH", + [3]byte{0, 29, 25}: "Arcadyan Technology Corporation", + [3]byte{0, 29, 26}: "OvisLink S.A.", + [3]byte{0, 29, 27}: "Sangean Electronics Inc.", + [3]byte{0, 29, 28}: "Gennet s.a.", + [3]byte{0, 29, 29}: "Inter-M Corporation", + [3]byte{0, 29, 30}: "KYUSHU TEN CO.,LTD", + [3]byte{0, 29, 31}: "Siauliu Tauro Televizoriai, JSC", + [3]byte{0, 29, 32}: "Comtrend Corporation", + [3]byte{0, 29, 33}: "Alcad SL", + [3]byte{0, 29, 34}: "Foss Analytical A/S", + [3]byte{0, 29, 35}: "SENSUS ", + [3]byte{0, 29, 36}: "Aclara Power-Line Systems Inc.", + [3]byte{0, 29, 37}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 29, 38}: "Rockridgesound Technology Co.", + [3]byte{0, 29, 39}: "NAC-INTERCOM", + [3]byte{0, 29, 40}: "Sony Mobile Communications AB", + [3]byte{0, 29, 41}: "Doro AB", + [3]byte{0, 29, 42}: "SHENZHEN BUL-TECH CO.,LTD.", + [3]byte{0, 29, 43}: "Wuhan Pont Technology CO. , LTD", + [3]byte{0, 29, 44}: "Wavetrend Technologies (Pty) Limited", + [3]byte{0, 29, 45}: "Pylone, Inc.", + [3]byte{0, 29, 46}: "Ruckus Wireless", + [3]byte{0, 29, 47}: "QuantumVision Corporation", + [3]byte{0, 29, 48}: "YX Wireless S.A.", + [3]byte{0, 29, 49}: "HIGHPRO INTERNATIONAL R&D CO,.LTD.", + [3]byte{0, 29, 50}: "Longkay Communication & Technology (Shanghai) Co. Ltd", + [3]byte{0, 29, 51}: "Maverick Systems Inc.", + [3]byte{0, 29, 52}: "SYRIS Technology Corp", + [3]byte{0, 29, 53}: "Viconics Electronics Inc.", + [3]byte{0, 29, 54}: "ELECTRONICS CORPORATION OF INDIA LIMITED", + [3]byte{0, 29, 55}: "Thales-Panda Transportation System", + [3]byte{0, 29, 56}: "Seagate Technology", + [3]byte{0, 29, 57}: "MOOHADIGITAL CO., LTD", + [3]byte{0, 29, 58}: "mh acoustics LLC", + [3]byte{0, 29, 59}: "Nokia Danmark A/S", + [3]byte{0, 29, 60}: "Muscle Corporation", + [3]byte{0, 29, 61}: "Avidyne Corporation", + [3]byte{0, 29, 62}: "SAKA TECHNO SCIENCE CO.,LTD", + [3]byte{0, 29, 63}: "Mitron Pty Ltd", + [3]byte{0, 29, 64}: "Intel – GE Care Innovations LLC", + [3]byte{0, 29, 65}: "Hardy Instruments", + [3]byte{0, 29, 66}: "Nortel Networks", + [3]byte{0, 29, 67}: "Shenzhen G-link Digital Technology Co., Ltd.", + [3]byte{0, 29, 68}: "Krohne", + [3]byte{0, 29, 69}: "Cisco Systems, Inc", + [3]byte{0, 29, 70}: "Cisco Systems, Inc", + [3]byte{0, 29, 71}: "Covote GmbH & Co KG", + [3]byte{0, 29, 72}: "Sensor-Technik Wiedemann GmbH", + [3]byte{0, 29, 73}: "Innovation Wireless Inc.", + [3]byte{0, 29, 74}: "Carestream Health, Inc.", + [3]byte{0, 29, 75}: "Grid Connect Inc.", + [3]byte{0, 29, 76}: "Alcatel-Lucent", + [3]byte{0, 29, 77}: "Adaptive Recognition Hungary, Inc", + [3]byte{0, 29, 78}: "TCM Mobile LLC", + [3]byte{0, 29, 79}: "Apple, Inc.", + [3]byte{0, 29, 80}: "SPINETIX SA", + [3]byte{0, 29, 81}: "Babcock & Wilcox Power Generation Group, Inc", + [3]byte{0, 29, 82}: "Defzone B.V.", + [3]byte{0, 29, 83}: "S&O Electronics (Malaysia) Sdn. Bhd.", + [3]byte{0, 29, 84}: "Sunnic Technology & Merchandise INC.", + [3]byte{0, 29, 85}: "ZANTAZ, Inc", + [3]byte{0, 29, 86}: "Kramer Electronics Ltd.", + [3]byte{0, 29, 87}: "CAETEC Messtechnik", + [3]byte{0, 29, 88}: "CQ Inc", + [3]byte{0, 29, 89}: "Mitra Energy & Infrastructure", + [3]byte{0, 29, 90}: "2Wire Inc", + [3]byte{0, 29, 91}: "Tecvan Informática Ltda", + [3]byte{0, 29, 92}: "Tom Communication Industrial Co.,Ltd.", + [3]byte{0, 29, 93}: "Control Dynamics Pty. Ltd.", + [3]byte{0, 29, 94}: "COMING MEDIA CORP.", + [3]byte{0, 29, 95}: "OverSpeed SARL", + [3]byte{0, 29, 96}: "ASUSTek COMPUTER INC.", + [3]byte{0, 29, 97}: "BIJ Corporation", + [3]byte{0, 29, 98}: "InPhase Technologies", + [3]byte{0, 29, 99}: "Miele & Cie. KG", + [3]byte{0, 29, 100}: "Adam Communications Systems Int Ltd", + [3]byte{0, 29, 101}: "Microwave Radio Communications", + [3]byte{0, 29, 102}: "Hyundai Telecom", + [3]byte{0, 29, 103}: "AMEC", + [3]byte{0, 29, 104}: "Thomson Telecom Belgium", + [3]byte{0, 29, 105}: "Knorr-Bremse IT-Services GmbH", + [3]byte{0, 29, 106}: "Alpha Networks Inc.", + [3]byte{0, 29, 107}: "ARRIS Group, Inc.", + [3]byte{0, 29, 108}: "ClariPhy Communications, Inc.", + [3]byte{0, 29, 109}: "Confidant International LLC", + [3]byte{0, 29, 110}: "Nokia Danmark A/S", + [3]byte{0, 29, 111}: "Chainzone Technology Co., Ltd", + [3]byte{0, 29, 112}: "Cisco Systems, Inc", + [3]byte{0, 29, 113}: "Cisco Systems, Inc", + [3]byte{0, 29, 114}: "Wistron Corporation", + [3]byte{0, 29, 115}: "BUFFALO.INC", + [3]byte{0, 29, 116}: "Tianjin China-Silicon Microelectronics Co., Ltd.", + [3]byte{0, 29, 117}: "Radioscape PLC", + [3]byte{0, 29, 118}: "Eyeheight Ltd.", + [3]byte{0, 29, 119}: "NSGate", + [3]byte{0, 29, 120}: "Invengo Information Technology Co.,Ltd", + [3]byte{0, 29, 121}: "SIGNAMAX LLC", + [3]byte{0, 29, 122}: "Wideband Semiconductor, Inc.", + [3]byte{0, 29, 123}: "Ice Energy, Inc.", + [3]byte{0, 29, 124}: "ABE Elettronica S.p.A.", + [3]byte{0, 29, 125}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{0, 29, 126}: "Cisco-Linksys, LLC", + [3]byte{0, 29, 127}: "Tekron International Ltd", + [3]byte{0, 29, 128}: "Beijing Huahuan Eletronics Co.,Ltd", + [3]byte{0, 29, 129}: "GUANGZHOU GATEWAY ELECTRONICS CO., LTD", + [3]byte{0, 29, 130}: "GN Netcom A/S", + [3]byte{0, 29, 131}: "Emitech Corporation", + [3]byte{0, 29, 132}: "Gateway, Inc.", + [3]byte{0, 29, 133}: "Call Direct Cellular Solutions", + [3]byte{0, 29, 134}: "Shinwa Industries(China) Ltd.", + [3]byte{0, 29, 135}: "VigTech Labs Sdn Bhd", + [3]byte{0, 29, 136}: "Clearwire", + [3]byte{0, 29, 137}: "VaultStor Corporation", + [3]byte{0, 29, 138}: "TechTrex Inc", + [3]byte{0, 29, 139}: "ADB Broadband Italia", + [3]byte{0, 29, 140}: "La Crosse Technology LTD", + [3]byte{0, 29, 141}: "Raytek GmbH", + [3]byte{0, 29, 142}: "Alereon, Inc.", + [3]byte{0, 29, 143}: "PureWave Networks", + [3]byte{0, 29, 144}: "EMCO Flow Systems", + [3]byte{0, 29, 145}: "Digitize, Inc", + [3]byte{0, 29, 146}: "MICRO-STAR INT'L CO.,LTD.", + [3]byte{0, 29, 147}: "Modacom", + [3]byte{0, 29, 148}: "Climax Technology Co., Ltd", + [3]byte{0, 29, 149}: "Flash, Inc.", + [3]byte{0, 29, 150}: "WatchGuard Video", + [3]byte{0, 29, 151}: "Alertus Technologies LLC", + [3]byte{0, 29, 152}: "Nokia Danmark A/S", + [3]byte{0, 29, 153}: "Cyan Optic, Inc.", + [3]byte{0, 29, 154}: "GODEX INTERNATIONAL CO., LTD", + [3]byte{0, 29, 155}: "Hokuyo Automatic Co., Ltd.", + [3]byte{0, 29, 156}: "Rockwell Automation", + [3]byte{0, 29, 157}: "ARTJOY INTERNATIONAL LIMITED", + [3]byte{0, 29, 158}: "AXION TECHNOLOGIES", + [3]byte{0, 29, 159}: "MATT R.P.Traczynscy Sp.J.", + [3]byte{0, 29, 160}: "Heng Yu Electronic Manufacturing Company Limited", + [3]byte{0, 29, 161}: "Cisco Systems, Inc", + [3]byte{0, 29, 162}: "Cisco Systems, Inc", + [3]byte{0, 29, 163}: "SabiOso", + [3]byte{0, 29, 164}: "Hangzhou System Technology CO., LTD", + [3]byte{0, 29, 165}: "WB Electronics", + [3]byte{0, 29, 166}: "Media Numerics Limited", + [3]byte{0, 29, 167}: "Seamless Internet", + [3]byte{0, 29, 168}: "Takahata Electronics Co.,Ltd", + [3]byte{0, 29, 169}: "Castles Technology, Co., LTD", + [3]byte{0, 29, 170}: "DrayTek Corp.", + [3]byte{0, 29, 171}: "SwissQual License AG", + [3]byte{0, 29, 172}: "Gigamon Systems LLC", + [3]byte{0, 29, 173}: "Sinotech Engineering Consultants, Inc. Geotechnical Enginee", + [3]byte{0, 29, 174}: "CHANG TSENG TECHNOLOGY CO., LTD", + [3]byte{0, 29, 175}: "Nortel Networks", + [3]byte{0, 29, 176}: "FuJian HengTong Information Technology Co.,Ltd", + [3]byte{0, 29, 177}: "Crescendo Networks", + [3]byte{0, 29, 178}: "HOKKAIDO ELECTRIC ENGINEERING CO.,LTD.", + [3]byte{0, 29, 179}: "HPN Supply Chain", + [3]byte{0, 29, 180}: "KUMHO ENG CO.,LTD", + [3]byte{0, 29, 181}: "Juniper Networks", + [3]byte{0, 29, 182}: "BestComm Networks, Inc.", + [3]byte{0, 29, 183}: "Tendril Networks, Inc.", + [3]byte{0, 29, 184}: "Intoto Inc.", + [3]byte{0, 29, 185}: "Wellspring Wireless", + [3]byte{0, 29, 186}: "Sony Corporation", + [3]byte{0, 29, 187}: "Dynamic System Electronics Corp.", + [3]byte{0, 29, 188}: "Nintendo Co., Ltd.", + [3]byte{0, 29, 189}: "Versamed Inc.", + [3]byte{0, 29, 190}: "ARRIS Group, Inc.", + [3]byte{0, 29, 191}: "Radiient Technologies, Inc.", + [3]byte{0, 29, 192}: "Enphase Energy", + [3]byte{0, 29, 193}: "Audinate Pty L", + [3]byte{0, 29, 194}: "XORTEC OY", + [3]byte{0, 29, 195}: "RIKOR TV, Ltd", + [3]byte{0, 29, 196}: "AIOI Systems Co., Ltd.", + [3]byte{0, 29, 197}: "Beijing Jiaxun Feihong Electricial Co., Ltd.", + [3]byte{0, 29, 198}: "SNR Inc.", + [3]byte{0, 29, 199}: "L-3 Communications Geneva Aerospace", + [3]byte{0, 29, 200}: "Navionics Research Inc., dba SCADAmetrics", + [3]byte{0, 29, 201}: "GainSpan Corp.", + [3]byte{0, 29, 202}: "PAV Electronics Limited", + [3]byte{0, 29, 203}: "Exéns Development Oy", + [3]byte{0, 29, 204}: "Hetra Secure Solutions", + [3]byte{0, 29, 205}: "ARRIS Group, Inc.", + [3]byte{0, 29, 206}: "ARRIS Group, Inc.", + [3]byte{0, 29, 207}: "ARRIS Group, Inc.", + [3]byte{0, 29, 208}: "ARRIS Group, Inc.", + [3]byte{0, 29, 209}: "ARRIS Group, Inc.", + [3]byte{0, 29, 210}: "ARRIS Group, Inc.", + [3]byte{0, 29, 211}: "ARRIS Group, Inc.", + [3]byte{0, 29, 212}: "ARRIS Group, Inc.", + [3]byte{0, 29, 213}: "ARRIS Group, Inc.", + [3]byte{0, 29, 214}: "ARRIS Group, Inc.", + [3]byte{0, 29, 215}: "Algolith", + [3]byte{0, 29, 216}: "Microsoft Corporation", + [3]byte{0, 29, 217}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 29, 218}: "Mikroelektronika spol. s r. o.", + [3]byte{0, 29, 219}: "C-BEL Corporation", + [3]byte{0, 29, 220}: "HangZhou DeChangLong Tech&Info Co.,Ltd", + [3]byte{0, 29, 221}: "DAT H.K. LIMITED", + [3]byte{0, 29, 222}: "Zhejiang Broadcast&Television Technology Co.,Ltd.", + [3]byte{0, 29, 223}: "Sunitec Enterprise Co., Ltd.", + [3]byte{0, 29, 224}: "Intel Corporate", + [3]byte{0, 29, 225}: "Intel Corporate", + [3]byte{0, 29, 226}: "Radionor Communications", + [3]byte{0, 29, 227}: "Intuicom", + [3]byte{0, 29, 228}: "Visioneered Image Systems", + [3]byte{0, 29, 229}: "Cisco Systems, Inc", + [3]byte{0, 29, 230}: "Cisco Systems, Inc", + [3]byte{0, 29, 231}: "Marine Sonic Technology, Ltd.", + [3]byte{0, 29, 232}: "Nikko Denki Tsushin Corporation(NDTC)", + [3]byte{0, 29, 233}: "Nokia Danmark A/S", + [3]byte{0, 29, 234}: "Commtest Instruments Ltd", + [3]byte{0, 29, 235}: "DINEC International", + [3]byte{0, 29, 236}: "Marusys", + [3]byte{0, 29, 237}: "Grid Net, Inc.", + [3]byte{0, 29, 238}: "NEXTVISION SISTEMAS DIGITAIS DE TELEVISÃO LTDA.", + [3]byte{0, 29, 239}: "TRIMM, INC.", + [3]byte{0, 29, 240}: "Vidient Systems, Inc.", + [3]byte{0, 29, 241}: "Intego Systems, Inc.", + [3]byte{0, 29, 242}: "Netflix, Inc.", + [3]byte{0, 29, 243}: "SBS Science & Technology Co., Ltd", + [3]byte{0, 29, 244}: "Magellan Technology Pty Limited", + [3]byte{0, 29, 245}: "Sunshine Co,LTD", + [3]byte{0, 29, 246}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 29, 247}: "R. STAHL Schaltgeräte GmbH", + [3]byte{0, 29, 248}: "Webpro Vision Technology Corporation", + [3]byte{0, 29, 249}: "Cybiotronics (Far East) Limited", + [3]byte{0, 29, 250}: "Fujian LANDI Commercial Equipment Co.,Ltd", + [3]byte{0, 29, 251}: "NETCLEUS Systems Corporation", + [3]byte{0, 29, 252}: "KSIC", + [3]byte{0, 29, 253}: "Nokia Danmark A/S", + [3]byte{0, 29, 254}: "Palm, Inc", + [3]byte{0, 29, 255}: "Network Critical Solutions Ltd", + [3]byte{0, 30, 0}: "Shantou Institute of Ultrasonic Instruments", + [3]byte{0, 30, 1}: "Renesas Technology Sales Co., Ltd.", + [3]byte{0, 30, 2}: "Sougou Keikaku Kougyou Co.,Ltd.", + [3]byte{0, 30, 3}: "LiComm Co., Ltd.", + [3]byte{0, 30, 4}: "Hanson Research Corporation", + [3]byte{0, 30, 5}: "Xseed Technologies & Computing", + [3]byte{0, 30, 6}: "WIBRAIN", + [3]byte{0, 30, 7}: "Winy Technology Co., Ltd.", + [3]byte{0, 30, 8}: "Centec Networks Inc", + [3]byte{0, 30, 9}: "ZEFATEK Co.,LTD", + [3]byte{0, 30, 10}: "Syba Tech Limited", + [3]byte{0, 30, 11}: "Hewlett Packard", + [3]byte{0, 30, 12}: "Sherwood Information Partners, Inc.", + [3]byte{0, 30, 13}: "Micran Ltd.", + [3]byte{0, 30, 14}: "MAXI VIEW HOLDINGS LIMITED", + [3]byte{0, 30, 15}: "Briot International", + [3]byte{0, 30, 16}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 30, 17}: "ELELUX INTERNATIONAL LTD", + [3]byte{0, 30, 18}: "Ecolab", + [3]byte{0, 30, 19}: "Cisco Systems, Inc", + [3]byte{0, 30, 20}: "Cisco Systems, Inc", + [3]byte{0, 30, 21}: "Beech Hill Electronics", + [3]byte{0, 30, 22}: "Keytronix", + [3]byte{0, 30, 23}: "STN BV", + [3]byte{0, 30, 24}: "Radio Activity srl", + [3]byte{0, 30, 25}: "GTRI", + [3]byte{0, 30, 26}: "Best Source Taiwan Inc.", + [3]byte{0, 30, 27}: "Digital Stream Technology, Inc.", + [3]byte{0, 30, 28}: "SWS Australia Pty Limited", + [3]byte{0, 30, 29}: "East Coast Datacom, Inc.", + [3]byte{0, 30, 30}: "Honeywell Life Safety", + [3]byte{0, 30, 31}: "Nortel Networks", + [3]byte{0, 30, 32}: "Intertain Inc.", + [3]byte{0, 30, 33}: "Qisda Corporation", + [3]byte{0, 30, 34}: "ARVOO Imaging Products BV", + [3]byte{0, 30, 35}: "Electronic Educational Devices, Inc", + [3]byte{0, 30, 36}: "Zhejiang Bell Technology Co.,ltd", + [3]byte{0, 30, 37}: "INTEK DIGITAL", + [3]byte{0, 30, 38}: "Digifriends Co. Ltd", + [3]byte{0, 30, 39}: "SBN TECH Co.,Ltd.", + [3]byte{0, 30, 40}: "Lumexis Corporation", + [3]byte{0, 30, 41}: "Hypertherm Inc", + [3]byte{0, 30, 42}: "NETGEAR", + [3]byte{0, 30, 43}: "Radio Systems Design, Inc.", + [3]byte{0, 30, 44}: "CyVerse Corporation", + [3]byte{0, 30, 45}: "STIM", + [3]byte{0, 30, 46}: "SIRTI S.p.A.", + [3]byte{0, 30, 47}: "DiMoto Pty Ltd", + [3]byte{0, 30, 48}: "Shireen Inc", + [3]byte{0, 30, 49}: "INFOMARK CO.,LTD.", + [3]byte{0, 30, 50}: "Zensys", + [3]byte{0, 30, 51}: "INVENTEC Corporation", + [3]byte{0, 30, 52}: "CryptoMetrics", + [3]byte{0, 30, 53}: "Nintendo Co., Ltd.", + [3]byte{0, 30, 54}: "IPTE", + [3]byte{0, 30, 55}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{0, 30, 56}: "Bluecard Software Technology Co., Ltd.", + [3]byte{0, 30, 57}: "Comsys Communication Ltd.", + [3]byte{0, 30, 58}: "Nokia Danmark A/S", + [3]byte{0, 30, 59}: "Nokia Danmark A/S", + [3]byte{0, 30, 60}: "Lyngbox Media AB", + [3]byte{0, 30, 61}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 30, 62}: "KMW Inc.", + [3]byte{0, 30, 63}: "TrellisWare Technologies, Inc.", + [3]byte{0, 30, 64}: "Shanghai DareGlobal Technologies Co.,Ltd", + [3]byte{0, 30, 65}: "Microwave Communication & Component, Inc.", + [3]byte{0, 30, 66}: "Teltonika", + [3]byte{0, 30, 67}: "AISIN AW CO.,LTD.", + [3]byte{0, 30, 68}: "SANTEC", + [3]byte{0, 30, 69}: "Sony Mobile Communications AB", + [3]byte{0, 30, 70}: "ARRIS Group, Inc.", + [3]byte{0, 30, 71}: "PT. Hariff Daya Tunggal Engineering", + [3]byte{0, 30, 72}: "Wi-Links", + [3]byte{0, 30, 73}: "Cisco Systems, Inc", + [3]byte{0, 30, 74}: "Cisco Systems, Inc", + [3]byte{0, 30, 75}: "City Theatrical", + [3]byte{0, 30, 76}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 30, 77}: "Welkin Sciences, LLC", + [3]byte{0, 30, 78}: "DAKO EDV-Ingenieur- und Systemhaus GmbH", + [3]byte{0, 30, 79}: "Dell Inc.", + [3]byte{0, 30, 80}: "BATTISTONI RESEARCH", + [3]byte{0, 30, 81}: "Converter Industry Srl", + [3]byte{0, 30, 82}: "Apple, Inc.", + [3]byte{0, 30, 83}: "Further Tech Co., LTD", + [3]byte{0, 30, 84}: "TOYO ELECTRIC Corporation", + [3]byte{0, 30, 85}: "COWON SYSTEMS,Inc.", + [3]byte{0, 30, 86}: "Bally Wulff Entertainment GmbH", + [3]byte{0, 30, 87}: "ALCOMA, spol. s r.o.", + [3]byte{0, 30, 88}: "D-Link Corporation", + [3]byte{0, 30, 89}: "Silicon Turnkey Express, LLC", + [3]byte{0, 30, 90}: "ARRIS Group, Inc.", + [3]byte{0, 30, 91}: "Unitron Company, Inc.", + [3]byte{0, 30, 92}: "RB GeneralEkonomik", + [3]byte{0, 30, 93}: "Holosys d.o.o.", + [3]byte{0, 30, 94}: "COmputime Ltd.", + [3]byte{0, 30, 95}: "KwikByte, LLC", + [3]byte{0, 30, 96}: "Digital Lighting Systems, Inc", + [3]byte{0, 30, 97}: "ITEC GmbH", + [3]byte{0, 30, 98}: "Siemon", + [3]byte{0, 30, 99}: "Vibro-Meter SA", + [3]byte{0, 30, 100}: "Intel Corporate", + [3]byte{0, 30, 101}: "Intel Corporate", + [3]byte{0, 30, 102}: "RESOL Elektronische Regelungen GmbH", + [3]byte{0, 30, 103}: "Intel Corporate", + [3]byte{0, 30, 104}: "QUANTA COMPUTER INC.", + [3]byte{0, 30, 105}: "Thomson Inc.", + [3]byte{0, 30, 106}: "Beijing Bluexon Technology Co.,Ltd", + [3]byte{0, 30, 107}: "Cisco SPVTG", + [3]byte{0, 30, 108}: "Opaque Systems", + [3]byte{0, 30, 109}: "IT R&D Center", + [3]byte{0, 30, 110}: "Shenzhen First Mile Communications Ltd", + [3]byte{0, 30, 111}: "Magna-Power Electronics, Inc.", + [3]byte{0, 30, 112}: "Cobham Defence Communications Ltd", + [3]byte{0, 30, 113}: "MIrcom Group of Companies", + [3]byte{0, 30, 114}: "PCS", + [3]byte{0, 30, 115}: "zte corporation", + [3]byte{0, 30, 116}: "Sagemcom Broadband SAS", + [3]byte{0, 30, 117}: "LG Electronics (Mobile Communications)", + [3]byte{0, 30, 118}: "Thermo Fisher Scientific", + [3]byte{0, 30, 119}: "Air2App", + [3]byte{0, 30, 120}: "Owitek Technology Ltd.,", + [3]byte{0, 30, 121}: "Cisco Systems, Inc", + [3]byte{0, 30, 122}: "Cisco Systems, Inc", + [3]byte{0, 30, 123}: "R.I.CO. S.r.l.", + [3]byte{0, 30, 124}: "Taiwick Limited", + [3]byte{0, 30, 125}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 30, 126}: "Nortel Networks", + [3]byte{0, 30, 127}: "CBM of America", + [3]byte{0, 30, 128}: "Last Mile Ltd.", + [3]byte{0, 30, 129}: "CNB Technology Inc.", + [3]byte{0, 30, 130}: "SanDisk Corporation", + [3]byte{0, 30, 131}: "LAN/MAN Standards Association (LMSC)", + [3]byte{0, 30, 132}: "Pika Technologies Inc.", + [3]byte{0, 30, 133}: "Lagotek Corporation", + [3]byte{0, 30, 134}: "MEL Co.,Ltd.", + [3]byte{0, 30, 135}: "Realease Limited", + [3]byte{0, 30, 136}: "ANDOR SYSTEM SUPPORT CO., LTD.", + [3]byte{0, 30, 137}: "CRFS Limited", + [3]byte{0, 30, 138}: "eCopy, Inc", + [3]byte{0, 30, 139}: "Infra Access Korea Co., Ltd.", + [3]byte{0, 30, 140}: "ASUSTek COMPUTER INC.", + [3]byte{0, 30, 141}: "ARRIS Group, Inc.", + [3]byte{0, 30, 142}: "Hunkeler AG", + [3]byte{0, 30, 143}: "CANON INC.", + [3]byte{0, 30, 144}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 30, 145}: "KIMIN Electronic Co., Ltd.", + [3]byte{0, 30, 146}: "JEULIN S.A.", + [3]byte{0, 30, 147}: "CiriTech Systems Inc", + [3]byte{0, 30, 148}: "SUPERCOM TECHNOLOGY CORPORATION", + [3]byte{0, 30, 149}: "SIGMALINK", + [3]byte{0, 30, 150}: "Sepura Plc", + [3]byte{0, 30, 151}: "Medium Link System Technology CO., LTD,", + [3]byte{0, 30, 152}: "GreenLine Communications", + [3]byte{0, 30, 153}: "Vantanol Industrial Corporation", + [3]byte{0, 30, 154}: "HAMILTON Bonaduz AG", + [3]byte{0, 30, 155}: "San-Eisha, Ltd.", + [3]byte{0, 30, 156}: "Fidustron INC", + [3]byte{0, 30, 157}: "Recall Technologies, Inc.", + [3]byte{0, 30, 158}: "ddm hopt + schuler Gmbh + Co. KG", + [3]byte{0, 30, 159}: "Visioneering Systems, Inc.", + [3]byte{0, 30, 160}: "XLN-t", + [3]byte{0, 30, 161}: "Brunata a/s", + [3]byte{0, 30, 162}: "Symx Systems, Inc.", + [3]byte{0, 30, 163}: "Nokia Danmark A/S", + [3]byte{0, 30, 164}: "Nokia Danmark A/S", + [3]byte{0, 30, 165}: "ROBOTOUS, Inc.", + [3]byte{0, 30, 166}: "Best IT World (India) Pvt. Ltd.", + [3]byte{0, 30, 167}: "Actiontec Electronics, Inc", + [3]byte{0, 30, 168}: "Datang Mobile Communications Equipment CO.,LTD", + [3]byte{0, 30, 169}: "Nintendo Co., Ltd.", + [3]byte{0, 30, 170}: "E-Senza Technologies GmbH", + [3]byte{0, 30, 171}: "TeleWell Oy", + [3]byte{0, 30, 172}: "Armadeus Systems", + [3]byte{0, 30, 173}: "Wingtech Group Limited", + [3]byte{0, 30, 174}: "Continental Automotive Systems Inc.", + [3]byte{0, 30, 175}: "Ophir Optronics Ltd", + [3]byte{0, 30, 176}: "ImesD Electronica S.L.", + [3]byte{0, 30, 177}: "Cryptsoft Pty Ltd", + [3]byte{0, 30, 178}: "LG innotek", + [3]byte{0, 30, 179}: "Primex Wireless", + [3]byte{0, 30, 180}: "UNIFAT TECHNOLOGY LTD.", + [3]byte{0, 30, 181}: "Ever Sparkle Technologies Ltd", + [3]byte{0, 30, 182}: "TAG Heuer SA", + [3]byte{0, 30, 183}: "TBTech, Co., Ltd.", + [3]byte{0, 30, 184}: "Fortis, Inc.", + [3]byte{0, 30, 185}: "Sing Fai Technology Limited", + [3]byte{0, 30, 186}: "High Density Devices AS", + [3]byte{0, 30, 187}: "BLUELIGHT TECHNOLOGY INC.", + [3]byte{0, 30, 188}: "WINTECH AUTOMATION CO.,LTD.", + [3]byte{0, 30, 189}: "Cisco Systems, Inc", + [3]byte{0, 30, 190}: "Cisco Systems, Inc", + [3]byte{0, 30, 191}: "Haas Automation Inc.", + [3]byte{0, 30, 192}: "Microchip Technology Inc.", + [3]byte{0, 30, 193}: "3COM EUROPE LTD", + [3]byte{0, 30, 194}: "Apple, Inc.", + [3]byte{0, 30, 195}: "Kozio, Inc.", + [3]byte{0, 30, 196}: "Celio Corp", + [3]byte{0, 30, 197}: "Middle Atlantic Products Inc", + [3]byte{0, 30, 198}: "Obvius Holdings LLC", + [3]byte{0, 30, 199}: "2Wire Inc", + [3]byte{0, 30, 200}: "Rapid Mobile (Pty) Ltd", + [3]byte{0, 30, 201}: "Dell Inc.", + [3]byte{0, 30, 202}: "Nortel Networks", + [3]byte{0, 30, 203}: "RPC Energoautomatika Ltd", + [3]byte{0, 30, 204}: "CDVI", + [3]byte{0, 30, 205}: "KYLAND Technology Co. LTD", + [3]byte{0, 30, 206}: "BISA Technologies (Hong Kong) Limited", + [3]byte{0, 30, 207}: "PHILIPS ELECTRONICS UK LTD", + [3]byte{0, 30, 208}: "Ingespace", + [3]byte{0, 30, 209}: "Keyprocessor B.V.", + [3]byte{0, 30, 210}: "Ray Shine Video Technology Inc", + [3]byte{0, 30, 211}: "Dot Technology Int'l Co., Ltd.", + [3]byte{0, 30, 212}: "Doble Engineering", + [3]byte{0, 30, 213}: "Tekon-Automatics", + [3]byte{0, 30, 214}: "Alentec & Orion AB", + [3]byte{0, 30, 215}: "H-Stream Wireless, Inc.", + [3]byte{0, 30, 216}: "Digital United Inc.", + [3]byte{0, 30, 217}: "Mitsubishi Precision Co.,LTd.", + [3]byte{0, 30, 218}: "Wesemann Elektrotechniek B.V.", + [3]byte{0, 30, 219}: "Giken Trastem Co., Ltd.", + [3]byte{0, 30, 220}: "Sony Mobile Communications AB", + [3]byte{0, 30, 221}: "WASKO S.A.", + [3]byte{0, 30, 222}: "BYD COMPANY LIMITED", + [3]byte{0, 30, 223}: "Master Industrialization Center Kista", + [3]byte{0, 30, 224}: "Urmet Domus SpA", + [3]byte{0, 30, 225}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 30, 226}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 30, 227}: "T&W Electronics (ShenZhen) Co.,Ltd", + [3]byte{0, 30, 228}: "ACS Solutions France", + [3]byte{0, 30, 229}: "Cisco-Linksys, LLC", + [3]byte{0, 30, 230}: "Shenzhen Advanced Video Info-Tech Co., Ltd.", + [3]byte{0, 30, 231}: "Epic Systems Inc", + [3]byte{0, 30, 232}: "Mytek", + [3]byte{0, 30, 233}: "Stoneridge Electronics AB", + [3]byte{0, 30, 234}: "Sensor Switch, Inc.", + [3]byte{0, 30, 235}: "Talk-A-Phone Co.", + [3]byte{0, 30, 236}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{0, 30, 237}: "Adventiq Ltd.", + [3]byte{0, 30, 238}: "ETL Systems Ltd", + [3]byte{0, 30, 239}: "Cantronic International Limited", + [3]byte{0, 30, 240}: "Gigafin Networks", + [3]byte{0, 30, 241}: "Servimat", + [3]byte{0, 30, 242}: "Micro Motion Inc", + [3]byte{0, 30, 243}: "From2", + [3]byte{0, 30, 244}: "L-3 Communications Display Systems", + [3]byte{0, 30, 245}: "Hitek Automated Inc.", + [3]byte{0, 30, 246}: "Cisco Systems, Inc", + [3]byte{0, 30, 247}: "Cisco Systems, Inc", + [3]byte{0, 30, 248}: "Emfinity Inc.", + [3]byte{0, 30, 249}: "Pascom Kommunikations systeme GmbH.", + [3]byte{0, 30, 250}: "PROTEI Ltd.", + [3]byte{0, 30, 251}: "Trio Motion Technology Ltd", + [3]byte{0, 30, 252}: "JSC MASSA-K", + [3]byte{0, 30, 253}: "Microbit 2.0 AB", + [3]byte{0, 30, 254}: "LEVEL s.r.o.", + [3]byte{0, 30, 255}: "Mueller-Elektronik GmbH & Co. KG", + [3]byte{0, 31, 0}: "Nokia Danmark A/S", + [3]byte{0, 31, 1}: "Nokia Danmark A/S", + [3]byte{0, 31, 2}: "Pixelmetrix Corporation Pte Ltd", + [3]byte{0, 31, 3}: "NUM AG", + [3]byte{0, 31, 4}: "Granch Ltd.", + [3]byte{0, 31, 5}: "iTAS Technology Corp.", + [3]byte{0, 31, 6}: "Integrated Dispatch Solutions", + [3]byte{0, 31, 7}: "AZTEQ Mobile", + [3]byte{0, 31, 8}: "RISCO LTD", + [3]byte{0, 31, 9}: "Jastec", + [3]byte{0, 31, 10}: "Nortel Networks", + [3]byte{0, 31, 11}: "Federal State Unitary Enterprise Industrial UnionElectropribor", + [3]byte{0, 31, 12}: "Intelligent Digital Services GmbH", + [3]byte{0, 31, 13}: "L3 Communications - Telemetry West", + [3]byte{0, 31, 14}: "Japan Kyastem Co., Ltd", + [3]byte{0, 31, 15}: "Select Engineered Systems", + [3]byte{0, 31, 16}: "TOLEDO DO BRASIL INDUSTRIA DE BALANCAS LTDA", + [3]byte{0, 31, 17}: "OPENMOKO, INC.", + [3]byte{0, 31, 18}: "Juniper Networks", + [3]byte{0, 31, 19}: "S.& A.S. Ltd.", + [3]byte{0, 31, 20}: "NexG", + [3]byte{0, 31, 21}: "Bioscrypt Inc", + [3]byte{0, 31, 22}: "Wistron Corporation", + [3]byte{0, 31, 23}: "IDX Company, Ltd.", + [3]byte{0, 31, 24}: "Hakusan.Mfg.Co,.Ltd", + [3]byte{0, 31, 25}: "BEN-RI ELECTRONICA S.A.", + [3]byte{0, 31, 26}: "Prominvest", + [3]byte{0, 31, 27}: "RoyalTek Company Ltd.", + [3]byte{0, 31, 28}: "KOBISHI ELECTRIC Co.,Ltd.", + [3]byte{0, 31, 29}: "Atlas Material Testing Technology LLC", + [3]byte{0, 31, 30}: "Astec Technology Co., Ltd", + [3]byte{0, 31, 31}: "Edimax Technology Co. Ltd.", + [3]byte{0, 31, 32}: "Logitech Europe SA", + [3]byte{0, 31, 33}: "Inner Mongolia Yin An Science & Technology Development Co.,L", + [3]byte{0, 31, 34}: "Source Photonics, Inc.", + [3]byte{0, 31, 35}: "Interacoustics", + [3]byte{0, 31, 36}: "DIGITVIEW TECHNOLOGY CO., LTD.", + [3]byte{0, 31, 37}: "MBS GmbH", + [3]byte{0, 31, 38}: "Cisco Systems, Inc", + [3]byte{0, 31, 39}: "Cisco Systems, Inc", + [3]byte{0, 31, 40}: "HPN Supply Chain", + [3]byte{0, 31, 41}: "Hewlett Packard", + [3]byte{0, 31, 42}: "ACCM", + [3]byte{0, 31, 43}: "Orange Logic", + [3]byte{0, 31, 44}: "Starbridge Networks", + [3]byte{0, 31, 45}: "Electro-Optical Imaging, Inc.", + [3]byte{0, 31, 46}: "Triangle Research Int'l Pte Ltd", + [3]byte{0, 31, 47}: "Berker GmbH & Co. KG", + [3]byte{0, 31, 48}: "Travelping", + [3]byte{0, 31, 49}: "Radiocomp", + [3]byte{0, 31, 50}: "Nintendo Co., Ltd.", + [3]byte{0, 31, 51}: "NETGEAR", + [3]byte{0, 31, 52}: "Lung Hwa Electronics Co., Ltd.", + [3]byte{0, 31, 53}: "AIR802 LLC", + [3]byte{0, 31, 54}: "Bellwin Information Co. Ltd.,", + [3]byte{0, 31, 55}: "Genesis I&C", + [3]byte{0, 31, 56}: "POSITRON", + [3]byte{0, 31, 57}: "Construcciones y Auxiliar de Ferrocarriles, S.A.", + [3]byte{0, 31, 58}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 31, 59}: "Intel Corporate", + [3]byte{0, 31, 60}: "Intel Corporate", + [3]byte{0, 31, 61}: "Qbit GmbH", + [3]byte{0, 31, 62}: "RP-Technik e.K.", + [3]byte{0, 31, 63}: "AVM GmbH", + [3]byte{0, 31, 64}: "Speakercraft Inc.", + [3]byte{0, 31, 65}: "Ruckus Wireless", + [3]byte{0, 31, 66}: "Etherstack plc", + [3]byte{0, 31, 67}: "ENTES ELEKTRONIK", + [3]byte{0, 31, 68}: "GE Transportation Systems", + [3]byte{0, 31, 69}: "Enterasys", + [3]byte{0, 31, 70}: "Nortel Networks", + [3]byte{0, 31, 71}: "MCS Logic Inc.", + [3]byte{0, 31, 72}: "Mojix Inc.", + [3]byte{0, 31, 73}: "Manhattan TV Ltd", + [3]byte{0, 31, 74}: "Albentia Systems S.A.", + [3]byte{0, 31, 75}: "Lineage Power", + [3]byte{0, 31, 76}: "Roseman Engineering Ltd", + [3]byte{0, 31, 77}: "Segnetics LLC", + [3]byte{0, 31, 78}: "ConMed Linvatec", + [3]byte{0, 31, 79}: "Thinkware Co. Ltd.", + [3]byte{0, 31, 80}: "Swissdis AG", + [3]byte{0, 31, 81}: "HD Communications Corp", + [3]byte{0, 31, 82}: "UVT Unternehmensberatung fur Verkehr und Technik GmbH", + [3]byte{0, 31, 83}: "GEMAC Gesellschaft für Mikroelektronikanwendung Chemnitz mbH", + [3]byte{0, 31, 84}: "Lorex Technology Inc.", + [3]byte{0, 31, 85}: "Honeywell Security (China) Co., Ltd.", + [3]byte{0, 31, 86}: "DIGITAL FORECAST", + [3]byte{0, 31, 87}: "Phonik Innovation Co.,LTD", + [3]byte{0, 31, 88}: "EMH Energiemesstechnik GmbH", + [3]byte{0, 31, 89}: "Kronback Tracers", + [3]byte{0, 31, 90}: "Beckwith Electric Co.", + [3]byte{0, 31, 91}: "Apple, Inc.", + [3]byte{0, 31, 92}: "Nokia Danmark A/S", + [3]byte{0, 31, 93}: "Nokia Danmark A/S", + [3]byte{0, 31, 94}: "Dyna Technology Co.,Ltd.", + [3]byte{0, 31, 95}: "Blatand GmbH", + [3]byte{0, 31, 96}: "COMPASS SYSTEMS CORP.", + [3]byte{0, 31, 97}: "Talent Communication Networks Inc.", + [3]byte{0, 31, 98}: "JSC Stilsoft", + [3]byte{0, 31, 99}: "JSC Goodwin-Europa", + [3]byte{0, 31, 100}: "Beijing Autelan Technology Inc.", + [3]byte{0, 31, 101}: "KOREA ELECTRIC TERMINAL CO., LTD.", + [3]byte{0, 31, 102}: "PLANAR LLC", + [3]byte{0, 31, 103}: "Hitachi,Ltd.", + [3]byte{0, 31, 104}: "Martinsson Elektronik AB", + [3]byte{0, 31, 105}: "Pingood Technology Co., Ltd.", + [3]byte{0, 31, 106}: "PacketFlux Technologies, Inc.", + [3]byte{0, 31, 107}: "LG Electronics (Mobile Communications)", + [3]byte{0, 31, 108}: "Cisco Systems, Inc", + [3]byte{0, 31, 109}: "Cisco Systems, Inc", + [3]byte{0, 31, 110}: "Vtech Engineering Corporation", + [3]byte{0, 31, 111}: "Fujian Sunnada Communication Co.,Ltd.", + [3]byte{0, 31, 112}: "Botik Technologies LTD", + [3]byte{0, 31, 113}: "xG Technology, Inc.", + [3]byte{0, 31, 114}: "QingDao Hiphone Technology Co,.Ltd", + [3]byte{0, 31, 115}: "Teraview Technology Co., Ltd.", + [3]byte{0, 31, 116}: "Eigen Development", + [3]byte{0, 31, 117}: "GiBahn Media", + [3]byte{0, 31, 118}: "AirLogic Systems Inc.", + [3]byte{0, 31, 119}: "HEOL DESIGN", + [3]byte{0, 31, 120}: "Blue Fox Porini Textile", + [3]byte{0, 31, 121}: "Lodam Electronics A/S", + [3]byte{0, 31, 122}: "WiWide Inc.", + [3]byte{0, 31, 123}: "TechNexion Ltd.", + [3]byte{0, 31, 124}: "Witelcom AS", + [3]byte{0, 31, 125}: "embedded wireless GmbH", + [3]byte{0, 31, 126}: "ARRIS Group, Inc.", + [3]byte{0, 31, 127}: "Phabrix Limited", + [3]byte{0, 31, 128}: "Lucas Holding bv", + [3]byte{0, 31, 129}: "Accel Semiconductor Corp", + [3]byte{0, 31, 130}: "Cal-Comp Electronics & Communications Company Ltd.", + [3]byte{0, 31, 131}: "Teleplan Technology Services Sdn Bhd", + [3]byte{0, 31, 132}: "Gigle Semiconductor", + [3]byte{0, 31, 133}: "Apriva ISS, LLC", + [3]byte{0, 31, 134}: "digEcor", + [3]byte{0, 31, 135}: "Skydigital Inc.", + [3]byte{0, 31, 136}: "FMS Force Measuring Systems AG", + [3]byte{0, 31, 137}: "Signalion GmbH", + [3]byte{0, 31, 138}: "Ellion Digital Inc.", + [3]byte{0, 31, 139}: "Cache IQ", + [3]byte{0, 31, 140}: "CCS Inc.", + [3]byte{0, 31, 141}: "Ingenieurbuero Stark GmbH und Ko. KG", + [3]byte{0, 31, 142}: "Metris USA Inc.", + [3]byte{0, 31, 143}: "Shanghai Bellmann Digital Source Co.,Ltd.", + [3]byte{0, 31, 144}: "Actiontec Electronics, Inc", + [3]byte{0, 31, 145}: "DBS Lodging Technologies, LLC", + [3]byte{0, 31, 146}: "VideoIQ, Inc.", + [3]byte{0, 31, 147}: "Xiotech Corporation", + [3]byte{0, 31, 148}: "Lascar Electronics Ltd", + [3]byte{0, 31, 149}: "Sagemcom Broadband SAS", + [3]byte{0, 31, 150}: "APROTECH CO.LTD", + [3]byte{0, 31, 151}: "BERTANA srl", + [3]byte{0, 31, 152}: "DAIICHI-DENTSU LTD.", + [3]byte{0, 31, 153}: "SERONICS co.ltd", + [3]byte{0, 31, 154}: "Nortel Networks", + [3]byte{0, 31, 155}: "POSBRO", + [3]byte{0, 31, 156}: "LEDCO", + [3]byte{0, 31, 157}: "Cisco Systems, Inc", + [3]byte{0, 31, 158}: "Cisco Systems, Inc", + [3]byte{0, 31, 159}: "Thomson Telecom Belgium", + [3]byte{0, 31, 160}: "A10 Networks", + [3]byte{0, 31, 161}: "Gtran Inc", + [3]byte{0, 31, 162}: "Datron World Communications, Inc.", + [3]byte{0, 31, 163}: "T&W Electronics(Shenzhen)Co.,Ltd.", + [3]byte{0, 31, 164}: "SHENZHEN GONGJIN ELECTRONICS CO.,LT", + [3]byte{0, 31, 165}: "Blue-White Industries", + [3]byte{0, 31, 166}: "Stilo srl", + [3]byte{0, 31, 167}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 31, 168}: "Smart Energy Instruments Inc.", + [3]byte{0, 31, 169}: "Atlanta DTH, Inc.", + [3]byte{0, 31, 170}: "Taseon, Inc.", + [3]byte{0, 31, 171}: "I.S HIGH TECH.INC", + [3]byte{0, 31, 172}: "Goodmill Systems Ltd", + [3]byte{0, 31, 173}: "Brown Innovations, Inc", + [3]byte{0, 31, 174}: "Blick South Africa (Pty) Ltd", + [3]byte{0, 31, 175}: "NextIO, Inc.", + [3]byte{0, 31, 176}: "TimeIPS, Inc.", + [3]byte{0, 31, 177}: "Cybertech Inc.", + [3]byte{0, 31, 178}: "Sontheim Industrie Elektronik GmbH", + [3]byte{0, 31, 179}: "2Wire Inc", + [3]byte{0, 31, 180}: "SmartShare Systems", + [3]byte{0, 31, 181}: "I/O Interconnect Inc.", + [3]byte{0, 31, 182}: "Chi Lin Technology Co., Ltd.", + [3]byte{0, 31, 183}: "WiMate Technologies Corp.", + [3]byte{0, 31, 184}: "Universal Remote Control, Inc.", + [3]byte{0, 31, 185}: "Paltronics", + [3]byte{0, 31, 186}: "Boyoung Tech", + [3]byte{0, 31, 187}: "Xenatech Co.,LTD", + [3]byte{0, 31, 188}: "EVGA Corporation", + [3]byte{0, 31, 189}: "Kyocera Wireless Corp.", + [3]byte{0, 31, 190}: "Shenzhen Mopnet Industrial Co.,Ltd", + [3]byte{0, 31, 191}: "Fulhua Microelectronics Corp. Taiwan Branch", + [3]byte{0, 31, 192}: "Control Express Finland Oy", + [3]byte{0, 31, 193}: "Hanlong Technology Co.,LTD", + [3]byte{0, 31, 194}: "Jow Tong Technology Co Ltd", + [3]byte{0, 31, 195}: "SmartSynch, Inc", + [3]byte{0, 31, 196}: "ARRIS Group, Inc.", + [3]byte{0, 31, 197}: "Nintendo Co., Ltd.", + [3]byte{0, 31, 198}: "ASUSTek COMPUTER INC.", + [3]byte{0, 31, 199}: "Casio Hitachi Mobile Communications Co., Ltd.", + [3]byte{0, 31, 200}: "Up-Today Industrial Co., Ltd.", + [3]byte{0, 31, 201}: "Cisco Systems, Inc", + [3]byte{0, 31, 202}: "Cisco Systems, Inc", + [3]byte{0, 31, 203}: "NIW Solutions", + [3]byte{0, 31, 204}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 31, 205}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 31, 206}: "QTECH LLC", + [3]byte{0, 31, 207}: "MSI Technology GmbH", + [3]byte{0, 31, 208}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{0, 31, 209}: "OPTEX CO.,LTD.", + [3]byte{0, 31, 210}: "COMMTECH TECHNOLOGY MACAO COMMERCIAL OFFSHORE LTD.", + [3]byte{0, 31, 211}: "RIVA Networks Inc.", + [3]byte{0, 31, 212}: "4IPNET, INC.", + [3]byte{0, 31, 213}: "MICRORISC s.r.o.", + [3]byte{0, 31, 214}: "Shenzhen Allywll", + [3]byte{0, 31, 215}: "TELERAD SA", + [3]byte{0, 31, 216}: "A-TRUST COMPUTER CORPORATION", + [3]byte{0, 31, 217}: "RSD Communications Ltd", + [3]byte{0, 31, 218}: "Nortel Networks", + [3]byte{0, 31, 219}: "Network Supply Corp.,", + [3]byte{0, 31, 220}: "Mobile Safe Track Ltd", + [3]byte{0, 31, 221}: "GDI LLC", + [3]byte{0, 31, 222}: "Nokia Danmark A/S", + [3]byte{0, 31, 223}: "Nokia Danmark A/S", + [3]byte{0, 31, 224}: "EdgeVelocity Corp", + [3]byte{0, 31, 225}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 31, 226}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 31, 227}: "LG Electronics (Mobile Communications)", + [3]byte{0, 31, 228}: "Sony Mobile Communications AB", + [3]byte{0, 31, 229}: "In-Circuit GmbH", + [3]byte{0, 31, 230}: "Alphion Corporation", + [3]byte{0, 31, 231}: "Simet", + [3]byte{0, 31, 232}: "KURUSUGAWA Electronics Industry Inc,.", + [3]byte{0, 31, 233}: "Printrex, Inc.", + [3]byte{0, 31, 234}: "Applied Media Technologies Corporation", + [3]byte{0, 31, 235}: "Trio Datacom Pty Ltd", + [3]byte{0, 31, 236}: "Synapse Électronique", + [3]byte{0, 31, 237}: "Tecan Systems Inc.", + [3]byte{0, 31, 238}: "ubisys technologies GmbH", + [3]byte{0, 31, 239}: "SHINSEI INDUSTRIES CO.,LTD", + [3]byte{0, 31, 240}: "Audio Partnership", + [3]byte{0, 31, 241}: "Paradox Hellas S.A.", + [3]byte{0, 31, 242}: "VIA Technologies, Inc.", + [3]byte{0, 31, 243}: "Apple, Inc.", + [3]byte{0, 31, 244}: "Power Monitors, Inc.", + [3]byte{0, 31, 245}: "Kongsberg Defence & Aerospace", + [3]byte{0, 31, 246}: "PS Audio International", + [3]byte{0, 31, 247}: "Nakajima All Precision Co., Ltd.", + [3]byte{0, 31, 248}: "Siemens AG, Sector Industry, Drive Technologies, Motion Control Systems", + [3]byte{0, 31, 249}: "Advanced Knowledge Associates", + [3]byte{0, 31, 250}: "Coretree, Co, Ltd", + [3]byte{0, 31, 251}: "Green Packet Bhd", + [3]byte{0, 31, 252}: "Riccius+Sohn GmbH", + [3]byte{0, 31, 253}: "Indigo Mobile Technologies Corp.", + [3]byte{0, 31, 254}: "HPN Supply Chain", + [3]byte{0, 31, 255}: "Respironics, Inc.", + [3]byte{0, 32, 0}: "LEXMARK INTERNATIONAL, INC.", + [3]byte{0, 32, 1}: "DSP SOLUTIONS, INC.", + [3]byte{0, 32, 2}: "SERITECH ENTERPRISE CO., LTD.", + [3]byte{0, 32, 3}: "PIXEL POWER LTD.", + [3]byte{0, 32, 4}: "YAMATAKE-HONEYWELL CO., LTD.", + [3]byte{0, 32, 5}: "SIMPLE TECHNOLOGY", + [3]byte{0, 32, 6}: "GARRETT COMMUNICATIONS, INC.", + [3]byte{0, 32, 7}: "SFA, INC.", + [3]byte{0, 32, 8}: "CABLE & COMPUTER TECHNOLOGY", + [3]byte{0, 32, 9}: "PACKARD BELL ELEC., INC.", + [3]byte{0, 32, 10}: "SOURCE-COMM CORP.", + [3]byte{0, 32, 11}: "OCTAGON SYSTEMS CORP.", + [3]byte{0, 32, 12}: "ADASTRA SYSTEMS CORP.", + [3]byte{0, 32, 13}: "CARL ZEISS", + [3]byte{0, 32, 14}: "SATELLITE TECHNOLOGY MGMT, INC", + [3]byte{0, 32, 15}: "EBRAINS Inc", + [3]byte{0, 32, 16}: "JEOL SYSTEM TECHNOLOGY CO. LTD", + [3]byte{0, 32, 17}: "CANOPUS CO., LTD.", + [3]byte{0, 32, 18}: "CAMTRONICS MEDICAL SYSTEMS", + [3]byte{0, 32, 19}: "DIVERSIFIED TECHNOLOGY, INC.", + [3]byte{0, 32, 20}: "GLOBAL VIEW CO., LTD.", + [3]byte{0, 32, 21}: "ACTIS COMPUTER SA", + [3]byte{0, 32, 22}: "SHOWA ELECTRIC WIRE & CABLE CO", + [3]byte{0, 32, 23}: "ORBOTECH", + [3]byte{0, 32, 24}: "CIS TECHNOLOGY INC.", + [3]byte{0, 32, 25}: "OHLER GMBH", + [3]byte{0, 32, 26}: "MRV Communications, Inc.", + [3]byte{0, 32, 27}: "NORTHERN TELECOM/NETWORK", + [3]byte{0, 32, 28}: "EXCEL, INC.", + [3]byte{0, 32, 29}: "KATANA PRODUCTS", + [3]byte{0, 32, 30}: "NETQUEST CORPORATION", + [3]byte{0, 32, 31}: "BEST POWER TECHNOLOGY, INC.", + [3]byte{0, 32, 32}: "MEGATRON COMPUTER INDUSTRIES PTY, LTD.", + [3]byte{0, 32, 33}: "ALGORITHMS SOFTWARE PVT. LTD.", + [3]byte{0, 32, 34}: "NMS Communications", + [3]byte{0, 32, 35}: "T.C. TECHNOLOGIES PTY. LTD", + [3]byte{0, 32, 36}: "PACIFIC COMMUNICATION SCIENCES", + [3]byte{0, 32, 37}: "CONTROL TECHNOLOGY, INC.", + [3]byte{0, 32, 38}: "AMKLY SYSTEMS, INC.", + [3]byte{0, 32, 39}: "MING FORTUNE INDUSTRY CO., LTD", + [3]byte{0, 32, 40}: "WEST EGG SYSTEMS, INC.", + [3]byte{0, 32, 41}: "TELEPROCESSING PRODUCTS, INC.", + [3]byte{0, 32, 42}: "N.V. DZINE", + [3]byte{0, 32, 43}: "ADVANCED TELECOMMUNICATIONS MODULES, LTD.", + [3]byte{0, 32, 44}: "WELLTRONIX CO., LTD.", + [3]byte{0, 32, 45}: "TAIYO CORPORATION", + [3]byte{0, 32, 46}: "DAYSTAR DIGITAL", + [3]byte{0, 32, 47}: "ZETA COMMUNICATIONS, LTD.", + [3]byte{0, 32, 48}: "ANALOG & DIGITAL SYSTEMS", + [3]byte{0, 32, 49}: "Tattile SRL ", + [3]byte{0, 32, 50}: "ALCATEL TAISEL", + [3]byte{0, 32, 51}: "SYNAPSE TECHNOLOGIES, INC.", + [3]byte{0, 32, 52}: "ROTEC INDUSTRIEAUTOMATION GMBH", + [3]byte{0, 32, 53}: "IBM Corp", + [3]byte{0, 32, 54}: "BMC SOFTWARE", + [3]byte{0, 32, 55}: "SEAGATE TECHNOLOGY", + [3]byte{0, 32, 56}: "VME MICROSYSTEMS INTERNATIONAL CORPORATION", + [3]byte{0, 32, 57}: "SCINETS", + [3]byte{0, 32, 58}: "DIGITAL BI0METRICS INC.", + [3]byte{0, 32, 59}: "WISDM LTD.", + [3]byte{0, 32, 60}: "EUROTIME AB", + [3]byte{0, 32, 61}: "Honeywell ECC", + [3]byte{0, 32, 62}: "LogiCan Technologies, Inc.", + [3]byte{0, 32, 63}: "JUKI CORPORATION", + [3]byte{0, 32, 64}: "ARRIS Group, Inc.", + [3]byte{0, 32, 65}: "DATA NET", + [3]byte{0, 32, 66}: "DATAMETRICS CORP.", + [3]byte{0, 32, 67}: "NEURON COMPANY LIMITED", + [3]byte{0, 32, 68}: "GENITECH PTY LTD", + [3]byte{0, 32, 69}: "ION Networks, Inc.", + [3]byte{0, 32, 70}: "CIPRICO, INC.", + [3]byte{0, 32, 71}: "STEINBRECHER CORP.", + [3]byte{0, 32, 72}: "Marconi Communications", + [3]byte{0, 32, 73}: "COMTRON, INC.", + [3]byte{0, 32, 74}: "PRONET GMBH", + [3]byte{0, 32, 75}: "AUTOCOMPUTER CO., LTD.", + [3]byte{0, 32, 76}: "MITRON COMPUTER PTE LTD.", + [3]byte{0, 32, 77}: "INOVIS GMBH", + [3]byte{0, 32, 78}: "NETWORK SECURITY SYSTEMS, INC.", + [3]byte{0, 32, 79}: "DEUTSCHE AEROSPACE AG", + [3]byte{0, 32, 80}: "KOREA COMPUTER INC.", + [3]byte{0, 32, 81}: "Verilink Corporation", + [3]byte{0, 32, 82}: "RAGULA SYSTEMS", + [3]byte{0, 32, 83}: "HUNTSVILLE MICROSYSTEMS, INC.", + [3]byte{0, 32, 84}: "Sycamore Networks", + [3]byte{0, 32, 85}: "ALTECH CO., LTD.", + [3]byte{0, 32, 86}: "NEOPRODUCTS", + [3]byte{0, 32, 87}: "TITZE DATENTECHNIK GmbH", + [3]byte{0, 32, 88}: "ALLIED SIGNAL INC.", + [3]byte{0, 32, 89}: "MIRO COMPUTER PRODUCTS AG", + [3]byte{0, 32, 90}: "COMPUTER IDENTICS", + [3]byte{0, 32, 91}: "Kentrox, LLC", + [3]byte{0, 32, 92}: "InterNet Systems of Florida, Inc.", + [3]byte{0, 32, 93}: "NANOMATIC OY", + [3]byte{0, 32, 94}: "CASTLE ROCK, INC.", + [3]byte{0, 32, 95}: "GAMMADATA COMPUTER GMBH", + [3]byte{0, 32, 96}: "ALCATEL ITALIA S.p.A.", + [3]byte{0, 32, 97}: "GarrettCom, Inc.", + [3]byte{0, 32, 98}: "SCORPION LOGIC, LTD.", + [3]byte{0, 32, 99}: "WIPRO INFOTECH LTD.", + [3]byte{0, 32, 100}: "PROTEC MICROSYSTEMS, INC.", + [3]byte{0, 32, 101}: "SUPERNET NETWORKING INC.", + [3]byte{0, 32, 102}: "GENERAL MAGIC, INC.", + [3]byte{0, 32, 103}: "Private", + [3]byte{0, 32, 104}: "ISDYNE", + [3]byte{0, 32, 105}: "ISDN SYSTEMS CORPORATION", + [3]byte{0, 32, 106}: "OSAKA COMPUTER CORP.", + [3]byte{0, 32, 107}: "KONICA MINOLTA HOLDINGS, INC.", + [3]byte{0, 32, 108}: "EVERGREEN TECHNOLOGY CORP.", + [3]byte{0, 32, 109}: "DATA RACE, INC.", + [3]byte{0, 32, 110}: "XACT, INC.", + [3]byte{0, 32, 111}: "FLOWPOINT CORPORATION", + [3]byte{0, 32, 112}: "HYNET, LTD.", + [3]byte{0, 32, 113}: "IBR GMBH", + [3]byte{0, 32, 114}: "WORKLINK INNOVATIONS", + [3]byte{0, 32, 115}: "FUSION SYSTEMS CORPORATION", + [3]byte{0, 32, 116}: "SUNGWOON SYSTEMS", + [3]byte{0, 32, 117}: "MOTOROLA COMMUNICATION ISRAEL", + [3]byte{0, 32, 118}: "REUDO CORPORATION", + [3]byte{0, 32, 119}: "KARDIOS SYSTEMS CORP.", + [3]byte{0, 32, 120}: "RUNTOP, INC.", + [3]byte{0, 32, 121}: "MIKRON GMBH", + [3]byte{0, 32, 122}: "WiSE Communications, Inc.", + [3]byte{0, 32, 123}: "Intel Corporation", + [3]byte{0, 32, 124}: "AUTEC GMBH", + [3]byte{0, 32, 125}: "ADVANCED COMPUTER APPLICATIONS", + [3]byte{0, 32, 126}: "FINECOM CO., LTD.", + [3]byte{0, 32, 127}: "KYOEI SANGYO CO., LTD.", + [3]byte{0, 32, 128}: "SYNERGY (UK) LTD.", + [3]byte{0, 32, 129}: "TITAN ELECTRONICS", + [3]byte{0, 32, 130}: "ONEAC CORPORATION", + [3]byte{0, 32, 131}: "PRESTICOM INCORPORATED", + [3]byte{0, 32, 132}: "OCE PRINTING SYSTEMS, GMBH", + [3]byte{0, 32, 133}: "Eaton Corporation", + [3]byte{0, 32, 134}: "MICROTECH ELECTRONICS LIMITED", + [3]byte{0, 32, 135}: "MEMOTEC, INC.", + [3]byte{0, 32, 136}: "GLOBAL VILLAGE COMMUNICATION", + [3]byte{0, 32, 137}: "T3PLUS NETWORKING, INC.", + [3]byte{0, 32, 138}: "SONIX COMMUNICATIONS, LTD.", + [3]byte{0, 32, 139}: "LAPIS TECHNOLOGIES, INC.", + [3]byte{0, 32, 140}: "GALAXY NETWORKS, INC.", + [3]byte{0, 32, 141}: "CMD TECHNOLOGY", + [3]byte{0, 32, 142}: "CHEVIN SOFTWARE ENG. LTD.", + [3]byte{0, 32, 143}: "ECI Telecom Ltd.", + [3]byte{0, 32, 144}: "ADVANCED COMPRESSION TECHNOLOGY, INC.", + [3]byte{0, 32, 145}: "J125, NATIONAL SECURITY AGENCY", + [3]byte{0, 32, 146}: "CHESS ENGINEERING B.V.", + [3]byte{0, 32, 147}: "LANDINGS TECHNOLOGY CORP.", + [3]byte{0, 32, 148}: "CUBIX CORPORATION", + [3]byte{0, 32, 149}: "RIVA ELECTRONICS", + [3]byte{0, 32, 150}: "Invensys", + [3]byte{0, 32, 151}: "APPLIED SIGNAL TECHNOLOGY", + [3]byte{0, 32, 152}: "HECTRONIC AB", + [3]byte{0, 32, 153}: "BON ELECTRIC CO., LTD.", + [3]byte{0, 32, 154}: "THE 3DO COMPANY", + [3]byte{0, 32, 155}: "ERSAT ELECTRONIC GMBH", + [3]byte{0, 32, 156}: "PRIMARY ACCESS CORP.", + [3]byte{0, 32, 157}: "LIPPERT AUTOMATIONSTECHNIK", + [3]byte{0, 32, 158}: "BROWN'S OPERATING SYSTEM SERVICES, LTD.", + [3]byte{0, 32, 159}: "MERCURY COMPUTER SYSTEMS, INC.", + [3]byte{0, 32, 160}: "OA LABORATORY CO., LTD.", + [3]byte{0, 32, 161}: "DOVATRON", + [3]byte{0, 32, 162}: "GALCOM NETWORKING LTD.", + [3]byte{0, 32, 163}: "Harmonic, Inc", + [3]byte{0, 32, 164}: "MULTIPOINT NETWORKS", + [3]byte{0, 32, 165}: "API ENGINEERING", + [3]byte{0, 32, 166}: "Proxim Wireless", + [3]byte{0, 32, 167}: "PAIRGAIN TECHNOLOGIES, INC.", + [3]byte{0, 32, 168}: "SAST TECHNOLOGY CORP.", + [3]byte{0, 32, 169}: "WHITE HORSE INDUSTRIAL", + [3]byte{0, 32, 170}: "Ericsson Television Limited", + [3]byte{0, 32, 171}: "MICRO INDUSTRIES CORP.", + [3]byte{0, 32, 172}: "INTERFLEX DATENSYSTEME GMBH", + [3]byte{0, 32, 173}: "LINQ SYSTEMS", + [3]byte{0, 32, 174}: "ORNET DATA COMMUNICATION TECH.", + [3]byte{0, 32, 175}: "3COM CORPORATION", + [3]byte{0, 32, 176}: "GATEWAY DEVICES, INC.", + [3]byte{0, 32, 177}: "COMTECH RESEARCH INC.", + [3]byte{0, 32, 178}: "GKD Gesellschaft Fur Kommunikation Und Datentechnik", + [3]byte{0, 32, 179}: "Tattile SRL ", + [3]byte{0, 32, 180}: "TERMA ELEKTRONIK AS", + [3]byte{0, 32, 181}: "YASKAWA ELECTRIC CORPORATION", + [3]byte{0, 32, 182}: "AGILE NETWORKS, INC.", + [3]byte{0, 32, 183}: "NAMAQUA COMPUTERWARE", + [3]byte{0, 32, 184}: "PRIME OPTION, INC.", + [3]byte{0, 32, 185}: "METRICOM, INC.", + [3]byte{0, 32, 186}: "CENTER FOR HIGH PERFORMANCE", + [3]byte{0, 32, 187}: "ZAX CORPORATION", + [3]byte{0, 32, 188}: "Long Reach Networks Pty Ltd", + [3]byte{0, 32, 189}: "NIOBRARA R & D CORPORATION", + [3]byte{0, 32, 190}: "LAN ACCESS CORP.", + [3]byte{0, 32, 191}: "AEHR TEST SYSTEMS", + [3]byte{0, 32, 192}: "PULSE ELECTRONICS, INC.", + [3]byte{0, 32, 193}: "SAXA, Inc.", + [3]byte{0, 32, 194}: "TEXAS MEMORY SYSTEMS, INC.", + [3]byte{0, 32, 195}: "COUNTER SOLUTIONS LTD.", + [3]byte{0, 32, 196}: "INET,INC.", + [3]byte{0, 32, 197}: "EAGLE TECHNOLOGY", + [3]byte{0, 32, 198}: "NECTEC", + [3]byte{0, 32, 199}: "AKAI Professional M.I. Corp.", + [3]byte{0, 32, 200}: "LARSCOM INCORPORATED", + [3]byte{0, 32, 201}: "VICTRON BV", + [3]byte{0, 32, 202}: "DIGITAL OCEAN", + [3]byte{0, 32, 203}: "PRETEC ELECTRONICS CORP.", + [3]byte{0, 32, 204}: "DIGITAL SERVICES, LTD.", + [3]byte{0, 32, 205}: "HYBRID NETWORKS, INC.", + [3]byte{0, 32, 206}: "LOGICAL DESIGN GROUP, INC.", + [3]byte{0, 32, 207}: "TEST & MEASUREMENT SYSTEMS INC", + [3]byte{0, 32, 208}: "VERSALYNX CORPORATION", + [3]byte{0, 32, 209}: "MICROCOMPUTER SYSTEMS (M) SDN.", + [3]byte{0, 32, 210}: "RAD DATA COMMUNICATIONS, LTD.", + [3]byte{0, 32, 211}: "OST (OUEST STANDARD TELEMATIQU", + [3]byte{0, 32, 212}: "Cabletron Systems, Inc.", + [3]byte{0, 32, 213}: "VIPA GMBH", + [3]byte{0, 32, 214}: "Breezecom, Ltd.", + [3]byte{0, 32, 215}: "JAPAN MINICOMPUTER SYSTEMS CO., Ltd.", + [3]byte{0, 32, 216}: "Nortel Networks", + [3]byte{0, 32, 217}: "PANASONIC TECHNOLOGIES, INC./MIECO-US", + [3]byte{0, 32, 218}: "Alcatel-Lucent Enterprise", + [3]byte{0, 32, 219}: "XNET TECHNOLOGY, INC.", + [3]byte{0, 32, 220}: "DENSITRON TAIWAN LTD.", + [3]byte{0, 32, 221}: "Cybertec Pty Ltd", + [3]byte{0, 32, 222}: "JAPAN DIGITAL LABORAT'Y CO.LTD", + [3]byte{0, 32, 223}: "KYOSAN ELECTRIC MFG. CO., LTD.", + [3]byte{0, 32, 224}: "Actiontec Electronics, Inc", + [3]byte{0, 32, 225}: "ALAMAR ELECTRONICS", + [3]byte{0, 32, 226}: "INFORMATION RESOURCE ENGINEERING", + [3]byte{0, 32, 227}: "MCD KENCOM CORPORATION", + [3]byte{0, 32, 228}: "HSING TECH ENTERPRISE CO., LTD", + [3]byte{0, 32, 229}: "APEX DATA, INC.", + [3]byte{0, 32, 230}: "LIDKOPING MACHINE TOOLS AB", + [3]byte{0, 32, 231}: "B&W NUCLEAR SERVICE COMPANY", + [3]byte{0, 32, 232}: "DATATREK CORPORATION", + [3]byte{0, 32, 233}: "DANTEL", + [3]byte{0, 32, 234}: "EFFICIENT NETWORKS, INC.", + [3]byte{0, 32, 235}: "CINCINNATI MICROWAVE, INC.", + [3]byte{0, 32, 236}: "TECHWARE SYSTEMS CORP.", + [3]byte{0, 32, 237}: "GIGA-BYTE TECHNOLOGY CO., LTD.", + [3]byte{0, 32, 238}: "GTECH CORPORATION", + [3]byte{0, 32, 239}: "USC CORPORATION", + [3]byte{0, 32, 240}: "UNIVERSAL MICROELECTRONICS CO.", + [3]byte{0, 32, 241}: "ALTOS INDIA LIMITED", + [3]byte{0, 32, 242}: "Oracle Corporation ", + [3]byte{0, 32, 243}: "RAYNET CORPORATION", + [3]byte{0, 32, 244}: "SPECTRIX CORPORATION", + [3]byte{0, 32, 245}: "PANDATEL AG", + [3]byte{0, 32, 246}: "NET TEK AND KARLNET, INC.", + [3]byte{0, 32, 247}: "CYBERDATA CORPORATION", + [3]byte{0, 32, 248}: "CARRERA COMPUTERS, INC.", + [3]byte{0, 32, 249}: "PARALINK NETWORKS, INC.", + [3]byte{0, 32, 250}: "GDE SYSTEMS, INC.", + [3]byte{0, 32, 251}: "OCTEL COMMUNICATIONS CORP.", + [3]byte{0, 32, 252}: "MATROX", + [3]byte{0, 32, 253}: "ITV TECHNOLOGIES, INC.", + [3]byte{0, 32, 254}: "TOPWARE INC. / GRAND COMPUTER", + [3]byte{0, 32, 255}: "SYMMETRICAL TECHNOLOGIES", + [3]byte{0, 33, 0}: "Gemtek Technology Co., Ltd.", + [3]byte{0, 33, 1}: "Aplicaciones Electronicas Quasar (AEQ)", + [3]byte{0, 33, 2}: "UpdateLogic Inc.", + [3]byte{0, 33, 3}: "GHI Electronics, LLC", + [3]byte{0, 33, 4}: "Gigaset Communications GmbH", + [3]byte{0, 33, 5}: "Alcatel-Lucent IPD", + [3]byte{0, 33, 6}: "RIM Testing Services", + [3]byte{0, 33, 7}: "Seowonintech Co Ltd.", + [3]byte{0, 33, 8}: "Nokia Danmark A/S", + [3]byte{0, 33, 9}: "Nokia Danmark A/S", + [3]byte{0, 33, 10}: "byd:sign Corporation", + [3]byte{0, 33, 11}: "GEMINI TRAZE RFID PVT. LTD.", + [3]byte{0, 33, 12}: "Cymtec Systems, Inc.", + [3]byte{0, 33, 13}: "SAMSIN INNOTEC", + [3]byte{0, 33, 14}: "Orpak Systems L.T.D.", + [3]byte{0, 33, 15}: "Cernium Corp", + [3]byte{0, 33, 16}: "Clearbox Systems", + [3]byte{0, 33, 17}: "Uniphone Inc.", + [3]byte{0, 33, 18}: "WISCOM SYSTEM CO.,LTD", + [3]byte{0, 33, 19}: "Padtec S/A", + [3]byte{0, 33, 20}: "Hylab Technology Inc.", + [3]byte{0, 33, 21}: "PHYWE Systeme GmbH & Co. KG", + [3]byte{0, 33, 22}: "Transcon Electronic Systems, spol. s r. o.", + [3]byte{0, 33, 23}: "Tellord", + [3]byte{0, 33, 24}: "Athena Tech, Inc.", + [3]byte{0, 33, 25}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{0, 33, 26}: "LInTech Corporation", + [3]byte{0, 33, 27}: "Cisco Systems, Inc", + [3]byte{0, 33, 28}: "Cisco Systems, Inc", + [3]byte{0, 33, 29}: "Dataline AB", + [3]byte{0, 33, 30}: "ARRIS Group, Inc.", + [3]byte{0, 33, 31}: "SHINSUNG DELTATECH CO.,LTD.", + [3]byte{0, 33, 32}: "Sequel Technologies", + [3]byte{0, 33, 33}: "VRmagic GmbH", + [3]byte{0, 33, 34}: "Chip-pro Ltd.", + [3]byte{0, 33, 35}: "Aerosat Avionics", + [3]byte{0, 33, 36}: "Optos Plc", + [3]byte{0, 33, 37}: "KUK JE TONG SHIN Co.,LTD", + [3]byte{0, 33, 38}: "Shenzhen Torch Equipment Co., Ltd.", + [3]byte{0, 33, 39}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 33, 40}: "Oracle Corporation", + [3]byte{0, 33, 41}: "Cisco-Linksys, LLC", + [3]byte{0, 33, 42}: "Audiovox Corporation", + [3]byte{0, 33, 43}: "MSA Auer", + [3]byte{0, 33, 44}: "SemIndia System Private Limited", + [3]byte{0, 33, 45}: "SCIMOLEX CORPORATION", + [3]byte{0, 33, 46}: "dresden-elektronik", + [3]byte{0, 33, 47}: "Phoebe Micro Inc.", + [3]byte{0, 33, 48}: "Keico Hightech Inc.", + [3]byte{0, 33, 49}: "Blynke Inc.", + [3]byte{0, 33, 50}: "Masterclock, Inc.", + [3]byte{0, 33, 51}: "Building B, Inc", + [3]byte{0, 33, 52}: "Brandywine Communications", + [3]byte{0, 33, 53}: "ALCATEL-LUCENT", + [3]byte{0, 33, 54}: "ARRIS Group, Inc.", + [3]byte{0, 33, 55}: "Bay Controls, LLC", + [3]byte{0, 33, 56}: "Cepheid", + [3]byte{0, 33, 57}: "Escherlogic Inc.", + [3]byte{0, 33, 58}: "Winchester Systems Inc.", + [3]byte{0, 33, 59}: "Berkshire Products, Inc", + [3]byte{0, 33, 60}: "AliphCom", + [3]byte{0, 33, 61}: "Cermetek Microelectronics, Inc.", + [3]byte{0, 33, 62}: "TomTom", + [3]byte{0, 33, 63}: "A-Team Technology Ltd.", + [3]byte{0, 33, 64}: "EN Technologies Inc.", + [3]byte{0, 33, 65}: "RADLIVE", + [3]byte{0, 33, 66}: "Advanced Control Systems doo", + [3]byte{0, 33, 67}: "ARRIS Group, Inc.", + [3]byte{0, 33, 68}: "SS Telecoms", + [3]byte{0, 33, 69}: "Semptian Technologies Ltd.", + [3]byte{0, 33, 70}: "Sanmina-SCI", + [3]byte{0, 33, 71}: "Nintendo Co., Ltd.", + [3]byte{0, 33, 72}: "Kaco Solar Korea", + [3]byte{0, 33, 73}: "China Daheng Group ,Inc.", + [3]byte{0, 33, 74}: "Pixel Velocity, Inc", + [3]byte{0, 33, 75}: "Shenzhen HAMP Science & Technology Co.,Ltd", + [3]byte{0, 33, 76}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 33, 77}: "Guangzhou Skytone Transmission Technology Com. Ltd.", + [3]byte{0, 33, 78}: "GS Yuasa Power Supply Ltd.", + [3]byte{0, 33, 79}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 33, 80}: "EYEVIEW ELECTRONICS", + [3]byte{0, 33, 81}: "Millinet Co., Ltd.", + [3]byte{0, 33, 82}: "General Satellite Research & Development Limited", + [3]byte{0, 33, 83}: "SeaMicro Inc.", + [3]byte{0, 33, 84}: "D-TACQ Solutions Ltd", + [3]byte{0, 33, 85}: "Cisco Systems, Inc", + [3]byte{0, 33, 86}: "Cisco Systems, Inc", + [3]byte{0, 33, 87}: "National Datacast, Inc.", + [3]byte{0, 33, 88}: "Style Flying Technology Co.", + [3]byte{0, 33, 89}: "Juniper Networks", + [3]byte{0, 33, 90}: "Hewlett Packard", + [3]byte{0, 33, 91}: "SenseAnywhere", + [3]byte{0, 33, 92}: "Intel Corporate", + [3]byte{0, 33, 93}: "Intel Corporate", + [3]byte{0, 33, 94}: "IBM Corp", + [3]byte{0, 33, 95}: "IHSE GmbH", + [3]byte{0, 33, 96}: "Hidea Solutions Co. Ltd.", + [3]byte{0, 33, 97}: "Yournet Inc.", + [3]byte{0, 33, 98}: "Nortel Networks", + [3]byte{0, 33, 99}: "ASKEY COMPUTER CORP", + [3]byte{0, 33, 100}: "Special Design Bureau for Seismic Instrumentation", + [3]byte{0, 33, 101}: "Presstek Inc.", + [3]byte{0, 33, 102}: "NovAtel Inc.", + [3]byte{0, 33, 103}: "HWA JIN T&I Corp.", + [3]byte{0, 33, 104}: "iVeia, LLC", + [3]byte{0, 33, 105}: "Prologix, LLC.", + [3]byte{0, 33, 106}: "Intel Corporate", + [3]byte{0, 33, 107}: "Intel Corporate", + [3]byte{0, 33, 108}: "ODVA", + [3]byte{0, 33, 109}: "Soltech Co., Ltd.", + [3]byte{0, 33, 110}: "Function ATI (Huizhou) Telecommunications Co., Ltd.", + [3]byte{0, 33, 111}: "SymCom, Inc.", + [3]byte{0, 33, 112}: "Dell Inc.", + [3]byte{0, 33, 113}: "Wesung TNC Co., Ltd.", + [3]byte{0, 33, 114}: "Seoultek Valley", + [3]byte{0, 33, 115}: "Ion Torrent Systems, Inc.", + [3]byte{0, 33, 116}: "AvaLAN Wireless", + [3]byte{0, 33, 117}: "Pacific Satellite International Ltd.", + [3]byte{0, 33, 118}: "YMax Telecom Ltd.", + [3]byte{0, 33, 119}: "W. L. Gore & Associates", + [3]byte{0, 33, 120}: "Matuschek Messtechnik GmbH", + [3]byte{0, 33, 121}: "IOGEAR, Inc.", + [3]byte{0, 33, 122}: "Sejin Electron, Inc.", + [3]byte{0, 33, 123}: "Bastec AB", + [3]byte{0, 33, 124}: "2Wire Inc", + [3]byte{0, 33, 125}: "PYXIS S.R.L.", + [3]byte{0, 33, 126}: "Telit Communication s.p.a", + [3]byte{0, 33, 127}: "Intraco Technology Pte Ltd", + [3]byte{0, 33, 128}: "ARRIS Group, Inc.", + [3]byte{0, 33, 129}: "Si2 Microsystems Limited", + [3]byte{0, 33, 130}: "SandLinks Systems, Ltd.", + [3]byte{0, 33, 131}: "ANDRITZ HYDRO GmbH", + [3]byte{0, 33, 132}: "POWERSOFT SRL", + [3]byte{0, 33, 133}: "MICRO-STAR INT'L CO.,LTD.", + [3]byte{0, 33, 134}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{0, 33, 135}: "Imacs GmbH", + [3]byte{0, 33, 136}: "EMC Corporation", + [3]byte{0, 33, 137}: "AppTech, Inc.", + [3]byte{0, 33, 138}: "Electronic Design and Manufacturing Company", + [3]byte{0, 33, 139}: "Wescon Technology, Inc.", + [3]byte{0, 33, 140}: "TopControl GMBH", + [3]byte{0, 33, 141}: "AP Router Ind. Eletronica LTDA", + [3]byte{0, 33, 142}: "MEKICS CO., LTD.", + [3]byte{0, 33, 143}: "Avantgarde Acoustic Lautsprechersysteme GmbH", + [3]byte{0, 33, 144}: "Goliath Solutions", + [3]byte{0, 33, 145}: "D-Link Corporation", + [3]byte{0, 33, 146}: "Baoding Galaxy Electronic Technology Co.,Ltd", + [3]byte{0, 33, 147}: "Videofon MV", + [3]byte{0, 33, 148}: "Ping Communication", + [3]byte{0, 33, 149}: "GWD Media Limited", + [3]byte{0, 33, 150}: "Telsey S.p.A.", + [3]byte{0, 33, 151}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 33, 152}: "Thai Radio Co, LTD", + [3]byte{0, 33, 153}: "Vacon Plc", + [3]byte{0, 33, 154}: "Cambridge Visual Networks Ltd", + [3]byte{0, 33, 155}: "Dell Inc.", + [3]byte{0, 33, 156}: "Honeywld Technology Corp.", + [3]byte{0, 33, 157}: "Adesys BV", + [3]byte{0, 33, 158}: "Sony Mobile Communications AB", + [3]byte{0, 33, 159}: "SATEL OY", + [3]byte{0, 33, 160}: "Cisco Systems, Inc", + [3]byte{0, 33, 161}: "Cisco Systems, Inc", + [3]byte{0, 33, 162}: "EKE-Electronics Ltd.", + [3]byte{0, 33, 163}: "Micromint", + [3]byte{0, 33, 164}: "Dbii Networks", + [3]byte{0, 33, 165}: "ERLPhase Power Technologies Ltd.", + [3]byte{0, 33, 166}: "Videotec Spa", + [3]byte{0, 33, 167}: "Hantle System Co., Ltd.", + [3]byte{0, 33, 168}: "Telephonics Corporation", + [3]byte{0, 33, 169}: "Mobilink Telecom Co.,Ltd", + [3]byte{0, 33, 170}: "Nokia Danmark A/S", + [3]byte{0, 33, 171}: "Nokia Danmark A/S", + [3]byte{0, 33, 172}: "Infrared Integrated Systems Ltd", + [3]byte{0, 33, 173}: "Nordic ID Oy", + [3]byte{0, 33, 174}: "ALCATEL-LUCENT FRANCE - WTD", + [3]byte{0, 33, 175}: "Radio Frequency Systems", + [3]byte{0, 33, 176}: "Tyco Telecommunications", + [3]byte{0, 33, 177}: "DIGITAL SOLUTIONS LTD", + [3]byte{0, 33, 178}: "Fiberblaze A/S", + [3]byte{0, 33, 179}: "Ross Controls", + [3]byte{0, 33, 180}: "APRO MEDIA CO., LTD", + [3]byte{0, 33, 181}: "Galvanic Ltd", + [3]byte{0, 33, 182}: "Triacta Power Technologies Inc.", + [3]byte{0, 33, 183}: "Lexmark International Inc.", + [3]byte{0, 33, 184}: "Inphi Corporation", + [3]byte{0, 33, 185}: "Universal Devices Inc.", + [3]byte{0, 33, 186}: "Texas Instruments", + [3]byte{0, 33, 187}: "Riken Keiki Co., Ltd.", + [3]byte{0, 33, 188}: "ZALA COMPUTER", + [3]byte{0, 33, 189}: "Nintendo Co., Ltd.", + [3]byte{0, 33, 190}: "Cisco SPVTG", + [3]byte{0, 33, 191}: "Hitachi High-Tech Control Systems Corporation", + [3]byte{0, 33, 192}: "Mobile Appliance, Inc.", + [3]byte{0, 33, 193}: "ABB Oy / Medium Voltage Products", + [3]byte{0, 33, 194}: "GL Communications Inc", + [3]byte{0, 33, 195}: "CORNELL Communications, Inc.", + [3]byte{0, 33, 196}: "Consilium AB", + [3]byte{0, 33, 197}: "3DSP Corp", + [3]byte{0, 33, 198}: "CSJ Global, Inc.", + [3]byte{0, 33, 199}: "Russound", + [3]byte{0, 33, 200}: "LOHUIS Networks", + [3]byte{0, 33, 201}: "Wavecom Asia Pacific Limited", + [3]byte{0, 33, 202}: "ART System Co., Ltd.", + [3]byte{0, 33, 203}: "SMS TECNOLOGIA ELETRONICA LTDA", + [3]byte{0, 33, 204}: "Flextronics International", + [3]byte{0, 33, 205}: "LiveTV", + [3]byte{0, 33, 206}: "NTC-Metrotek", + [3]byte{0, 33, 207}: "The Crypto Group", + [3]byte{0, 33, 208}: "Global Display Solutions Spa", + [3]byte{0, 33, 209}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 33, 210}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 33, 211}: "BOCOM SECURITY(ASIA PACIFIC) LIMITED", + [3]byte{0, 33, 212}: "Vollmer Werke GmbH", + [3]byte{0, 33, 213}: "X2E GmbH", + [3]byte{0, 33, 214}: "LXI Consortium", + [3]byte{0, 33, 215}: "Cisco Systems, Inc", + [3]byte{0, 33, 216}: "Cisco Systems, Inc", + [3]byte{0, 33, 217}: "SEKONIC CORPORATION", + [3]byte{0, 33, 218}: "Automation Products Group Inc.", + [3]byte{0, 33, 219}: "Santachi Video Technology (Shenzhen) Co., Ltd.", + [3]byte{0, 33, 220}: "TECNOALARM S.r.l.", + [3]byte{0, 33, 221}: "Northstar Systems Corp", + [3]byte{0, 33, 222}: "Firepro Wireless", + [3]byte{0, 33, 223}: "Martin Christ GmbH", + [3]byte{0, 33, 224}: "CommAgility Ltd", + [3]byte{0, 33, 225}: "Nortel Networks", + [3]byte{0, 33, 226}: "visago Systems & Controls GmbH & Co. KG", + [3]byte{0, 33, 227}: "SerialTek LLC", + [3]byte{0, 33, 228}: "I-WIN", + [3]byte{0, 33, 229}: "Display Solution AG", + [3]byte{0, 33, 230}: "Starlight Video Limited", + [3]byte{0, 33, 231}: "Informatics Services Corporation", + [3]byte{0, 33, 232}: "Murata Manufacturing Co., Ltd.", + [3]byte{0, 33, 233}: "Apple, Inc.", + [3]byte{0, 33, 234}: "Bystronic Laser AG", + [3]byte{0, 33, 235}: "ESP SYSTEMS, LLC", + [3]byte{0, 33, 236}: "Solutronic GmbH", + [3]byte{0, 33, 237}: "Telegesis", + [3]byte{0, 33, 238}: "Full Spectrum Inc.", + [3]byte{0, 33, 239}: "Kapsys", + [3]byte{0, 33, 240}: "EW3 Technologies LLC", + [3]byte{0, 33, 241}: "Tutus Data AB", + [3]byte{0, 33, 242}: "EASY3CALL Technology Limited", + [3]byte{0, 33, 243}: "Si14 SpA", + [3]byte{0, 33, 244}: "INRange Systems, Inc", + [3]byte{0, 33, 245}: "Western Engravers Supply, Inc.", + [3]byte{0, 33, 246}: "Oracle Corporation", + [3]byte{0, 33, 247}: "HPN Supply Chain", + [3]byte{0, 33, 248}: "Enseo, Inc.", + [3]byte{0, 33, 249}: "WIRECOM Technologies", + [3]byte{0, 33, 250}: "A4SP Technologies Ltd.", + [3]byte{0, 33, 251}: "LG Electronics (Mobile Communications)", + [3]byte{0, 33, 252}: "Nokia Danmark A/S", + [3]byte{0, 33, 253}: "LACROIX TRAFFIC S.A.U", + [3]byte{0, 33, 254}: "Nokia Danmark A/S", + [3]byte{0, 33, 255}: "Cyfrowy Polsat SA", + [3]byte{0, 34, 0}: "IBM Corp", + [3]byte{0, 34, 1}: "Aksys Networks Inc", + [3]byte{0, 34, 2}: "Excito Elektronik i Skåne AB", + [3]byte{0, 34, 3}: "Glensound Electronics Ltd", + [3]byte{0, 34, 4}: "KORATEK", + [3]byte{0, 34, 5}: "WeLink Solutions, Inc.", + [3]byte{0, 34, 6}: "Cyberdyne Inc.", + [3]byte{0, 34, 7}: "Inteno Broadband Technology AB", + [3]byte{0, 34, 8}: "Certicom Corp", + [3]byte{0, 34, 9}: "Omron Healthcare Co., Ltd", + [3]byte{0, 34, 10}: "OnLive, Inc", + [3]byte{0, 34, 11}: "National Source Coding Center", + [3]byte{0, 34, 12}: "Cisco Systems, Inc", + [3]byte{0, 34, 13}: "Cisco Systems, Inc", + [3]byte{0, 34, 14}: "Indigo Security Co., Ltd.", + [3]byte{0, 34, 15}: "MoCA (Multimedia over Coax Alliance)", + [3]byte{0, 34, 16}: "ARRIS Group, Inc.", + [3]byte{0, 34, 17}: "Rohati Systems", + [3]byte{0, 34, 18}: "CAI Networks, Inc.", + [3]byte{0, 34, 19}: "PCI CORPORATION", + [3]byte{0, 34, 20}: "RINNAI KOREA", + [3]byte{0, 34, 21}: "ASUSTek COMPUTER INC.", + [3]byte{0, 34, 22}: "SHIBAURA VENDING MACHINE CORPORATION", + [3]byte{0, 34, 23}: "Neat Electronics", + [3]byte{0, 34, 24}: "Verivue Inc.", + [3]byte{0, 34, 25}: "Dell Inc.", + [3]byte{0, 34, 26}: "Audio Precision", + [3]byte{0, 34, 27}: "Morega Systems", + [3]byte{0, 34, 28}: "Private", + [3]byte{0, 34, 29}: "Freegene Technology LTD", + [3]byte{0, 34, 30}: "Media Devices Co., Ltd.", + [3]byte{0, 34, 31}: "eSang Technologies Co., Ltd.", + [3]byte{0, 34, 32}: "Mitac Technology Corp", + [3]byte{0, 34, 33}: "ITOH DENKI CO,LTD.", + [3]byte{0, 34, 34}: "Schaffner Deutschland GmbH", + [3]byte{0, 34, 35}: "TimeKeeping Systems, Inc.", + [3]byte{0, 34, 36}: "Good Will Instrument Co., Ltd.", + [3]byte{0, 34, 37}: "Thales Avionics Ltd", + [3]byte{0, 34, 38}: "Avaak, Inc.", + [3]byte{0, 34, 39}: "uv-electronic GmbH", + [3]byte{0, 34, 40}: "Breeze Innovations Ltd.", + [3]byte{0, 34, 41}: "Compumedics Ltd", + [3]byte{0, 34, 42}: "SoundEar A/S", + [3]byte{0, 34, 43}: "Nucomm, Inc.", + [3]byte{0, 34, 44}: "Ceton Corp", + [3]byte{0, 34, 45}: "SMC Networks Inc.", + [3]byte{0, 34, 46}: "maintech GmbH", + [3]byte{0, 34, 47}: "Open Grid Computing, Inc.", + [3]byte{0, 34, 48}: "FutureLogic Inc.", + [3]byte{0, 34, 49}: "SMT&C Co., Ltd.", + [3]byte{0, 34, 50}: "Design Design Technology Ltd", + [3]byte{0, 34, 51}: "ADB Broadband Italia", + [3]byte{0, 34, 52}: "Corventis Inc.", + [3]byte{0, 34, 53}: "Strukton Systems bv", + [3]byte{0, 34, 54}: "VECTOR SP. Z O.O.", + [3]byte{0, 34, 55}: "Shinhint Group", + [3]byte{0, 34, 56}: "LOGIPLUS", + [3]byte{0, 34, 57}: "Indiana Life Sciences Incorporated", + [3]byte{0, 34, 58}: "Cisco SPVTG", + [3]byte{0, 34, 59}: "Communication Networks, LLC", + [3]byte{0, 34, 60}: "RATIO Entwicklungen GmbH", + [3]byte{0, 34, 61}: "JumpGen Systems, LLC", + [3]byte{0, 34, 62}: "IRTrans GmbH", + [3]byte{0, 34, 63}: "NETGEAR", + [3]byte{0, 34, 64}: "Universal Telecom S/A", + [3]byte{0, 34, 65}: "Apple, Inc.", + [3]byte{0, 34, 66}: "Alacron Inc.", + [3]byte{0, 34, 67}: "AzureWave Technology Inc.", + [3]byte{0, 34, 68}: "Chengdu Linkon Communications Device Co., Ltd", + [3]byte{0, 34, 69}: "Leine & Linde AB", + [3]byte{0, 34, 70}: "Evoc Intelligent Technology Co.,Ltd.", + [3]byte{0, 34, 71}: "DAC ENGINEERING CO., LTD.", + [3]byte{0, 34, 72}: "Microsoft Corporation", + [3]byte{0, 34, 73}: "HOME MULTIENERGY SL", + [3]byte{0, 34, 74}: "RAYLASE AG", + [3]byte{0, 34, 75}: "AIRTECH TECHNOLOGIES, INC.", + [3]byte{0, 34, 76}: "Nintendo Co., Ltd.", + [3]byte{0, 34, 77}: "MITAC INTERNATIONAL CORP.", + [3]byte{0, 34, 78}: "SEEnergy Corp.", + [3]byte{0, 34, 79}: "Byzoro Networks Ltd.", + [3]byte{0, 34, 80}: "Point Six Wireless, LLC", + [3]byte{0, 34, 81}: "Lumasense Technologies", + [3]byte{0, 34, 82}: "ZOLL Lifecor Corporation", + [3]byte{0, 34, 83}: "Entorian Technologies", + [3]byte{0, 34, 84}: "Bigelow Aerospace", + [3]byte{0, 34, 85}: "Cisco Systems, Inc", + [3]byte{0, 34, 86}: "Cisco Systems, Inc", + [3]byte{0, 34, 87}: "3COM EUROPE LTD", + [3]byte{0, 34, 88}: "Taiyo Yuden Co., Ltd.", + [3]byte{0, 34, 89}: "Guangzhou New Postcom Equipment Co.,Ltd.", + [3]byte{0, 34, 90}: "Garde Security AB", + [3]byte{0, 34, 91}: "Teradici Corporation", + [3]byte{0, 34, 92}: "Multimedia & Communication Technology", + [3]byte{0, 34, 93}: "Digicable Network India Pvt. Ltd.", + [3]byte{0, 34, 94}: "Uwin Technologies Co.,LTD", + [3]byte{0, 34, 95}: "Liteon Technology Corporation", + [3]byte{0, 34, 96}: "AFREEY Inc.", + [3]byte{0, 34, 97}: "Frontier Silicon Ltd", + [3]byte{0, 34, 98}: "BEP Marine", + [3]byte{0, 34, 99}: "Koos Technical Services, Inc.", + [3]byte{0, 34, 100}: "Hewlett Packard", + [3]byte{0, 34, 101}: "Nokia Danmark A/S", + [3]byte{0, 34, 102}: "Nokia Danmark A/S", + [3]byte{0, 34, 103}: "Nortel Networks", + [3]byte{0, 34, 104}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 34, 105}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 34, 106}: "Honeywell", + [3]byte{0, 34, 107}: "Cisco-Linksys, LLC", + [3]byte{0, 34, 108}: "LinkSprite Technologies, Inc.", + [3]byte{0, 34, 109}: "Shenzhen GIEC Electronics Co., Ltd.", + [3]byte{0, 34, 110}: "Gowell Electronic Limited", + [3]byte{0, 34, 111}: "3onedata Technology Co. Ltd.", + [3]byte{0, 34, 112}: "ABK North America, LLC", + [3]byte{0, 34, 113}: "Jäger Computergesteuerte Meßtechnik GmbH.", + [3]byte{0, 34, 114}: "American Micro-Fuel Device Corp.", + [3]byte{0, 34, 115}: "Techway", + [3]byte{0, 34, 116}: "FamilyPhone AB", + [3]byte{0, 34, 117}: "Belkin International Inc.", + [3]byte{0, 34, 118}: "Triple EYE B.V.", + [3]byte{0, 34, 119}: "NEC Australia Pty Ltd", + [3]byte{0, 34, 120}: "Shenzhen Tongfang Multimedia Technology Co.,Ltd.", + [3]byte{0, 34, 121}: "Nippon Conlux Co., Ltd.", + [3]byte{0, 34, 122}: "Telecom Design", + [3]byte{0, 34, 123}: "Apogee Labs, Inc.", + [3]byte{0, 34, 124}: "Woori SMT Co.,ltd", + [3]byte{0, 34, 125}: "YE DATA INC.", + [3]byte{0, 34, 126}: "Chengdu 30Kaitian Communication Industry Co.Ltd", + [3]byte{0, 34, 127}: "Ruckus Wireless", + [3]byte{0, 34, 128}: "A2B Electronics AB", + [3]byte{0, 34, 129}: "Daintree Networks Pty", + [3]byte{0, 34, 130}: "8086 Consultancy", + [3]byte{0, 34, 131}: "Juniper Networks", + [3]byte{0, 34, 132}: "DESAY A&V SCIENCE AND TECHNOLOGY CO.,LTD", + [3]byte{0, 34, 133}: "NOMUS COMM SYSTEMS", + [3]byte{0, 34, 134}: "ASTRON", + [3]byte{0, 34, 135}: "Titan Wireless LLC", + [3]byte{0, 34, 136}: "Sagrad, Inc.", + [3]byte{0, 34, 137}: "Optosecurity Inc.", + [3]byte{0, 34, 138}: "Teratronik elektronische systeme gmbh", + [3]byte{0, 34, 139}: "Kensington Computer Products Group", + [3]byte{0, 34, 140}: "Photon Europe GmbH", + [3]byte{0, 34, 141}: "GBS Laboratories LLC", + [3]byte{0, 34, 142}: "TV-NUMERIC", + [3]byte{0, 34, 143}: "CNRS", + [3]byte{0, 34, 144}: "Cisco Systems, Inc", + [3]byte{0, 34, 145}: "Cisco Systems, Inc", + [3]byte{0, 34, 146}: "Cinetal", + [3]byte{0, 34, 147}: "zte corporation", + [3]byte{0, 34, 148}: "Kyocera Corporation", + [3]byte{0, 34, 149}: "SGM Technology for lighting spa", + [3]byte{0, 34, 150}: "LinoWave Corporation", + [3]byte{0, 34, 151}: "XMOS Semiconductor", + [3]byte{0, 34, 152}: "Sony Mobile Communications AB", + [3]byte{0, 34, 153}: "SeaMicro Inc.", + [3]byte{0, 34, 154}: "Lastar, Inc.", + [3]byte{0, 34, 155}: "AverLogic Technologies, Inc.", + [3]byte{0, 34, 156}: "Verismo Networks Inc", + [3]byte{0, 34, 157}: "PYUNG-HWA IND.CO.,LTD", + [3]byte{0, 34, 158}: "Social Aid Research Co., Ltd.", + [3]byte{0, 34, 159}: "Sensys Traffic AB", + [3]byte{0, 34, 160}: "Delphi Corporation", + [3]byte{0, 34, 161}: "Huawei Symantec Technologies Co.,Ltd.", + [3]byte{0, 34, 162}: "Xtramus Technologies", + [3]byte{0, 34, 163}: "California Eastern Laboratories", + [3]byte{0, 34, 164}: "2Wire Inc", + [3]byte{0, 34, 165}: "Texas Instruments", + [3]byte{0, 34, 166}: "Sony Computer Entertainment America", + [3]byte{0, 34, 167}: "Tyco Electronics AMP GmbH", + [3]byte{0, 34, 168}: "Ouman Oy", + [3]byte{0, 34, 169}: "LG Electronics (Mobile Communications)", + [3]byte{0, 34, 170}: "Nintendo Co., Ltd.", + [3]byte{0, 34, 171}: "Shenzhen Turbosight Technology Ltd", + [3]byte{0, 34, 172}: "Hangzhou Siyuan Tech. Co., Ltd", + [3]byte{0, 34, 173}: "TELESIS TECHNOLOGIES, INC.", + [3]byte{0, 34, 174}: "Mattel Inc.", + [3]byte{0, 34, 175}: "Safety Vision", + [3]byte{0, 34, 176}: "D-Link Corporation", + [3]byte{0, 34, 177}: "Elbit Systems Ltd.", + [3]byte{0, 34, 178}: "4RF Communications Ltd", + [3]byte{0, 34, 179}: "Sei S.p.A.", + [3]byte{0, 34, 180}: "ARRIS Group, Inc.", + [3]byte{0, 34, 181}: "NOVITA", + [3]byte{0, 34, 182}: "Superflow Technologies Group", + [3]byte{0, 34, 183}: "GSS Grundig SAT-Systems GmbH", + [3]byte{0, 34, 184}: "Norcott", + [3]byte{0, 34, 185}: "Analogix Seminconductor, Inc", + [3]byte{0, 34, 186}: "HUTH Elektronik Systeme GmbH", + [3]byte{0, 34, 187}: "beyerdynamic GmbH & Co. KG", + [3]byte{0, 34, 188}: "JDSU France SAS", + [3]byte{0, 34, 189}: "Cisco Systems, Inc", + [3]byte{0, 34, 190}: "Cisco Systems, Inc", + [3]byte{0, 34, 191}: "SieAmp Group of Companies", + [3]byte{0, 34, 192}: "Shenzhen Forcelink Electronic Co, Ltd", + [3]byte{0, 34, 193}: "Active Storage Inc.", + [3]byte{0, 34, 194}: "Proview Eletrônica do Brasil LTDA", + [3]byte{0, 34, 195}: "Zeeport Technology Inc.", + [3]byte{0, 34, 196}: "epro GmbH", + [3]byte{0, 34, 197}: "INFORSON Co,Ltd.", + [3]byte{0, 34, 198}: "Sutus Inc", + [3]byte{0, 34, 199}: "SEGGER Microcontroller GmbH & Co. KG", + [3]byte{0, 34, 200}: "Applied Instruments B.V.", + [3]byte{0, 34, 201}: "Lenord, Bauer & Co GmbH", + [3]byte{0, 34, 202}: "Anviz Biometric Tech. Co., Ltd.", + [3]byte{0, 34, 203}: "IONODES Inc.", + [3]byte{0, 34, 204}: "SciLog, Inc.", + [3]byte{0, 34, 205}: "Ared Technology Co., Ltd.", + [3]byte{0, 34, 206}: "Cisco SPVTG", + [3]byte{0, 34, 207}: "PLANEX COMMUNICATIONS INC.", + [3]byte{0, 34, 208}: "Polar Electro Oy", + [3]byte{0, 34, 209}: "Albrecht Jung GmbH & Co. KG", + [3]byte{0, 34, 210}: "All Earth Comércio de Eletrônicos LTDA.", + [3]byte{0, 34, 211}: "Hub-Tech", + [3]byte{0, 34, 212}: "ComWorth Co., Ltd.", + [3]byte{0, 34, 213}: "Eaton Corp. Electrical Group Data Center Solutions - Pulizzi", + [3]byte{0, 34, 214}: "Cypak AB", + [3]byte{0, 34, 215}: "Nintendo Co., Ltd.", + [3]byte{0, 34, 216}: "Shenzhen GST Security and Safety Technology Limited", + [3]byte{0, 34, 217}: "Fortex Industrial Ltd.", + [3]byte{0, 34, 218}: "ANATEK, LLC", + [3]byte{0, 34, 219}: "Translogic Corporation", + [3]byte{0, 34, 220}: "Vigil Health Solutions Inc.", + [3]byte{0, 34, 221}: "Protecta Electronics Ltd", + [3]byte{0, 34, 222}: "OPPO Digital, Inc.", + [3]byte{0, 34, 223}: "TAMUZ Monitors", + [3]byte{0, 34, 224}: "Atlantic Software Technologies S.r.L.", + [3]byte{0, 34, 225}: "ZORT Labs, LLC.", + [3]byte{0, 34, 226}: "WABTEC Transit Division", + [3]byte{0, 34, 227}: "Amerigon", + [3]byte{0, 34, 228}: "APASS TECHNOLOGY CO., LTD.", + [3]byte{0, 34, 229}: "Fisher-Rosemount Systems Inc.", + [3]byte{0, 34, 230}: "Intelligent Data", + [3]byte{0, 34, 231}: "WPS Parking Systems", + [3]byte{0, 34, 232}: "Applition Co., Ltd.", + [3]byte{0, 34, 233}: "ProVision Communications", + [3]byte{0, 34, 234}: "Rustelcom Inc.", + [3]byte{0, 34, 235}: "Data Respons A/S", + [3]byte{0, 34, 236}: "IDEALBT TECHNOLOGY CORPORATION", + [3]byte{0, 34, 237}: "TSI Power Corporation", + [3]byte{0, 34, 238}: "Algo Communication Products Ltd", + [3]byte{0, 34, 239}: "iWDL Technologies", + [3]byte{0, 34, 240}: "3 Greens Aviation Limited", + [3]byte{0, 34, 241}: "Private", + [3]byte{0, 34, 242}: "SunPower Corp", + [3]byte{0, 34, 243}: "SHARP Corporation", + [3]byte{0, 34, 244}: "AMPAK Technology, Inc.", + [3]byte{0, 34, 245}: "Advanced Realtime Tracking GmbH", + [3]byte{0, 34, 246}: "Syracuse Research Corporation", + [3]byte{0, 34, 247}: "Conceptronic", + [3]byte{0, 34, 248}: "PIMA Electronic Systems Ltd.", + [3]byte{0, 34, 249}: "Pollin Electronic GmbH", + [3]byte{0, 34, 250}: "Intel Corporate", + [3]byte{0, 34, 251}: "Intel Corporate", + [3]byte{0, 34, 252}: "Nokia Danmark A/S", + [3]byte{0, 34, 253}: "Nokia Danmark A/S", + [3]byte{0, 34, 254}: "Advanced Illumination", + [3]byte{0, 34, 255}: "NIVIS LLC", + [3]byte{0, 35, 0}: "Cayee Computer Ltd.", + [3]byte{0, 35, 1}: "Witron Technology Limited", + [3]byte{0, 35, 2}: "Cobalt Digital, Inc.", + [3]byte{0, 35, 3}: "LITE-ON IT Corporation", + [3]byte{0, 35, 4}: "Cisco Systems, Inc", + [3]byte{0, 35, 5}: "Cisco Systems, Inc", + [3]byte{0, 35, 6}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 35, 7}: "FUTURE INNOVATION TECH CO.,LTD", + [3]byte{0, 35, 8}: "Arcadyan Technology Corporation", + [3]byte{0, 35, 9}: "Janam Technologies LLC", + [3]byte{0, 35, 10}: "ARBURG GmbH & Co KG", + [3]byte{0, 35, 11}: "ARRIS Group, Inc.", + [3]byte{0, 35, 12}: "CLOVER ELECTRONICS CO.,LTD.", + [3]byte{0, 35, 13}: "Nortel Networks", + [3]byte{0, 35, 14}: "Gorba AG", + [3]byte{0, 35, 15}: "Hirsch Electronics Corporation", + [3]byte{0, 35, 16}: "LNC Technology Co., Ltd.", + [3]byte{0, 35, 17}: "Gloscom Co., Ltd.", + [3]byte{0, 35, 18}: "Apple, Inc.", + [3]byte{0, 35, 19}: "Qool Technologies Ltd.", + [3]byte{0, 35, 20}: "Intel Corporate", + [3]byte{0, 35, 21}: "Intel Corporate", + [3]byte{0, 35, 22}: "KISAN ELECTRONICS CO", + [3]byte{0, 35, 23}: "Lasercraft Inc", + [3]byte{0, 35, 24}: "Toshiba", + [3]byte{0, 35, 25}: "Sielox LLC", + [3]byte{0, 35, 26}: "ITF Co., Ltd.", + [3]byte{0, 35, 27}: "Danaher Motion - Kollmorgen", + [3]byte{0, 35, 28}: "Fourier Systems Ltd.", + [3]byte{0, 35, 29}: "Deltacom Electronics Ltd", + [3]byte{0, 35, 30}: "Cezzer Multimedia Technologies", + [3]byte{0, 35, 31}: "Guangda Electronic & Telecommunication Technology Development Co., Ltd.", + [3]byte{0, 35, 32}: "Nicira Networks", + [3]byte{0, 35, 33}: "Avitech International Corp", + [3]byte{0, 35, 34}: "KISS Teknical Solutions, Inc.", + [3]byte{0, 35, 35}: "Zylin AS", + [3]byte{0, 35, 36}: "G-PRO COMPUTER", + [3]byte{0, 35, 37}: "IOLAN Holding", + [3]byte{0, 35, 38}: "FUJITSU LIMITED", + [3]byte{0, 35, 39}: "Shouyo Electronics CO., LTD", + [3]byte{0, 35, 40}: "ALCON TELECOMMUNICATIONS CO., LTD.", + [3]byte{0, 35, 41}: "DDRdrive LLC", + [3]byte{0, 35, 42}: "eonas IT-Beratung und -Entwicklung GmbH", + [3]byte{0, 35, 43}: "IRD A/S", + [3]byte{0, 35, 44}: "Senticare", + [3]byte{0, 35, 45}: "SandForce", + [3]byte{0, 35, 46}: "Kedah Electronics Engineering, LLC", + [3]byte{0, 35, 47}: "Advanced Card Systems Ltd.", + [3]byte{0, 35, 48}: "DIZIPIA, INC.", + [3]byte{0, 35, 49}: "Nintendo Co., Ltd.", + [3]byte{0, 35, 50}: "Apple, Inc.", + [3]byte{0, 35, 51}: "Cisco Systems, Inc", + [3]byte{0, 35, 52}: "Cisco Systems, Inc", + [3]byte{0, 35, 53}: "Linkflex Co.,Ltd", + [3]byte{0, 35, 54}: "METEL s.r.o.", + [3]byte{0, 35, 55}: "Global Star Solutions ULC", + [3]byte{0, 35, 56}: "OJ-Electronics A/S", + [3]byte{0, 35, 57}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 35, 58}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 35, 59}: "C-Matic Systems Ltd", + [3]byte{0, 35, 60}: "Alflex", + [3]byte{0, 35, 61}: "Novero holding B.V.", + [3]byte{0, 35, 62}: "Alcatel-Lucent IPD", + [3]byte{0, 35, 63}: "Purechoice Inc", + [3]byte{0, 35, 64}: "MiXTelematics", + [3]byte{0, 35, 65}: "Vanderbilt International (SWE) AB ", + [3]byte{0, 35, 66}: "Coffee Equipment Company", + [3]byte{0, 35, 67}: "TEM AG", + [3]byte{0, 35, 68}: "Objective Interface Systems, Inc.", + [3]byte{0, 35, 69}: "Sony Mobile Communications AB", + [3]byte{0, 35, 70}: "Vestac", + [3]byte{0, 35, 71}: "ProCurve Networking by HP", + [3]byte{0, 35, 72}: "Sagemcom Broadband SAS", + [3]byte{0, 35, 73}: "Helmholtz Centre Berlin for Material and Energy", + [3]byte{0, 35, 74}: "Private", + [3]byte{0, 35, 75}: "Inyuan Technology Inc.", + [3]byte{0, 35, 76}: "KTC AB", + [3]byte{0, 35, 77}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 35, 78}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 35, 79}: "Luminous Power Technologies Pvt. Ltd.", + [3]byte{0, 35, 80}: "LynTec", + [3]byte{0, 35, 81}: "2Wire Inc", + [3]byte{0, 35, 82}: "DATASENSOR S.p.A.", + [3]byte{0, 35, 83}: "F E T Elettronica snc", + [3]byte{0, 35, 84}: "ASUSTek COMPUTER INC.", + [3]byte{0, 35, 85}: "Kinco Automation(Shanghai) Ltd.", + [3]byte{0, 35, 86}: "Packet Forensics LLC", + [3]byte{0, 35, 87}: "Pitronot Technologies and Engineering P.T.E. Ltd.", + [3]byte{0, 35, 88}: "SYSTEL SA", + [3]byte{0, 35, 89}: "Benchmark Electronics ( Thailand ) Public Company Limited", + [3]byte{0, 35, 90}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{0, 35, 91}: "Gulfstream", + [3]byte{0, 35, 92}: "Aprius, Inc.", + [3]byte{0, 35, 93}: "Cisco Systems, Inc", + [3]byte{0, 35, 94}: "Cisco Systems, Inc", + [3]byte{0, 35, 95}: "Silicon Micro Sensors GmbH", + [3]byte{0, 35, 96}: "Lookit Technology Co., Ltd", + [3]byte{0, 35, 97}: "Unigen Corporation", + [3]byte{0, 35, 98}: "Goldline Controls", + [3]byte{0, 35, 99}: "Zhuhai Raysharp Technology Co.,Ltd", + [3]byte{0, 35, 100}: "Power Instruments Pte Ltd", + [3]byte{0, 35, 101}: "Insta Elektro GmbH", + [3]byte{0, 35, 102}: "Beijing Siasun Electronic System Co.,Ltd.", + [3]byte{0, 35, 103}: "UniControls a.s.", + [3]byte{0, 35, 104}: "Zebra Technologies Inc", + [3]byte{0, 35, 105}: "Cisco-Linksys, LLC", + [3]byte{0, 35, 106}: "SmartRG Inc", + [3]byte{0, 35, 107}: "Xembedded, Inc.", + [3]byte{0, 35, 108}: "Apple, Inc.", + [3]byte{0, 35, 109}: "ResMed Ltd", + [3]byte{0, 35, 110}: "Burster GmbH & Co KG", + [3]byte{0, 35, 111}: "DAQ System", + [3]byte{0, 35, 112}: "Snell", + [3]byte{0, 35, 113}: "SOAM Systel", + [3]byte{0, 35, 114}: "MORE STAR INDUSTRIAL GROUP LIMITED", + [3]byte{0, 35, 115}: "GridIron Systems, Inc.", + [3]byte{0, 35, 116}: "ARRIS Group, Inc.", + [3]byte{0, 35, 117}: "ARRIS Group, Inc.", + [3]byte{0, 35, 118}: "HTC Corporation", + [3]byte{0, 35, 119}: "Isotek Electronics Ltd", + [3]byte{0, 35, 120}: "GN Netcom A/S", + [3]byte{0, 35, 121}: "Union Business Machines Co. Ltd.", + [3]byte{0, 35, 122}: "RIM", + [3]byte{0, 35, 123}: "WHDI LLC", + [3]byte{0, 35, 124}: "NEOTION", + [3]byte{0, 35, 125}: "Hewlett Packard", + [3]byte{0, 35, 126}: "ELSTER GMBH", + [3]byte{0, 35, 127}: "PLANTRONICS, INC.", + [3]byte{0, 35, 128}: "Nanoteq", + [3]byte{0, 35, 129}: "Lengda Technology(Xiamen) Co.,Ltd.", + [3]byte{0, 35, 130}: "Lih Rong electronic Enterprise Co., Ltd.", + [3]byte{0, 35, 131}: "InMage Systems Inc", + [3]byte{0, 35, 132}: "GGH Engineering s.r.l.", + [3]byte{0, 35, 133}: "ANTIPODE", + [3]byte{0, 35, 134}: "Tour & Andersson AB", + [3]byte{0, 35, 135}: "ThinkFlood, Inc.", + [3]byte{0, 35, 136}: "V.T. Telematica S.p.a.", + [3]byte{0, 35, 137}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{0, 35, 138}: "Ciena Corporation", + [3]byte{0, 35, 139}: "QUANTA COMPUTER INC.", + [3]byte{0, 35, 140}: "Private", + [3]byte{0, 35, 141}: "Techno Design Co., Ltd.", + [3]byte{0, 35, 142}: "ADB Broadband Italia", + [3]byte{0, 35, 143}: "NIDEC COPAL CORPORATION", + [3]byte{0, 35, 144}: "Algolware Corporation", + [3]byte{0, 35, 145}: "Maxian", + [3]byte{0, 35, 146}: "Proteus Industries Inc.", + [3]byte{0, 35, 147}: "AJINEXTEK", + [3]byte{0, 35, 148}: "Samjeon", + [3]byte{0, 35, 149}: "ARRIS Group, Inc.", + [3]byte{0, 35, 150}: "ANDES TECHNOLOGY CORPORATION", + [3]byte{0, 35, 151}: "Westell Technologies Inc.", + [3]byte{0, 35, 152}: "Vutlan sro", + [3]byte{0, 35, 153}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 35, 154}: "EasyData Hardware GmbH", + [3]byte{0, 35, 155}: "Elster Solutions, LLC", + [3]byte{0, 35, 156}: "Juniper Networks", + [3]byte{0, 35, 157}: "Mapower Electronics Co., Ltd", + [3]byte{0, 35, 158}: "Jiangsu Lemote Technology Corporation Limited", + [3]byte{0, 35, 159}: "Institut für Prüftechnik", + [3]byte{0, 35, 160}: "Hana CNS Co., LTD.", + [3]byte{0, 35, 161}: "Trend Electronics Ltd", + [3]byte{0, 35, 162}: "ARRIS Group, Inc.", + [3]byte{0, 35, 163}: "ARRIS Group, Inc.", + [3]byte{0, 35, 164}: "New Concepts Development Corp.", + [3]byte{0, 35, 165}: "SageTV, LLC", + [3]byte{0, 35, 166}: "E-Mon", + [3]byte{0, 35, 167}: "Redpine Signals, Inc.", + [3]byte{0, 35, 168}: "Marshall Electronics", + [3]byte{0, 35, 169}: "Beijing Detianquan Electromechanical Equipment Co., Ltd", + [3]byte{0, 35, 170}: "HFR, Inc.", + [3]byte{0, 35, 171}: "Cisco Systems, Inc", + [3]byte{0, 35, 172}: "Cisco Systems, Inc", + [3]byte{0, 35, 173}: "Xmark Corporation", + [3]byte{0, 35, 174}: "Dell Inc.", + [3]byte{0, 35, 175}: "ARRIS Group, Inc.", + [3]byte{0, 35, 176}: "COMXION Technology Inc.", + [3]byte{0, 35, 177}: "Longcheer Technology (Singapore) Pte Ltd", + [3]byte{0, 35, 178}: "Intelligent Mechatronic Systems Inc", + [3]byte{0, 35, 179}: "Lyyn AB", + [3]byte{0, 35, 180}: "Nokia Danmark A/S", + [3]byte{0, 35, 181}: "ORTANA LTD", + [3]byte{0, 35, 182}: "SECURITE COMMUNICATIONS / HONEYWELL", + [3]byte{0, 35, 183}: "Q-Light Co., Ltd.", + [3]byte{0, 35, 184}: "Sichuan Jiuzhou Electronic Technology Co.,Ltd", + [3]byte{0, 35, 185}: "Airbus Defence and Space Deutschland GmbH", + [3]byte{0, 35, 186}: "Chroma", + [3]byte{0, 35, 187}: "Schmitt Industries", + [3]byte{0, 35, 188}: "EQ-SYS GmbH", + [3]byte{0, 35, 189}: "Digital Ally, Inc.", + [3]byte{0, 35, 190}: "Cisco SPVTG", + [3]byte{0, 35, 191}: "Mainpine, Inc.", + [3]byte{0, 35, 192}: "Broadway Networks", + [3]byte{0, 35, 193}: "Securitas Direct AB", + [3]byte{0, 35, 194}: "SAMSUNG Electronics. Co. LTD", + [3]byte{0, 35, 195}: "LogMeIn, Inc.", + [3]byte{0, 35, 196}: "Lux Lumen", + [3]byte{0, 35, 197}: "Radiation Safety and Control Services Inc", + [3]byte{0, 35, 198}: "SMC Corporation", + [3]byte{0, 35, 199}: "AVSystem", + [3]byte{0, 35, 200}: "TEAM-R", + [3]byte{0, 35, 201}: "Sichuan Tianyi Information Science & Technology Stock CO.,LTD", + [3]byte{0, 35, 202}: "Behind The Set, LLC", + [3]byte{0, 35, 203}: "Shenzhen Full-join Technology Co.,Ltd", + [3]byte{0, 35, 204}: "Nintendo Co., Ltd.", + [3]byte{0, 35, 205}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 35, 206}: "KITA DENSHI CORPORATION", + [3]byte{0, 35, 207}: "CUMMINS-ALLISON CORP.", + [3]byte{0, 35, 208}: "Uniloc USA Inc.", + [3]byte{0, 35, 209}: "TRG", + [3]byte{0, 35, 210}: "Inhand Electronics, Inc.", + [3]byte{0, 35, 211}: "AirLink WiFi Networking Corp.", + [3]byte{0, 35, 212}: "Texas Instruments", + [3]byte{0, 35, 213}: "WAREMA electronic GmbH", + [3]byte{0, 35, 214}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 35, 215}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 35, 216}: "Ball-It Oy", + [3]byte{0, 35, 217}: "Banner Engineering", + [3]byte{0, 35, 218}: "Industrial Computer Source (Deutschland)GmbH", + [3]byte{0, 35, 219}: "saxnet gmbh", + [3]byte{0, 35, 220}: "Benein, Inc", + [3]byte{0, 35, 221}: "ELGIN S.A.", + [3]byte{0, 35, 222}: "Ansync Inc.", + [3]byte{0, 35, 223}: "Apple, Inc.", + [3]byte{0, 35, 224}: "INO Therapeutics LLC", + [3]byte{0, 35, 225}: "Cavena Image Products AB", + [3]byte{0, 35, 226}: "SEA Signalisation", + [3]byte{0, 35, 227}: "Microtronic AG", + [3]byte{0, 35, 228}: "IPnect co. ltd.", + [3]byte{0, 35, 229}: "IPaXiom Networks", + [3]byte{0, 35, 230}: "Pirkus, Inc.", + [3]byte{0, 35, 231}: "Hinke A/S", + [3]byte{0, 35, 232}: "Demco Corp.", + [3]byte{0, 35, 233}: "F5 Networks, Inc.", + [3]byte{0, 35, 234}: "Cisco Systems, Inc", + [3]byte{0, 35, 235}: "Cisco Systems, Inc", + [3]byte{0, 35, 236}: "Algorithmix GmbH", + [3]byte{0, 35, 237}: "ARRIS Group, Inc.", + [3]byte{0, 35, 238}: "ARRIS Group, Inc.", + [3]byte{0, 35, 239}: "Zuend Systemtechnik AG", + [3]byte{0, 35, 240}: "Shanghai Jinghan Weighing Apparatus Co. Ltd.", + [3]byte{0, 35, 241}: "Sony Mobile Communications AB", + [3]byte{0, 35, 242}: "TVLogic", + [3]byte{0, 35, 243}: "Glocom, Inc.", + [3]byte{0, 35, 244}: "Masternaut", + [3]byte{0, 35, 245}: "WILO SE", + [3]byte{0, 35, 246}: "Softwell Technology Co., Ltd.", + [3]byte{0, 35, 247}: "Private", + [3]byte{0, 35, 248}: "ZyXEL Communications Corporation", + [3]byte{0, 35, 249}: "Double-Take Software, INC.", + [3]byte{0, 35, 250}: "RG Nets, Inc.", + [3]byte{0, 35, 251}: "IP Datatel, LLC.", + [3]byte{0, 35, 252}: "Ultra Stereo Labs, Inc", + [3]byte{0, 35, 253}: "AFT Atlas Fahrzeugtechnik GmbH", + [3]byte{0, 35, 254}: "Biodevices, SA", + [3]byte{0, 35, 255}: "Beijing HTTC Technology Ltd.", + [3]byte{0, 36, 0}: "Nortel Networks", + [3]byte{0, 36, 1}: "D-Link Corporation", + [3]byte{0, 36, 2}: "Op-Tection GmbH", + [3]byte{0, 36, 3}: "Nokia Danmark A/S", + [3]byte{0, 36, 4}: "Nokia Danmark A/S", + [3]byte{0, 36, 5}: "Dilog Nordic AB", + [3]byte{0, 36, 6}: "Pointmobile", + [3]byte{0, 36, 7}: "TELEM SAS", + [3]byte{0, 36, 8}: "Pacific Biosciences", + [3]byte{0, 36, 9}: "The Toro Company", + [3]byte{0, 36, 10}: "US Beverage Net", + [3]byte{0, 36, 11}: "Virtual Computer Inc.", + [3]byte{0, 36, 12}: "DELEC GmbH", + [3]byte{0, 36, 13}: "OnePath Networks LTD.", + [3]byte{0, 36, 14}: "Inventec Besta Co., Ltd.", + [3]byte{0, 36, 15}: "Ishii Tool & Engineering Corporation", + [3]byte{0, 36, 16}: "NUETEQ Technology,Inc.", + [3]byte{0, 36, 17}: "PharmaSmart LLC", + [3]byte{0, 36, 18}: "Benign Technologies Co, Ltd.", + [3]byte{0, 36, 19}: "Cisco Systems, Inc", + [3]byte{0, 36, 20}: "Cisco Systems, Inc", + [3]byte{0, 36, 21}: "Magnetic Autocontrol GmbH", + [3]byte{0, 36, 22}: "Any Use", + [3]byte{0, 36, 23}: "Thomson Telecom Belgium", + [3]byte{0, 36, 24}: "Nextwave Semiconductor", + [3]byte{0, 36, 25}: "Private", + [3]byte{0, 36, 26}: "Red Beetle Inc.", + [3]byte{0, 36, 27}: "iWOW Communications Pte Ltd", + [3]byte{0, 36, 28}: "FuGang Electronic (DG) Co.,Ltd", + [3]byte{0, 36, 29}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{0, 36, 30}: "Nintendo Co., Ltd.", + [3]byte{0, 36, 31}: "DCT-Delta GmbH", + [3]byte{0, 36, 32}: "NetUP Inc.", + [3]byte{0, 36, 33}: "MICRO-STAR INT'L CO., LTD.", + [3]byte{0, 36, 34}: "Knapp Logistik Automation GmbH", + [3]byte{0, 36, 35}: "AzureWave Technologies (Shanghai) Inc.", + [3]byte{0, 36, 36}: "Axis Network Technology", + [3]byte{0, 36, 37}: "Shenzhenshi chuangzhicheng Technology Co.,Ltd", + [3]byte{0, 36, 38}: "NOHMI BOSAI LTD.", + [3]byte{0, 36, 39}: "SSI COMPUTER CORP", + [3]byte{0, 36, 40}: "EnergyICT", + [3]byte{0, 36, 41}: "MK MASTER INC.", + [3]byte{0, 36, 42}: "Hittite Microwave Corporation", + [3]byte{0, 36, 43}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 36, 44}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 36, 46}: "Datastrip Inc.", + [3]byte{0, 36, 47}: "Micron", + [3]byte{0, 36, 48}: "Ruby Tech Corp.", + [3]byte{0, 36, 49}: "Uni-v co.,ltd", + [3]byte{0, 36, 50}: "Neostar Technology Co.,LTD", + [3]byte{0, 36, 51}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 36, 52}: "Lectrosonics, Inc.", + [3]byte{0, 36, 53}: "WIDE CORPORATION", + [3]byte{0, 36, 54}: "Apple, Inc.", + [3]byte{0, 36, 55}: "Motorola - BSG", + [3]byte{0, 36, 56}: "Brocade Communications Systems, Inc.", + [3]byte{0, 36, 57}: "Digital Barriers Advanced Technologies", + [3]byte{0, 36, 58}: "Ludl Electronic Products", + [3]byte{0, 36, 59}: "CSSI (S) Pte Ltd", + [3]byte{0, 36, 60}: "S.A.A.A.", + [3]byte{0, 36, 61}: "Emerson Appliance Motors and Controls", + [3]byte{0, 36, 63}: "Storwize, Inc.", + [3]byte{0, 36, 64}: "Halo Monitoring, Inc.", + [3]byte{0, 36, 65}: "Wanzl Metallwarenfabrik GmbH", + [3]byte{0, 36, 66}: "Axona Limited", + [3]byte{0, 36, 67}: "Nortel Networks", + [3]byte{0, 36, 68}: "Nintendo Co., Ltd.", + [3]byte{0, 36, 69}: "Adtran Inc", + [3]byte{0, 36, 70}: "MMB Research Inc.", + [3]byte{0, 36, 71}: "Kaztek Systems", + [3]byte{0, 36, 72}: "SpiderCloud Wireless, Inc", + [3]byte{0, 36, 73}: "Shen Zhen Lite Star Electronics Technology Co., Ltd", + [3]byte{0, 36, 74}: "Voyant International", + [3]byte{0, 36, 75}: "PERCEPTRON INC", + [3]byte{0, 36, 76}: "Solartron Metrology Ltd", + [3]byte{0, 36, 77}: "Hokkaido Electronics Corporation", + [3]byte{0, 36, 78}: "RadChips, Inc.", + [3]byte{0, 36, 79}: "Asantron Technologies Ltd.", + [3]byte{0, 36, 80}: "Cisco Systems, Inc", + [3]byte{0, 36, 81}: "Cisco Systems, Inc", + [3]byte{0, 36, 82}: "Silicon Software GmbH", + [3]byte{0, 36, 83}: "Initra d.o.o.", + [3]byte{0, 36, 84}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 36, 85}: "MuLogic BV", + [3]byte{0, 36, 86}: "2Wire Inc", + [3]byte{0, 36, 88}: "PA Bastion CC", + [3]byte{0, 36, 89}: "ABB Automation products GmbH", + [3]byte{0, 36, 90}: "Nanjing Panda Electronics Company Limited", + [3]byte{0, 36, 91}: "RAIDON TECHNOLOGY, INC.", + [3]byte{0, 36, 92}: "Design-Com Technologies Pty. Ltd.", + [3]byte{0, 36, 93}: "Terberg besturingstechniek B.V.", + [3]byte{0, 36, 94}: "Hivision Co.,ltd", + [3]byte{0, 36, 95}: "Vine Telecom CO.,Ltd.", + [3]byte{0, 36, 96}: "Giaval Science Development Co. Ltd.", + [3]byte{0, 36, 97}: "Shin Wang Tech.", + [3]byte{0, 36, 98}: "Rayzone Corporation", + [3]byte{0, 36, 99}: "Phybridge Inc", + [3]byte{0, 36, 100}: "Bridge Technologies Co AS", + [3]byte{0, 36, 101}: "Elentec", + [3]byte{0, 36, 102}: "Unitron nv", + [3]byte{0, 36, 103}: "AOC International (Europe) GmbH", + [3]byte{0, 36, 104}: "Sumavision Technologies Co.,Ltd", + [3]byte{0, 36, 105}: "Smart Doorphones", + [3]byte{0, 36, 106}: "Solid Year Co., Ltd.", + [3]byte{0, 36, 107}: "Covia, Inc.", + [3]byte{0, 36, 108}: "Aruba Networks", + [3]byte{0, 36, 109}: "Weinzierl Engineering GmbH", + [3]byte{0, 36, 110}: "Phihong USA Corp.", + [3]byte{0, 36, 111}: "Onda Communication spa", + [3]byte{0, 36, 112}: "AUROTECH ultrasound AS.", + [3]byte{0, 36, 113}: "Fusion MultiSystems dba Fusion-io", + [3]byte{0, 36, 114}: "ReDriven Power Inc.", + [3]byte{0, 36, 115}: "3COM EUROPE LTD", + [3]byte{0, 36, 116}: "Autronica Fire And Securirty", + [3]byte{0, 36, 117}: "Compass System(Embedded Dept.)", + [3]byte{0, 36, 118}: "TAP.tv", + [3]byte{0, 36, 119}: "Tibbo Technology", + [3]byte{0, 36, 120}: "Mag Tech Electronics Co Limited", + [3]byte{0, 36, 121}: "Optec Displays, Inc.", + [3]byte{0, 36, 122}: "FU YI CHENG Technology Co., Ltd.", + [3]byte{0, 36, 123}: "Actiontec Electronics, Inc", + [3]byte{0, 36, 124}: "Nokia Danmark A/S", + [3]byte{0, 36, 125}: "Nokia Danmark A/S", + [3]byte{0, 36, 126}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{0, 36, 127}: "Nortel Networks", + [3]byte{0, 36, 128}: "Meteocontrol GmbH", + [3]byte{0, 36, 129}: "Hewlett Packard", + [3]byte{0, 36, 130}: "Ruckus Wireless", + [3]byte{0, 36, 131}: "LG Electronics (Mobile Communications)", + [3]byte{0, 36, 132}: "Bang and Olufsen Medicom a/s", + [3]byte{0, 36, 133}: "ConteXtream Ltd", + [3]byte{0, 36, 134}: "DesignArt Networks", + [3]byte{0, 36, 135}: "Blackboard Inc.", + [3]byte{0, 36, 136}: "Centre For Development Of Telematics", + [3]byte{0, 36, 137}: "Vodafone Omnitel N.V.", + [3]byte{0, 36, 138}: "Kaga Electronics Co., Ltd.", + [3]byte{0, 36, 139}: "HYBUS CO., LTD.", + [3]byte{0, 36, 140}: "ASUSTek COMPUTER INC.", + [3]byte{0, 36, 141}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 36, 142}: "Infoware ZRt.", + [3]byte{0, 36, 143}: "DO-MONIX", + [3]byte{0, 36, 144}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 36, 145}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 36, 146}: "Motorola, Broadband Solutions Group", + [3]byte{0, 36, 147}: "ARRIS Group, Inc.", + [3]byte{0, 36, 148}: "Shenzhen Baoxin Tech CO., Ltd.", + [3]byte{0, 36, 149}: "ARRIS Group, Inc.", + [3]byte{0, 36, 150}: "Ginzinger electronic systems", + [3]byte{0, 36, 151}: "Cisco Systems, Inc", + [3]byte{0, 36, 152}: "Cisco Systems, Inc", + [3]byte{0, 36, 153}: "Aquila Technologies", + [3]byte{0, 36, 154}: "Beijing Zhongchuang Telecommunication Test Co., Ltd.", + [3]byte{0, 36, 155}: "Action Star Enterprise Co., Ltd.", + [3]byte{0, 36, 156}: "Bimeng Comunication System Co. Ltd", + [3]byte{0, 36, 157}: "NES Technology Inc.", + [3]byte{0, 36, 158}: "ADC-Elektronik GmbH", + [3]byte{0, 36, 159}: "RIM Testing Services", + [3]byte{0, 36, 160}: "ARRIS Group, Inc.", + [3]byte{0, 36, 161}: "ARRIS Group, Inc.", + [3]byte{0, 36, 162}: "Hong Kong Middleware Technology Limited", + [3]byte{0, 36, 163}: "Sonim Technologies Inc", + [3]byte{0, 36, 164}: "Siklu Communication", + [3]byte{0, 36, 165}: "BUFFALO.INC", + [3]byte{0, 36, 166}: "TELESTAR DIGITAL GmbH", + [3]byte{0, 36, 167}: "Advanced Video Communications Inc.", + [3]byte{0, 36, 168}: "ProCurve Networking by HP", + [3]byte{0, 36, 169}: "Ag Leader Technology", + [3]byte{0, 36, 170}: "Dycor Technologies Ltd.", + [3]byte{0, 36, 171}: "A7 Engineering, Inc.", + [3]byte{0, 36, 172}: "Hangzhou DPtech Technologies Co., Ltd.", + [3]byte{0, 36, 173}: "Adolf Thies Gmbh & Co. KG", + [3]byte{0, 36, 174}: "Morpho", + [3]byte{0, 36, 175}: "Echostar Technologies Corp", + [3]byte{0, 36, 176}: "ESAB AB", + [3]byte{0, 36, 177}: "Coulomb Technologies", + [3]byte{0, 36, 178}: "NETGEAR", + [3]byte{0, 36, 179}: "Graf-Syteco GmbH & Co. KG", + [3]byte{0, 36, 180}: "ESCATRONIC GmbH", + [3]byte{0, 36, 181}: "Nortel Networks", + [3]byte{0, 36, 182}: "Seagate Technology", + [3]byte{0, 36, 183}: "GridPoint, Inc.", + [3]byte{0, 36, 184}: "free alliance sdn bhd", + [3]byte{0, 36, 185}: "Wuhan Higheasy Electronic Technology Development Co.Ltd", + [3]byte{0, 36, 186}: "Texas Instruments", + [3]byte{0, 36, 187}: "CENTRAL Corporation", + [3]byte{0, 36, 188}: "HuRob Co.,Ltd", + [3]byte{0, 36, 189}: "Hainzl Industriesysteme GmbH", + [3]byte{0, 36, 190}: "Sony Corporation", + [3]byte{0, 36, 191}: "CIAT", + [3]byte{0, 36, 192}: "NTI COMODO INC", + [3]byte{0, 36, 193}: "ARRIS Group, Inc.", + [3]byte{0, 36, 194}: "Asumo Co.,Ltd.", + [3]byte{0, 36, 195}: "Cisco Systems, Inc", + [3]byte{0, 36, 196}: "Cisco Systems, Inc", + [3]byte{0, 36, 197}: "Meridian Audio Limited", + [3]byte{0, 36, 198}: "Hager Electro SAS", + [3]byte{0, 36, 199}: "Mobilarm Ltd", + [3]byte{0, 36, 200}: "Broadband Solutions Group", + [3]byte{0, 36, 201}: "Broadband Solutions Group", + [3]byte{0, 36, 202}: "Tobii Technology AB", + [3]byte{0, 36, 203}: "Autonet Mobile", + [3]byte{0, 36, 204}: "Fascinations Toys and Gifts, Inc.", + [3]byte{0, 36, 205}: "Willow Garage, Inc.", + [3]byte{0, 36, 206}: "Exeltech Inc", + [3]byte{0, 36, 207}: "Inscape Data Corporation", + [3]byte{0, 36, 208}: "Shenzhen SOGOOD Industry CO.,LTD.", + [3]byte{0, 36, 209}: "Thomson Inc.", + [3]byte{0, 36, 210}: "ASKEY COMPUTER CORP", + [3]byte{0, 36, 211}: "QUALICA Inc.", + [3]byte{0, 36, 212}: "FREEBOX SAS", + [3]byte{0, 36, 213}: "Winward Industrial Limited", + [3]byte{0, 36, 214}: "Intel Corporate", + [3]byte{0, 36, 215}: "Intel Corporate", + [3]byte{0, 36, 216}: "IlSung Precision", + [3]byte{0, 36, 217}: "BICOM, Inc.", + [3]byte{0, 36, 218}: "Innovar Systems Limited", + [3]byte{0, 36, 219}: "Alcohol Monitoring Systems", + [3]byte{0, 36, 220}: "Juniper Networks", + [3]byte{0, 36, 221}: "Centrak, Inc.", + [3]byte{0, 36, 222}: "GLOBAL Technology Inc.", + [3]byte{0, 36, 223}: "Digitalbox Europe GmbH", + [3]byte{0, 36, 224}: "DS Tech, LLC", + [3]byte{0, 36, 225}: "Convey Computer Corp.", + [3]byte{0, 36, 226}: "HASEGAWA ELECTRIC CO.,LTD.", + [3]byte{0, 36, 227}: "CAO Group", + [3]byte{0, 36, 228}: "Withings", + [3]byte{0, 36, 229}: "Seer Technology, Inc", + [3]byte{0, 36, 230}: "In Motion Technology Inc.", + [3]byte{0, 36, 231}: "Plaster Networks", + [3]byte{0, 36, 232}: "Dell Inc.", + [3]byte{0, 36, 233}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 36, 234}: "iris-GmbH infrared & intelligent sensors", + [3]byte{0, 36, 235}: "ClearPath Networks, Inc.", + [3]byte{0, 36, 236}: "United Information Technology Co.,Ltd.", + [3]byte{0, 36, 237}: "YT Elec. Co,.Ltd.", + [3]byte{0, 36, 238}: "Wynmax Inc.", + [3]byte{0, 36, 239}: "Sony Mobile Communications AB", + [3]byte{0, 36, 240}: "Seanodes", + [3]byte{0, 36, 241}: "Shenzhen Fanhai Sanjiang Electronics Co., Ltd.", + [3]byte{0, 36, 242}: "Uniphone Telecommunication Co., Ltd.", + [3]byte{0, 36, 243}: "Nintendo Co., Ltd.", + [3]byte{0, 36, 244}: "Kaminario, Ltd.", + [3]byte{0, 36, 245}: "NDS Surgical Imaging", + [3]byte{0, 36, 246}: "MIYOSHI ELECTRONICS CORPORATION", + [3]byte{0, 36, 247}: "Cisco Systems, Inc", + [3]byte{0, 36, 248}: "Technical Solutions Company Ltd.", + [3]byte{0, 36, 249}: "Cisco Systems, Inc", + [3]byte{0, 36, 250}: "Hilger u. Kern GMBH", + [3]byte{0, 36, 251}: "Private", + [3]byte{0, 36, 252}: "QuoPin Co., Ltd.", + [3]byte{0, 36, 253}: "Accedian Networks Inc", + [3]byte{0, 36, 254}: "AVM GmbH", + [3]byte{0, 36, 255}: "QLogic Corporation", + [3]byte{0, 37, 0}: "Apple, Inc.", + [3]byte{0, 37, 1}: "JSC Supertel", + [3]byte{0, 37, 2}: "NaturalPoint", + [3]byte{0, 37, 3}: "IBM Corp", + [3]byte{0, 37, 4}: "Valiant Communications Limited", + [3]byte{0, 37, 5}: "eks Engel GmbH & Co. KG", + [3]byte{0, 37, 6}: "A.I. ANTITACCHEGGIO ITALIA SRL", + [3]byte{0, 37, 7}: "ASTAK Inc.", + [3]byte{0, 37, 8}: "Maquet Cardiopulmonary AG", + [3]byte{0, 37, 9}: "SHARETRONIC Group LTD", + [3]byte{0, 37, 10}: "Security Expert Co. Ltd", + [3]byte{0, 37, 11}: "CENTROFACTOR INC", + [3]byte{0, 37, 12}: "Enertrac", + [3]byte{0, 37, 13}: "GZT Telkom-Telmor sp. z o.o.", + [3]byte{0, 37, 14}: "gt german telematics gmbh", + [3]byte{0, 37, 15}: "On-Ramp Wireless, Inc.", + [3]byte{0, 37, 16}: "Pico-Tesla Magnetic Therapies", + [3]byte{0, 37, 17}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{0, 37, 18}: "zte corporation", + [3]byte{0, 37, 19}: "CXP DIGITAL BV", + [3]byte{0, 37, 20}: "PC Worth Int'l Co., Ltd.", + [3]byte{0, 37, 21}: "SFR", + [3]byte{0, 37, 22}: "Integrated Design Tools, Inc.", + [3]byte{0, 37, 23}: "Venntis, LLC", + [3]byte{0, 37, 24}: "Power PLUS Communications AG", + [3]byte{0, 37, 25}: "Viaas Inc", + [3]byte{0, 37, 26}: "Psiber Data Systems Inc.", + [3]byte{0, 37, 27}: "Philips CareServant", + [3]byte{0, 37, 28}: "EDT", + [3]byte{0, 37, 29}: "DSA Encore, LLC", + [3]byte{0, 37, 30}: "ROTEL TECHNOLOGIES", + [3]byte{0, 37, 31}: "ZYNUS VISION INC.", + [3]byte{0, 37, 32}: "SMA Railway Technology GmbH", + [3]byte{0, 37, 33}: "Logitek Electronic Systems, Inc.", + [3]byte{0, 37, 34}: "ASRock Incorporation", + [3]byte{0, 37, 35}: "OCP Inc.", + [3]byte{0, 37, 36}: "Lightcomm Technology Co., Ltd", + [3]byte{0, 37, 37}: "CTERA Networks Ltd.", + [3]byte{0, 37, 38}: "Genuine Technologies Co., Ltd.", + [3]byte{0, 37, 39}: "Bitrode Corp.", + [3]byte{0, 37, 40}: "Daido Signal Co., Ltd.", + [3]byte{0, 37, 41}: "COMELIT GROUP S.P.A", + [3]byte{0, 37, 42}: "Chengdu GeeYa Technology Co.,LTD", + [3]byte{0, 37, 43}: "Stirling Energy Systems", + [3]byte{0, 37, 44}: "Entourage Systems, Inc.", + [3]byte{0, 37, 45}: "Kiryung Electronics", + [3]byte{0, 37, 46}: "Cisco SPVTG", + [3]byte{0, 37, 47}: "Energy, Inc.", + [3]byte{0, 37, 48}: "Aetas Systems Inc.", + [3]byte{0, 37, 49}: "Cloud Engines, Inc.", + [3]byte{0, 37, 50}: "Digital Recorders", + [3]byte{0, 37, 51}: "WITTENSTEIN AG", + [3]byte{0, 37, 53}: "Minimax GmbH & Co KG", + [3]byte{0, 37, 54}: "Oki Electric Industry Co., Ltd.", + [3]byte{0, 37, 55}: "Runcom Technologies Ltd.", + [3]byte{0, 37, 56}: "Samsung Electronics Co., Ltd., Memory Division", + [3]byte{0, 37, 57}: "IfTA GmbH", + [3]byte{0, 37, 58}: "CEVA, Ltd.", + [3]byte{0, 37, 59}: "din Dietmar Nocker Facilitymanagement GmbH", + [3]byte{0, 37, 60}: "2Wire Inc", + [3]byte{0, 37, 61}: "DRS Consolidated Controls", + [3]byte{0, 37, 62}: "Sensus Metering Systems", + [3]byte{0, 37, 64}: "Quasar Technologies, Inc.", + [3]byte{0, 37, 65}: "Maquet Critical Care AB", + [3]byte{0, 37, 66}: "Pittasoft", + [3]byte{0, 37, 67}: "MONEYTECH", + [3]byte{0, 37, 68}: "LoJack Corporation", + [3]byte{0, 37, 69}: "Cisco Systems, Inc", + [3]byte{0, 37, 70}: "Cisco Systems, Inc", + [3]byte{0, 37, 71}: "Nokia Danmark A/S", + [3]byte{0, 37, 72}: "Nokia Danmark A/S", + [3]byte{0, 37, 73}: "Jeorich Tech. Co.,Ltd.", + [3]byte{0, 37, 74}: "RingCube Technologies, Inc.", + [3]byte{0, 37, 75}: "Apple, Inc.", + [3]byte{0, 37, 76}: "Videon Central, Inc.", + [3]byte{0, 37, 77}: "Singapore Technologies Electronics Limited", + [3]byte{0, 37, 78}: "Vertex Wireless Co., Ltd.", + [3]byte{0, 37, 79}: "ELETTROLAB Srl", + [3]byte{0, 37, 80}: "Riverbed Technology, Inc.", + [3]byte{0, 37, 81}: "SE-Elektronic GmbH", + [3]byte{0, 37, 82}: "VXi Corporation", + [3]byte{0, 37, 83}: "ADB Broadband Italia", + [3]byte{0, 37, 84}: "Pixel8 Networks", + [3]byte{0, 37, 85}: "Visonic Technologies 1993 Ltd.", + [3]byte{0, 37, 86}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 37, 87}: "BlackBerry RTS", + [3]byte{0, 37, 88}: "MPEDIA", + [3]byte{0, 37, 89}: "Syphan Technologies Ltd", + [3]byte{0, 37, 90}: "Tantalus Systems Corp.", + [3]byte{0, 37, 91}: "CoachComm, LLC", + [3]byte{0, 37, 92}: "NEC Corporation", + [3]byte{0, 37, 93}: "Morningstar Corporation", + [3]byte{0, 37, 94}: "Shanghai Dare Technologies Co.,Ltd.", + [3]byte{0, 37, 95}: "SenTec AG", + [3]byte{0, 37, 96}: "Ibridge Networks & Communications Ltd.", + [3]byte{0, 37, 97}: "ProCurve Networking by HP", + [3]byte{0, 37, 98}: "interbro Co. Ltd.", + [3]byte{0, 37, 99}: "Luxtera Inc", + [3]byte{0, 37, 100}: "Dell Inc.", + [3]byte{0, 37, 101}: "Vizimax Inc.", + [3]byte{0, 37, 102}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 37, 103}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 37, 104}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 37, 105}: "Sagemcom Broadband SAS", + [3]byte{0, 37, 106}: "inIT - Institut Industrial IT", + [3]byte{0, 37, 107}: "ATENIX E.E. s.r.l.", + [3]byte{0, 37, 108}: "Azimut Production Association JSC", + [3]byte{0, 37, 109}: "Broadband Forum", + [3]byte{0, 37, 110}: "Van Breda B.V.", + [3]byte{0, 37, 111}: "Dantherm Power", + [3]byte{0, 37, 112}: "Eastern Communications Company Limited", + [3]byte{0, 37, 113}: "Zhejiang Tianle Digital Electric Co.,Ltd", + [3]byte{0, 37, 114}: "Nemo-Q International AB", + [3]byte{0, 37, 115}: "ST Electronics (Info-Security) Pte Ltd", + [3]byte{0, 37, 116}: "KUNIMI MEDIA DEVICE Co., Ltd.", + [3]byte{0, 37, 117}: "FiberPlex Technologies, LLC", + [3]byte{0, 37, 118}: "NELI TECHNOLOGIES", + [3]byte{0, 37, 119}: "D-BOX Technologies", + [3]byte{0, 37, 120}: "JSC Concern Sozvezdie", + [3]byte{0, 37, 121}: "J & F Labs", + [3]byte{0, 37, 122}: "CAMCO Produktions- und Vertriebs-GmbH für Beschallungs- und Beleuchtungsanlagen", + [3]byte{0, 37, 123}: "STJ ELECTRONICS PVT LTD", + [3]byte{0, 37, 124}: "Huachentel Technology Development Co., Ltd", + [3]byte{0, 37, 125}: "PointRed Telecom Private Ltd.", + [3]byte{0, 37, 126}: "NEW POS Technology Limited", + [3]byte{0, 37, 127}: "CallTechSolution Co.,Ltd", + [3]byte{0, 37, 128}: "Equipson S.A.", + [3]byte{0, 37, 129}: "x-star networks Inc.", + [3]byte{0, 37, 130}: "Maksat Technologies (P) Ltd", + [3]byte{0, 37, 131}: "Cisco Systems, Inc", + [3]byte{0, 37, 132}: "Cisco Systems, Inc", + [3]byte{0, 37, 133}: "KOKUYO S&T Co., Ltd.", + [3]byte{0, 37, 134}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 37, 135}: "Vitality, Inc.", + [3]byte{0, 37, 136}: "Genie Industries, Inc.", + [3]byte{0, 37, 137}: "Hills Industries Limited", + [3]byte{0, 37, 138}: "Pole/Zero Corporation", + [3]byte{0, 37, 139}: "Mellanox Technologies, Inc.", + [3]byte{0, 37, 140}: "ESUS ELEKTRONIK SAN. VE DIS. TIC. LTD. STI.", + [3]byte{0, 37, 141}: "Haier", + [3]byte{0, 37, 142}: "The Weather Channel", + [3]byte{0, 37, 143}: "Trident Microsystems, Inc.", + [3]byte{0, 37, 144}: "Super Micro Computer, Inc.", + [3]byte{0, 37, 145}: "NEXTEK, Inc.", + [3]byte{0, 37, 146}: "Guangzhou Shirui Electronic Co., Ltd", + [3]byte{0, 37, 147}: "DatNet Informatikai Kft.", + [3]byte{0, 37, 148}: "Eurodesign BG LTD", + [3]byte{0, 37, 149}: "Northwest Signal Supply, Inc", + [3]byte{0, 37, 150}: "GIGAVISION srl", + [3]byte{0, 37, 151}: "Kalki Communication Technologies", + [3]byte{0, 37, 152}: "Zhong Shan City Litai Electronic Industrial Co. Ltd", + [3]byte{0, 37, 153}: "Hedon e.d. B.V.", + [3]byte{0, 37, 154}: "CEStronics GmbH", + [3]byte{0, 37, 155}: "Beijing PKUNITY Microsystems Technology Co., Ltd", + [3]byte{0, 37, 156}: "Cisco-Linksys, LLC", + [3]byte{0, 37, 157}: "Private", + [3]byte{0, 37, 158}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 37, 159}: "TechnoDigital Technologies GmbH", + [3]byte{0, 37, 160}: "Nintendo Co., Ltd.", + [3]byte{0, 37, 161}: "Enalasys", + [3]byte{0, 37, 162}: "Alta Definicion LINCEO S.L.", + [3]byte{0, 37, 163}: "Trimax Wireless, Inc.", + [3]byte{0, 37, 164}: "EuroDesign embedded technologies GmbH", + [3]byte{0, 37, 165}: "Walnut Media Network", + [3]byte{0, 37, 166}: "Central Network Solution Co., Ltd.", + [3]byte{0, 37, 167}: "Comverge, Inc.", + [3]byte{0, 37, 168}: "Kontron (BeiJing) Technology Co.,Ltd", + [3]byte{0, 37, 169}: "Shanghai Embedway Information Technologies Co.,Ltd", + [3]byte{0, 37, 170}: "Beijing Soul Technology Co.,Ltd.", + [3]byte{0, 37, 171}: "AIO LCD PC BU / TPV", + [3]byte{0, 37, 172}: "I-Tech corporation", + [3]byte{0, 37, 173}: "Manufacturing Resources International", + [3]byte{0, 37, 174}: "Microsoft Corporation", + [3]byte{0, 37, 175}: "COMFILE Technology", + [3]byte{0, 37, 176}: "Schmartz Inc", + [3]byte{0, 37, 177}: "Maya-Creation Corporation", + [3]byte{0, 37, 178}: "MBDA Deutschland GmbH", + [3]byte{0, 37, 179}: "Hewlett Packard", + [3]byte{0, 37, 180}: "Cisco Systems, Inc", + [3]byte{0, 37, 181}: "Cisco Systems, Inc", + [3]byte{0, 37, 182}: "Telecom FM", + [3]byte{0, 37, 183}: "Costar electronics, inc.,", + [3]byte{0, 37, 184}: "Agile Communications, Inc.", + [3]byte{0, 37, 185}: "Cypress Solutions Inc", + [3]byte{0, 37, 186}: "Alcatel-Lucent IPD", + [3]byte{0, 37, 187}: "INNERINT Co., Ltd.", + [3]byte{0, 37, 188}: "Apple, Inc.", + [3]byte{0, 37, 189}: "Italdata Ingegneria dell'Idea S.p.A.", + [3]byte{0, 37, 190}: "Tektrap Systems Inc.", + [3]byte{0, 37, 191}: "Wireless Cables Inc.", + [3]byte{0, 37, 192}: "ZillionTV Corporation", + [3]byte{0, 37, 193}: "Nawoo Korea Corp.", + [3]byte{0, 37, 194}: "RingBell Co.,Ltd.", + [3]byte{0, 37, 195}: "21168", + [3]byte{0, 37, 196}: "Ruckus Wireless", + [3]byte{0, 37, 197}: "Star Link Communication Pvt. Ltd.", + [3]byte{0, 37, 198}: "kasercorp, ltd", + [3]byte{0, 37, 199}: "altek Corporation", + [3]byte{0, 37, 200}: "S-Access GmbH", + [3]byte{0, 37, 201}: "SHENZHEN HUAPU DIGITAL CO., LTD", + [3]byte{0, 37, 202}: "LS Research, LLC", + [3]byte{0, 37, 203}: "Reiner SCT", + [3]byte{0, 37, 204}: "Mobile Communications Korea Incorporated", + [3]byte{0, 37, 205}: "Skylane Optics", + [3]byte{0, 37, 206}: "InnerSpace", + [3]byte{0, 37, 207}: "Nokia Danmark A/S", + [3]byte{0, 37, 208}: "Nokia Danmark A/S", + [3]byte{0, 37, 209}: "Eastern Asia Technology Limited", + [3]byte{0, 37, 210}: "InpegVision Co., Ltd", + [3]byte{0, 37, 211}: "AzureWave Technology Inc.", + [3]byte{0, 37, 212}: "General Dynamics Mission Systems", + [3]byte{0, 37, 213}: "Robonica (Pty) Ltd", + [3]byte{0, 37, 214}: "The Kroger Co.", + [3]byte{0, 37, 215}: "CEDO", + [3]byte{0, 37, 216}: "KOREA MAINTENANCE", + [3]byte{0, 37, 217}: "DataFab Systems Inc.", + [3]byte{0, 37, 218}: "Secura Key", + [3]byte{0, 37, 219}: "ATI Electronics(Shenzhen) Co., LTD", + [3]byte{0, 37, 220}: "Sumitomo Electric Industries,Ltd", + [3]byte{0, 37, 221}: "SUNNYTEK INFORMATION CO., LTD.", + [3]byte{0, 37, 222}: "Probits Co., LTD.", + [3]byte{0, 37, 223}: "Private", + [3]byte{0, 37, 224}: "CeedTec Sdn Bhd", + [3]byte{0, 37, 225}: "SHANGHAI SEEYOO ELECTRONIC & TECHNOLOGY CO., LTD", + [3]byte{0, 37, 226}: "Everspring Industry Co., Ltd.", + [3]byte{0, 37, 227}: "Hanshinit Inc.", + [3]byte{0, 37, 228}: "OMNI-WiFi, LLC", + [3]byte{0, 37, 229}: "LG Electronics (Mobile Communications)", + [3]byte{0, 37, 230}: "Belgian Monitoring Systems bvba", + [3]byte{0, 37, 231}: "Sony Mobile Communications AB", + [3]byte{0, 37, 232}: "Idaho Technology", + [3]byte{0, 37, 233}: "i-mate Development, Inc.", + [3]byte{0, 37, 234}: "Iphion BV", + [3]byte{0, 37, 235}: "Reutech Radar Systems (PTY) Ltd", + [3]byte{0, 37, 236}: "Humanware", + [3]byte{0, 37, 237}: "NuVo Technologies LLC", + [3]byte{0, 37, 238}: "Avtex Ltd", + [3]byte{0, 37, 239}: "I-TEC Co., Ltd.", + [3]byte{0, 37, 240}: "Suga Electronics Limited", + [3]byte{0, 37, 241}: "ARRIS Group, Inc.", + [3]byte{0, 37, 242}: "ARRIS Group, Inc.", + [3]byte{0, 37, 243}: "Nordwestdeutsche Zählerrevision", + [3]byte{0, 37, 244}: "KoCo Connector AG", + [3]byte{0, 37, 245}: "DVS Korea, Co., Ltd", + [3]byte{0, 37, 246}: "netTALK.com, Inc.", + [3]byte{0, 37, 247}: "Ansaldo STS USA", + [3]byte{0, 37, 249}: "GMK electronic design GmbH", + [3]byte{0, 37, 250}: "J&M Analytik AG", + [3]byte{0, 37, 251}: "Tunstall Healthcare A/S", + [3]byte{0, 37, 252}: "ENDA ENDUSTRIYEL ELEKTRONIK LTD. STI.", + [3]byte{0, 37, 253}: "OBR Centrum Techniki Morskiej S.A.", + [3]byte{0, 37, 254}: "Pilot Electronics Corporation", + [3]byte{0, 37, 255}: "CreNova Multimedia Co., Ltd", + [3]byte{0, 38, 0}: "TEAC Australia Pty Ltd.", + [3]byte{0, 38, 1}: "Cutera Inc", + [3]byte{0, 38, 2}: "SMART Temps LLC", + [3]byte{0, 38, 3}: "Shenzhen Wistar Technology Co., Ltd", + [3]byte{0, 38, 4}: "Audio Processing Technology Ltd", + [3]byte{0, 38, 5}: "CC Systems AB", + [3]byte{0, 38, 6}: "RAUMFELD GmbH", + [3]byte{0, 38, 7}: "Enabling Technology Pty Ltd", + [3]byte{0, 38, 8}: "Apple, Inc.", + [3]byte{0, 38, 9}: "Phyllis Co., Ltd.", + [3]byte{0, 38, 10}: "Cisco Systems, Inc", + [3]byte{0, 38, 11}: "Cisco Systems, Inc", + [3]byte{0, 38, 12}: "Dataram", + [3]byte{0, 38, 13}: "Mercury Systems, Inc.", + [3]byte{0, 38, 14}: "Ablaze Systems, LLC", + [3]byte{0, 38, 15}: "Linn Products Ltd", + [3]byte{0, 38, 16}: "Apacewave Technologies", + [3]byte{0, 38, 17}: "Licera AB", + [3]byte{0, 38, 18}: "Space Exploration Technologies", + [3]byte{0, 38, 19}: "Engel Axil S.L.", + [3]byte{0, 38, 20}: "KTNF", + [3]byte{0, 38, 21}: "Teracom Limited", + [3]byte{0, 38, 22}: "Rosemount Inc.", + [3]byte{0, 38, 23}: "OEM Worldwide", + [3]byte{0, 38, 24}: "ASUSTek COMPUTER INC.", + [3]byte{0, 38, 25}: "FRC", + [3]byte{0, 38, 26}: "Femtocomm System Technology Corp.", + [3]byte{0, 38, 27}: "LAUREL BANK MACHINES CO., LTD.", + [3]byte{0, 38, 28}: "NEOVIA INC.", + [3]byte{0, 38, 29}: "COP SECURITY SYSTEM CORP.", + [3]byte{0, 38, 30}: "QINGBANG ELEC(SZ) CO., LTD", + [3]byte{0, 38, 31}: "SAE Magnetics (H.K.) Ltd.", + [3]byte{0, 38, 32}: "ISGUS GmbH", + [3]byte{0, 38, 33}: "InteliCloud Technology Inc.", + [3]byte{0, 38, 34}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{0, 38, 35}: "JRD Communication Inc", + [3]byte{0, 38, 36}: "Thomson Inc.", + [3]byte{0, 38, 37}: "MediaSputnik", + [3]byte{0, 38, 38}: "Geophysical Survey Systems, Inc.", + [3]byte{0, 38, 39}: "Truesell", + [3]byte{0, 38, 40}: "companytec automação e controle ltda.", + [3]byte{0, 38, 41}: "Juphoon System Software Inc.", + [3]byte{0, 38, 42}: "Proxense, LLC", + [3]byte{0, 38, 43}: "Wongs Electronics Co. Ltd.", + [3]byte{0, 38, 44}: "IKT Advanced Technologies s.r.o.", + [3]byte{0, 38, 45}: "Wistron Corporation", + [3]byte{0, 38, 46}: "Chengdu Jiuzhou Electronic Technology Inc", + [3]byte{0, 38, 47}: "HAMAMATSU TOA ELECTRONICS", + [3]byte{0, 38, 48}: "ACOREL S.A.S", + [3]byte{0, 38, 49}: "COMMTACT LTD", + [3]byte{0, 38, 50}: "Instrumentation Technologies d.d.", + [3]byte{0, 38, 51}: "MIR - Medical International Research", + [3]byte{0, 38, 52}: "Infineta Systems, Inc", + [3]byte{0, 38, 53}: "Bluetechnix GmbH", + [3]byte{0, 38, 54}: "ARRIS Group, Inc.", + [3]byte{0, 38, 55}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{0, 38, 56}: "Xia Men Joyatech Co., Ltd.", + [3]byte{0, 38, 57}: "T.M. Electronics, Inc.", + [3]byte{0, 38, 58}: "Digitec Systems", + [3]byte{0, 38, 59}: "Onbnetech", + [3]byte{0, 38, 60}: "Bachmann Technology GmbH & Co. KG", + [3]byte{0, 38, 61}: "MIA Corporation", + [3]byte{0, 38, 62}: "Trapeze Networks", + [3]byte{0, 38, 63}: "LIOS Technology GmbH", + [3]byte{0, 38, 64}: "Baustem Broadband Technologies, Ltd.", + [3]byte{0, 38, 65}: "ARRIS Group, Inc.", + [3]byte{0, 38, 66}: "ARRIS Group, Inc.", + [3]byte{0, 38, 67}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{0, 38, 68}: "Thomson Telecom Belgium", + [3]byte{0, 38, 69}: "Circontrol S.A.", + [3]byte{0, 38, 70}: "SHENYANG TONGFANG MULTIMEDIA TECHNOLOGY COMPANY LIMITED", + [3]byte{0, 38, 71}: "WFE TECHNOLOGY CORP.", + [3]byte{0, 38, 72}: "Emitech Corp.", + [3]byte{0, 38, 74}: "Apple, Inc.", + [3]byte{0, 38, 76}: "Shanghai DigiVision Technology Co., Ltd.", + [3]byte{0, 38, 77}: "Arcadyan Technology Corporation", + [3]byte{0, 38, 78}: "Rail & Road Protec GmbH", + [3]byte{0, 38, 79}: "Krüger &Gothe GmbH", + [3]byte{0, 38, 80}: "2Wire Inc", + [3]byte{0, 38, 81}: "Cisco Systems, Inc", + [3]byte{0, 38, 82}: "Cisco Systems, Inc", + [3]byte{0, 38, 83}: "DaySequerra Corporation", + [3]byte{0, 38, 84}: "3Com Corporation", + [3]byte{0, 38, 85}: "Hewlett Packard", + [3]byte{0, 38, 86}: "Sansonic Electronics USA", + [3]byte{0, 38, 87}: "OOO NPP EKRA", + [3]byte{0, 38, 88}: "T-Platforms (Cyprus) Limited", + [3]byte{0, 38, 89}: "Nintendo Co., Ltd.", + [3]byte{0, 38, 90}: "D-Link Corporation", + [3]byte{0, 38, 91}: "Hitron Technologies. Inc", + [3]byte{0, 38, 92}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 38, 93}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 38, 94}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 38, 95}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 38, 96}: "Logiways", + [3]byte{0, 38, 97}: "Irumtek Co., Ltd.", + [3]byte{0, 38, 98}: "Actiontec Electronics, Inc", + [3]byte{0, 38, 99}: "Shenzhen Huitaiwei Tech. Ltd, co.", + [3]byte{0, 38, 100}: "Core System Japan", + [3]byte{0, 38, 101}: "ProtectedLogic Corporation", + [3]byte{0, 38, 102}: "EFM Networks", + [3]byte{0, 38, 103}: "CARECOM CO.,LTD.", + [3]byte{0, 38, 104}: "Nokia Danmark A/S", + [3]byte{0, 38, 105}: "Nokia Danmark A/S", + [3]byte{0, 38, 106}: "ESSENSIUM NV", + [3]byte{0, 38, 107}: "SHINE UNION ENTERPRISE LIMITED", + [3]byte{0, 38, 108}: "INVENTEC Corporation", + [3]byte{0, 38, 109}: "MobileAccess Networks", + [3]byte{0, 38, 110}: "Nissho-denki Co.,LTD.", + [3]byte{0, 38, 111}: "Coordiwise Technology Corp.", + [3]byte{0, 38, 112}: "Cinch Connectors", + [3]byte{0, 38, 113}: "AUTOVISION Co., Ltd", + [3]byte{0, 38, 114}: "AAMP of America", + [3]byte{0, 38, 115}: "RICOH COMPANY,LTD.", + [3]byte{0, 38, 116}: "Electronic Solutions, Inc.", + [3]byte{0, 38, 117}: "Aztech Electronics Pte Ltd", + [3]byte{0, 38, 118}: "COMMidt AS", + [3]byte{0, 38, 119}: "DEIF A/S", + [3]byte{0, 38, 120}: "Logic Instrument SA", + [3]byte{0, 38, 121}: "Euphonic Technologies, Inc.", + [3]byte{0, 38, 122}: "wuhan hongxin telecommunication technologies co.,ltd", + [3]byte{0, 38, 123}: "GSI Helmholtzzentrum für Schwerionenforschung GmbH", + [3]byte{0, 38, 124}: "Metz-Werke GmbH & Co KG", + [3]byte{0, 38, 125}: "A-Max Technology Macao Commercial Offshore Company Limited", + [3]byte{0, 38, 126}: "PARROT SA", + [3]byte{0, 38, 127}: "Zenterio AB", + [3]byte{0, 38, 128}: "SIL3 Pty.Ltd", + [3]byte{0, 38, 129}: "Interspiro AB", + [3]byte{0, 38, 130}: "Gemtek Technology Co., Ltd.", + [3]byte{0, 38, 131}: "Ajoho Enterprise Co., Ltd.", + [3]byte{0, 38, 132}: "KISAN SYSTEM", + [3]byte{0, 38, 133}: "Digital Innovation", + [3]byte{0, 38, 134}: "Quantenna Communcations, Inc.", + [3]byte{0, 38, 135}: "corega K.K", + [3]byte{0, 38, 136}: "Juniper Networks", + [3]byte{0, 38, 137}: "General Dynamics Robotic Systems", + [3]byte{0, 38, 138}: "Terrier SC Ltd", + [3]byte{0, 38, 139}: "Guangzhou Escene Computer Technology Limited", + [3]byte{0, 38, 140}: "StarLeaf Ltd.", + [3]byte{0, 38, 141}: "CellTel S.p.A.", + [3]byte{0, 38, 142}: "Alta Solutions, Inc.", + [3]byte{0, 38, 143}: "MTA SpA", + [3]byte{0, 38, 144}: "I DO IT", + [3]byte{0, 38, 145}: "Sagemcom Broadband SAS", + [3]byte{0, 38, 146}: "Mitsubishi Electric Co.", + [3]byte{0, 38, 147}: "QVidium Technologies, Inc.", + [3]byte{0, 38, 148}: "Senscient Ltd", + [3]byte{0, 38, 149}: "ZT Group Int'l Inc", + [3]byte{0, 38, 150}: "NOOLIX Co., Ltd", + [3]byte{0, 38, 151}: "Alpha Technologies Inc.", + [3]byte{0, 38, 152}: "Cisco Systems, Inc", + [3]byte{0, 38, 153}: "Cisco Systems, Inc", + [3]byte{0, 38, 154}: "Carina System Co., Ltd.", + [3]byte{0, 38, 155}: "SOKRAT Ltd.", + [3]byte{0, 38, 156}: "ITUS JAPAN CO. LTD", + [3]byte{0, 38, 157}: "M2Mnet Co., Ltd.", + [3]byte{0, 38, 158}: "QUANTA COMPUTER INC.", + [3]byte{0, 38, 159}: "Private", + [3]byte{0, 38, 160}: "moblic", + [3]byte{0, 38, 161}: "Megger", + [3]byte{0, 38, 162}: "Instrumentation Technology Systems", + [3]byte{0, 38, 163}: "FQ Ingenieria Electronica S.A.", + [3]byte{0, 38, 164}: "Novus Produtos Eletronicos Ltda", + [3]byte{0, 38, 165}: "MICROROBOT.CO.,LTD", + [3]byte{0, 38, 166}: "TRIXELL", + [3]byte{0, 38, 167}: "CONNECT SRL", + [3]byte{0, 38, 168}: "DAEHAP HYPER-TECH", + [3]byte{0, 38, 169}: "Strong Technologies Pty Ltd", + [3]byte{0, 38, 170}: "Kenmec Mechanical Engineering Co., Ltd.", + [3]byte{0, 38, 171}: "Seiko Epson Corporation", + [3]byte{0, 38, 172}: "Shanghai LUSTER Teraband photonic Co., Ltd.", + [3]byte{0, 38, 173}: "Arada Systems, Inc.", + [3]byte{0, 38, 174}: "Wireless Measurement Ltd", + [3]byte{0, 38, 175}: "Duelco A/S", + [3]byte{0, 38, 176}: "Apple, Inc.", + [3]byte{0, 38, 177}: "Navis Auto Motive Systems, Inc.", + [3]byte{0, 38, 178}: "Setrix GmbH", + [3]byte{0, 38, 179}: "Thales Communications Inc", + [3]byte{0, 38, 180}: "Ford Motor Company", + [3]byte{0, 38, 181}: "ICOMM Tele Ltd", + [3]byte{0, 38, 182}: "ASKEY COMPUTER CORP", + [3]byte{0, 38, 183}: "Kingston Technology Company, Inc.", + [3]byte{0, 38, 184}: "Actiontec Electronics, Inc", + [3]byte{0, 38, 185}: "Dell Inc.", + [3]byte{0, 38, 186}: "ARRIS Group, Inc.", + [3]byte{0, 38, 187}: "Apple, Inc.", + [3]byte{0, 38, 188}: "General Jack Technology Ltd.", + [3]byte{0, 38, 189}: "JTEC Card & Communication Co., Ltd.", + [3]byte{0, 38, 190}: "Schoonderbeek Elektronica Systemen B.V.", + [3]byte{0, 38, 191}: "ShenZhen Temobi Science&Tech Development Co.,Ltd", + [3]byte{0, 38, 192}: "EnergyHub", + [3]byte{0, 38, 193}: "ARTRAY CO., LTD.", + [3]byte{0, 38, 194}: "SCDI Co. LTD", + [3]byte{0, 38, 195}: "Insightek Corp.", + [3]byte{0, 38, 196}: "Cadmos microsystems S.r.l.", + [3]byte{0, 38, 197}: "Guangdong Gosun Telecommunications Co.,Ltd", + [3]byte{0, 38, 198}: "Intel Corporate", + [3]byte{0, 38, 199}: "Intel Corporate", + [3]byte{0, 38, 200}: "System Sensor", + [3]byte{0, 38, 201}: "Proventix Systems, Inc.", + [3]byte{0, 38, 202}: "Cisco Systems, Inc", + [3]byte{0, 38, 203}: "Cisco Systems, Inc", + [3]byte{0, 38, 204}: "Nokia Danmark A/S", + [3]byte{0, 38, 205}: "PurpleComm, Inc.", + [3]byte{0, 38, 206}: "Kozumi USA Corp.", + [3]byte{0, 38, 207}: "DEKA R&D", + [3]byte{0, 38, 208}: "Semihalf", + [3]byte{0, 38, 209}: "S Squared Innovations Inc.", + [3]byte{0, 38, 210}: "Pcube Systems, Inc.", + [3]byte{0, 38, 211}: "Zeno Information System", + [3]byte{0, 38, 212}: "IRCA SpA", + [3]byte{0, 38, 213}: "Ory Solucoes em Comercio de Informatica Ltda.", + [3]byte{0, 38, 214}: "Ningbo Andy Optoelectronic Co., Ltd.", + [3]byte{0, 38, 215}: "KM Electornic Technology Co., Ltd.", + [3]byte{0, 38, 216}: "Magic Point Inc.", + [3]byte{0, 38, 217}: "ARRIS Group, Inc.", + [3]byte{0, 38, 218}: "Universal Media Corporation /Slovakia/ s.r.o.", + [3]byte{0, 38, 219}: "Ionics EMS Inc.", + [3]byte{0, 38, 220}: "Optical Systems Design", + [3]byte{0, 38, 221}: "Fival Science & Technology Co.,Ltd.", + [3]byte{0, 38, 222}: "FDI MATELEC", + [3]byte{0, 38, 223}: "TaiDoc Technology Corp.", + [3]byte{0, 38, 224}: "ASITEQ", + [3]byte{0, 38, 225}: "Stanford University, OpenFlow Group", + [3]byte{0, 38, 226}: "LG Electronics (Mobile Communications)", + [3]byte{0, 38, 227}: "DTI", + [3]byte{0, 38, 228}: "Canal +", + [3]byte{0, 38, 229}: "AEG Power Solutions", + [3]byte{0, 38, 230}: "Visionhitech Co., Ltd.", + [3]byte{0, 38, 231}: "Shanghai ONLAN Communication Tech. Co., Ltd.", + [3]byte{0, 38, 232}: "Murata Manufacturing Co., Ltd.", + [3]byte{0, 38, 233}: "SP Corp", + [3]byte{0, 38, 234}: "Cheerchip Electronic Technology (ShangHai) Co., Ltd.", + [3]byte{0, 38, 235}: "Advanced Spectrum Technology Co., Ltd.", + [3]byte{0, 38, 236}: "Legrand Home Systems, Inc", + [3]byte{0, 38, 237}: "zte corporation", + [3]byte{0, 38, 238}: "TKM GmbH", + [3]byte{0, 38, 239}: "Technology Advancement Group, Inc.", + [3]byte{0, 38, 240}: "cTrixs International GmbH.", + [3]byte{0, 38, 241}: "ProCurve Networking by HP", + [3]byte{0, 38, 242}: "NETGEAR", + [3]byte{0, 38, 243}: "SMC Networks", + [3]byte{0, 38, 244}: "Nesslab", + [3]byte{0, 38, 245}: "XRPLUS Inc.", + [3]byte{0, 38, 246}: "Military Communication Institute", + [3]byte{0, 38, 247}: "Nivetti Systems Pvt. Ltd.", + [3]byte{0, 38, 248}: "Golden Highway Industry Development Co., Ltd.", + [3]byte{0, 38, 249}: "S.E.M. srl", + [3]byte{0, 38, 250}: "BandRich Inc.", + [3]byte{0, 38, 251}: "AirDio Wireless, Inc.", + [3]byte{0, 38, 252}: "AcSiP Technology Corp.", + [3]byte{0, 38, 253}: "Interactive Intelligence", + [3]byte{0, 38, 254}: "MKD Technology Inc.", + [3]byte{0, 38, 255}: "BlackBerry RTS", + [3]byte{0, 39, 0}: "Shenzhen Siglent Technology Co., Ltd.", + [3]byte{0, 39, 1}: "INCOstartec GmbH", + [3]byte{0, 39, 2}: "SolarEdge Technologies", + [3]byte{0, 39, 3}: "Testech Electronics Pte Ltd", + [3]byte{0, 39, 4}: "Accelerated Concepts, Inc", + [3]byte{0, 39, 5}: "Sectronic", + [3]byte{0, 39, 6}: "YOISYS", + [3]byte{0, 39, 7}: "Lift Complex DS, JSC", + [3]byte{0, 39, 8}: "Nordiag ASA", + [3]byte{0, 39, 9}: "Nintendo Co., Ltd.", + [3]byte{0, 39, 10}: "IEE S.A.", + [3]byte{0, 39, 11}: "Adura Technologies", + [3]byte{0, 39, 12}: "Cisco Systems, Inc", + [3]byte{0, 39, 13}: "Cisco Systems, Inc", + [3]byte{0, 39, 14}: "Intel Corporate", + [3]byte{0, 39, 15}: "Envisionnovation Inc", + [3]byte{0, 39, 16}: "Intel Corporate", + [3]byte{0, 39, 17}: "LanPro Inc", + [3]byte{0, 39, 18}: "MaxVision LLC", + [3]byte{0, 39, 19}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{0, 39, 20}: "Grainmustards, Co,ltd.", + [3]byte{0, 39, 21}: "Rebound Telecom. Co., Ltd", + [3]byte{0, 39, 22}: "Adachi-Syokai Co., Ltd.", + [3]byte{0, 39, 23}: "CE Digital(Zhenjiang)Co.,Ltd", + [3]byte{0, 39, 24}: "Suzhou NEW SEAUNION Video Technology Co.,Ltd", + [3]byte{0, 39, 25}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{0, 39, 26}: "Geenovo Technology Ltd.", + [3]byte{0, 39, 27}: "Alec Sicherheitssysteme GmbH", + [3]byte{0, 39, 28}: "MERCURY CORPORATION", + [3]byte{0, 39, 29}: "Comba Telecom Systems (China) Ltd.", + [3]byte{0, 39, 30}: "Xagyl Communications", + [3]byte{0, 39, 31}: "MIPRO Electronics Co., Ltd", + [3]byte{0, 39, 32}: "NEW-SOL COM", + [3]byte{0, 39, 33}: "Shenzhen Baoan Fenda Industrial Co., Ltd", + [3]byte{0, 39, 34}: "Ubiquiti Networks Inc.", + [3]byte{0, 39, 248}: "Brocade Communications Systems, Inc.", + [3]byte{0, 40, 248}: "Intel Corporate", + [3]byte{0, 41, 38}: "Applied Optoelectronics, Inc Taiwan Branch", + [3]byte{0, 42, 16}: "Cisco Systems, Inc", + [3]byte{0, 42, 106}: "Cisco Systems, Inc", + [3]byte{0, 42, 175}: "LARsys-Automation GmbH", + [3]byte{0, 44, 200}: "Cisco Systems, Inc", + [3]byte{0, 45, 118}: "TITECH GmbH", + [3]byte{0, 48, 0}: "ALLWELL TECHNOLOGY CORP.", + [3]byte{0, 48, 1}: "SMP", + [3]byte{0, 48, 2}: "Expand Networks", + [3]byte{0, 48, 3}: "Phasys Ltd.", + [3]byte{0, 48, 4}: "LEADTEK RESEARCH INC.", + [3]byte{0, 48, 5}: "Fujitsu Siemens Computers", + [3]byte{0, 48, 6}: "SUPERPOWER COMPUTER", + [3]byte{0, 48, 7}: "OPTI, INC.", + [3]byte{0, 48, 8}: "AVIO DIGITAL, INC.", + [3]byte{0, 48, 9}: "Tachion Networks, Inc.", + [3]byte{0, 48, 10}: "Aztech Electronics Pte Ltd", + [3]byte{0, 48, 11}: "mPHASE Technologies, Inc.", + [3]byte{0, 48, 12}: "CONGRUENCY, LTD.", + [3]byte{0, 48, 13}: "MMC Technology, Inc.", + [3]byte{0, 48, 14}: "Klotz Digital AG", + [3]byte{0, 48, 15}: "IMT - Information Management T", + [3]byte{0, 48, 16}: "VISIONETICS INTERNATIONAL", + [3]byte{0, 48, 17}: "HMS Industrial Networks ", + [3]byte{0, 48, 18}: "DIGITAL ENGINEERING LTD.", + [3]byte{0, 48, 19}: "NEC Corporation", + [3]byte{0, 48, 20}: "DIVIO, INC.", + [3]byte{0, 48, 21}: "CP CLARE CORP.", + [3]byte{0, 48, 22}: "ISHIDA CO., LTD.", + [3]byte{0, 48, 23}: "BlueArc UK Ltd", + [3]byte{0, 48, 24}: "Jetway Information Co., Ltd.", + [3]byte{0, 48, 25}: "Cisco Systems, Inc", + [3]byte{0, 48, 26}: "SMARTBRIDGES PTE. LTD.", + [3]byte{0, 48, 27}: "SHUTTLE, INC.", + [3]byte{0, 48, 28}: "ALTVATER AIRDATA SYSTEMS", + [3]byte{0, 48, 29}: "SKYSTREAM, INC.", + [3]byte{0, 48, 30}: "3COM EUROPE LTD.", + [3]byte{0, 48, 31}: "OPTICAL NETWORKS, INC.", + [3]byte{0, 48, 32}: "TSI, Inc..", + [3]byte{0, 48, 33}: "HSING TECH. ENTERPRISE CO.,LTD", + [3]byte{0, 48, 34}: "Fong Kai Industrial Co., Ltd.", + [3]byte{0, 48, 35}: "COGENT COMPUTER SYSTEMS, INC.", + [3]byte{0, 48, 36}: "Cisco Systems, Inc", + [3]byte{0, 48, 37}: "CHECKOUT COMPUTER SYSTEMS, LTD", + [3]byte{0, 48, 38}: "HeiTel Digital Video GmbH", + [3]byte{0, 48, 39}: "KERBANGO, INC.", + [3]byte{0, 48, 40}: "FASE Saldatura srl", + [3]byte{0, 48, 41}: "OPICOM", + [3]byte{0, 48, 42}: "SOUTHERN INFORMATION", + [3]byte{0, 48, 43}: "INALP NETWORKS, INC.", + [3]byte{0, 48, 44}: "SYLANTRO SYSTEMS CORPORATION", + [3]byte{0, 48, 45}: "QUANTUM BRIDGE COMMUNICATIONS", + [3]byte{0, 48, 46}: "Hoft & Wessel AG", + [3]byte{0, 48, 47}: "GE Aviation System", + [3]byte{0, 48, 48}: "HARMONIX CORPORATION", + [3]byte{0, 48, 49}: "LIGHTWAVE COMMUNICATIONS, INC.", + [3]byte{0, 48, 50}: "MagicRam, Inc.", + [3]byte{0, 48, 51}: "ORIENT TELECOM CO., LTD.", + [3]byte{0, 48, 52}: "SET ENGINEERING", + [3]byte{0, 48, 53}: "Corning Incorporated", + [3]byte{0, 48, 54}: "RMP ELEKTRONIKSYSTEME GMBH", + [3]byte{0, 48, 55}: "Packard Bell Nec Services", + [3]byte{0, 48, 56}: "XCP, INC.", + [3]byte{0, 48, 57}: "SOFTBOOK PRESS", + [3]byte{0, 48, 58}: "MAATEL", + [3]byte{0, 48, 59}: "PowerCom Technology", + [3]byte{0, 48, 60}: "ONNTO CORP.", + [3]byte{0, 48, 61}: "IVA CORPORATION", + [3]byte{0, 48, 62}: "Radcom Ltd.", + [3]byte{0, 48, 63}: "TurboComm Tech Inc.", + [3]byte{0, 48, 64}: "Cisco Systems, Inc", + [3]byte{0, 48, 65}: "SAEJIN T & M CO., LTD.", + [3]byte{0, 48, 66}: "DeTeWe-Deutsche Telephonwerke", + [3]byte{0, 48, 67}: "IDREAM TECHNOLOGIES, PTE. LTD.", + [3]byte{0, 48, 68}: "CradlePoint, Inc", + [3]byte{0, 48, 69}: "Village Networks, Inc. (VNI)", + [3]byte{0, 48, 70}: "Controlled Electronic Manageme", + [3]byte{0, 48, 71}: "NISSEI ELECTRIC CO., LTD.", + [3]byte{0, 48, 72}: "Super Micro Computer, Inc.", + [3]byte{0, 48, 73}: "BRYANT TECHNOLOGY, LTD.", + [3]byte{0, 48, 74}: "Fraunhofer IPMS", + [3]byte{0, 48, 75}: "ORBACOM SYSTEMS, INC.", + [3]byte{0, 48, 76}: "APPIAN COMMUNICATIONS, INC.", + [3]byte{0, 48, 77}: "ESI", + [3]byte{0, 48, 78}: "BUSTEC PRODUCTION LTD.", + [3]byte{0, 48, 79}: "PLANET Technology Corporation", + [3]byte{0, 48, 80}: "Versa Technology", + [3]byte{0, 48, 81}: "ORBIT AVIONIC & COMMUNICATION", + [3]byte{0, 48, 82}: "ELASTIC NETWORKS", + [3]byte{0, 48, 83}: "Basler AG", + [3]byte{0, 48, 84}: "CASTLENET TECHNOLOGY, INC.", + [3]byte{0, 48, 85}: "Renesas Technology America, Inc.", + [3]byte{0, 48, 86}: "Beck IPC GmbH", + [3]byte{0, 48, 87}: "QTelNet, Inc.", + [3]byte{0, 48, 88}: "API MOTION", + [3]byte{0, 48, 89}: "KONTRON COMPACT COMPUTERS AG", + [3]byte{0, 48, 90}: "TELGEN CORPORATION", + [3]byte{0, 48, 91}: "Toko Inc.", + [3]byte{0, 48, 92}: "SMAR Laboratories Corp.", + [3]byte{0, 48, 93}: "DIGITRA SYSTEMS, INC.", + [3]byte{0, 48, 94}: "Abelko Innovation", + [3]byte{0, 48, 95}: "Hasselblad", + [3]byte{0, 48, 96}: "Powerfile, Inc.", + [3]byte{0, 48, 97}: "MobyTEL", + [3]byte{0, 48, 98}: "IP Video Networks Inc", + [3]byte{0, 48, 99}: "SANTERA SYSTEMS, INC.", + [3]byte{0, 48, 100}: "ADLINK TECHNOLOGY, INC.", + [3]byte{0, 48, 101}: "Apple, Inc.", + [3]byte{0, 48, 102}: "RFM", + [3]byte{0, 48, 103}: "BIOSTAR Microtech Int'l Corp.", + [3]byte{0, 48, 104}: "CYBERNETICS TECH. CO., LTD.", + [3]byte{0, 48, 105}: "IMPACCT TECHNOLOGY CORP.", + [3]byte{0, 48, 106}: "PENTA MEDIA CO., LTD.", + [3]byte{0, 48, 107}: "CMOS SYSTEMS, INC.", + [3]byte{0, 48, 108}: "Hitex Holding GmbH", + [3]byte{0, 48, 109}: "LUCENT TECHNOLOGIES", + [3]byte{0, 48, 110}: "Hewlett Packard", + [3]byte{0, 48, 111}: "SEYEON TECH. CO., LTD.", + [3]byte{0, 48, 112}: "1Net Corporation", + [3]byte{0, 48, 113}: "Cisco Systems, Inc", + [3]byte{0, 48, 114}: "Intellibyte Inc.", + [3]byte{0, 48, 115}: "International Microsystems, In", + [3]byte{0, 48, 116}: "EQUIINET LTD.", + [3]byte{0, 48, 117}: "ADTECH", + [3]byte{0, 48, 118}: "Akamba Corporation", + [3]byte{0, 48, 119}: "ONPREM NETWORKS", + [3]byte{0, 48, 120}: "Cisco Systems, Inc", + [3]byte{0, 48, 121}: "CQOS, INC.", + [3]byte{0, 48, 122}: "Advanced Technology & Systems", + [3]byte{0, 48, 123}: "Cisco Systems, Inc", + [3]byte{0, 48, 124}: "ADID SA", + [3]byte{0, 48, 125}: "GRE AMERICA, INC.", + [3]byte{0, 48, 126}: "Redflex Communication Systems", + [3]byte{0, 48, 127}: "IRLAN LTD.", + [3]byte{0, 48, 128}: "Cisco Systems, Inc", + [3]byte{0, 48, 129}: "ALTOS C&C", + [3]byte{0, 48, 130}: "TAIHAN ELECTRIC WIRE CO., LTD.", + [3]byte{0, 48, 131}: "Ivron Systems", + [3]byte{0, 48, 132}: "ALLIED TELESYN INTERNAIONAL", + [3]byte{0, 48, 133}: "Cisco Systems, Inc", + [3]byte{0, 48, 134}: "Transistor Devices, Inc.", + [3]byte{0, 48, 135}: "VEGA GRIESHABER KG", + [3]byte{0, 48, 136}: "Ericsson", + [3]byte{0, 48, 137}: "Spectrapoint Wireless, LLC", + [3]byte{0, 48, 138}: "NICOTRA SISTEMI S.P.A", + [3]byte{0, 48, 139}: "Brix Networks", + [3]byte{0, 48, 140}: "Quantum Corporation", + [3]byte{0, 48, 141}: "Pinnacle Systems, Inc.", + [3]byte{0, 48, 142}: "CROSS MATCH TECHNOLOGIES, INC.", + [3]byte{0, 48, 143}: "MICRILOR, Inc.", + [3]byte{0, 48, 144}: "CYRA TECHNOLOGIES, INC.", + [3]byte{0, 48, 145}: "TAIWAN FIRST LINE ELEC. CORP.", + [3]byte{0, 48, 146}: "ModuNORM GmbH", + [3]byte{0, 48, 147}: "Sonnet Technologies, Inc", + [3]byte{0, 48, 148}: "Cisco Systems, Inc", + [3]byte{0, 48, 149}: "Procomp Informatics, Ltd.", + [3]byte{0, 48, 150}: "Cisco Systems, Inc", + [3]byte{0, 48, 151}: "AB Regin", + [3]byte{0, 48, 152}: "Global Converging Technologies", + [3]byte{0, 48, 153}: "BOENIG UND KALLENBACH OHG", + [3]byte{0, 48, 154}: "ASTRO TERRA CORP.", + [3]byte{0, 48, 155}: "Smartware", + [3]byte{0, 48, 156}: "Timing Applications, Inc.", + [3]byte{0, 48, 157}: "Nimble Microsystems, Inc.", + [3]byte{0, 48, 158}: "WORKBIT CORPORATION.", + [3]byte{0, 48, 159}: "AMBER NETWORKS", + [3]byte{0, 48, 160}: "TYCO SUBMARINE SYSTEMS, LTD.", + [3]byte{0, 48, 161}: "WEBGATE Inc.", + [3]byte{0, 48, 162}: "Lightner Engineering", + [3]byte{0, 48, 163}: "Cisco Systems, Inc", + [3]byte{0, 48, 164}: "Woodwind Communications System", + [3]byte{0, 48, 165}: "ACTIVE POWER", + [3]byte{0, 48, 166}: "VIANET TECHNOLOGIES, LTD.", + [3]byte{0, 48, 167}: "SCHWEITZER ENGINEERING", + [3]byte{0, 48, 168}: "OL'E COMMUNICATIONS, INC.", + [3]byte{0, 48, 169}: "Netiverse, Inc.", + [3]byte{0, 48, 170}: "AXUS MICROSYSTEMS, INC.", + [3]byte{0, 48, 171}: "DELTA NETWORKS, INC.", + [3]byte{0, 48, 172}: "Systeme Lauer GmbH & Co., Ltd.", + [3]byte{0, 48, 173}: "SHANGHAI COMMUNICATION", + [3]byte{0, 48, 174}: "Times N System, Inc.", + [3]byte{0, 48, 175}: "Honeywell GmbH", + [3]byte{0, 48, 176}: "Convergenet Technologies", + [3]byte{0, 48, 177}: "TrunkNet", + [3]byte{0, 48, 178}: "L-3 Sonoma EO", + [3]byte{0, 48, 179}: "San Valley Systems, Inc.", + [3]byte{0, 48, 180}: "INTERSIL CORP.", + [3]byte{0, 48, 181}: "Tadiran Microwave Networks", + [3]byte{0, 48, 182}: "Cisco Systems, Inc", + [3]byte{0, 48, 183}: "Teletrol Systems, Inc.", + [3]byte{0, 48, 184}: "RiverDelta Networks", + [3]byte{0, 48, 185}: "ECTEL", + [3]byte{0, 48, 186}: "AC&T SYSTEM CO., LTD.", + [3]byte{0, 48, 187}: "CacheFlow, Inc.", + [3]byte{0, 48, 188}: "Optronic AG", + [3]byte{0, 48, 189}: "BELKIN COMPONENTS", + [3]byte{0, 48, 190}: "City-Net Technology, Inc.", + [3]byte{0, 48, 191}: "MULTIDATA GMBH", + [3]byte{0, 48, 192}: "Lara Technology, Inc.", + [3]byte{0, 48, 193}: "Hewlett Packard", + [3]byte{0, 48, 194}: "COMONE", + [3]byte{0, 48, 195}: "FLUECKIGER ELEKTRONIK AG", + [3]byte{0, 48, 196}: "Canon Imaging Systems Inc.", + [3]byte{0, 48, 197}: "CADENCE DESIGN SYSTEMS, INC.", + [3]byte{0, 48, 198}: "CONTROL SOLUTIONS, INC.", + [3]byte{0, 48, 199}: "Macromate Corp.", + [3]byte{0, 48, 200}: "GAD LINE, LTD.", + [3]byte{0, 48, 201}: "LuxN, N", + [3]byte{0, 48, 202}: "Discovery Com", + [3]byte{0, 48, 203}: "OMNI FLOW COMPUTERS, INC.", + [3]byte{0, 48, 204}: "Tenor Networks, Inc.", + [3]byte{0, 48, 205}: "CONEXANT SYSTEMS, INC.", + [3]byte{0, 48, 206}: "Zaffire", + [3]byte{0, 48, 207}: "TWO TECHNOLOGIES, INC.", + [3]byte{0, 48, 208}: "Tellabs", + [3]byte{0, 48, 209}: "INOVA CORPORATION", + [3]byte{0, 48, 210}: "WIN TECHNOLOGIES, CO., LTD.", + [3]byte{0, 48, 211}: "Agilent Technologies, Inc.", + [3]byte{0, 48, 212}: "AAE Systems, Inc.", + [3]byte{0, 48, 213}: "DResearch GmbH", + [3]byte{0, 48, 214}: "MSC VERTRIEBS GMBH", + [3]byte{0, 48, 215}: "Innovative Systems, L.L.C.", + [3]byte{0, 48, 216}: "SITEK", + [3]byte{0, 48, 217}: "DATACORE SOFTWARE CORP.", + [3]byte{0, 48, 218}: "Comtrend Corporation", + [3]byte{0, 48, 219}: "Mindready Solutions, Inc.", + [3]byte{0, 48, 220}: "RIGHTECH CORPORATION", + [3]byte{0, 48, 221}: "INDIGITA CORPORATION", + [3]byte{0, 48, 222}: "WAGO Kontakttechnik GmbH", + [3]byte{0, 48, 223}: "KB/TEL TELECOMUNICACIONES", + [3]byte{0, 48, 224}: "OXFORD SEMICONDUCTOR LTD.", + [3]byte{0, 48, 225}: "Network Equipment Technologies, Inc.", + [3]byte{0, 48, 226}: "GARNET SYSTEMS CO., LTD.", + [3]byte{0, 48, 227}: "SEDONA NETWORKS CORP.", + [3]byte{0, 48, 228}: "CHIYODA SYSTEM RIKEN", + [3]byte{0, 48, 229}: "Amper Datos S.A.", + [3]byte{0, 48, 230}: "Draeger Medical Systems, Inc.", + [3]byte{0, 48, 231}: "CNF MOBILE SOLUTIONS, INC.", + [3]byte{0, 48, 232}: "ENSIM CORP.", + [3]byte{0, 48, 233}: "GMA COMMUNICATION MANUFACT'G", + [3]byte{0, 48, 234}: "TeraForce Technology Corporation", + [3]byte{0, 48, 235}: "TURBONET COMMUNICATIONS, INC.", + [3]byte{0, 48, 236}: "BORGARDT", + [3]byte{0, 48, 237}: "Expert Magnetics Corp.", + [3]byte{0, 48, 238}: "DSG Technology, Inc.", + [3]byte{0, 48, 239}: "NEON TECHNOLOGY, INC.", + [3]byte{0, 48, 240}: "Uniform Industrial Corp.", + [3]byte{0, 48, 241}: "Accton Technology Corp", + [3]byte{0, 48, 242}: "Cisco Systems, Inc", + [3]byte{0, 48, 243}: "At Work Computers", + [3]byte{0, 48, 244}: "STARDOT TECHNOLOGIES", + [3]byte{0, 48, 245}: "Wild Lab. Ltd.", + [3]byte{0, 48, 246}: "SECURELOGIX CORPORATION", + [3]byte{0, 48, 247}: "RAMIX INC.", + [3]byte{0, 48, 248}: "Dynapro Systems, Inc.", + [3]byte{0, 48, 249}: "Sollae Systems Co., Ltd.", + [3]byte{0, 48, 250}: "TELICA, INC.", + [3]byte{0, 48, 251}: "AZS Technology AG", + [3]byte{0, 48, 252}: "Terawave Communications, Inc.", + [3]byte{0, 48, 253}: "INTEGRATED SYSTEMS DESIGN", + [3]byte{0, 48, 254}: "DSA GmbH", + [3]byte{0, 48, 255}: "DataFab Systems Inc.", + [3]byte{0, 49, 70}: "Juniper Networks", + [3]byte{0, 50, 58}: "so-logic", + [3]byte{0, 51, 108}: "SynapSense Corporation", + [3]byte{0, 52, 218}: "LG Electronics (Mobile Communications)", + [3]byte{0, 52, 241}: "Radicom Research, Inc.", + [3]byte{0, 52, 254}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 53, 26}: "Cisco Systems, Inc", + [3]byte{0, 53, 50}: "Electro-Metrics Corporation", + [3]byte{0, 53, 96}: "Rosen Aviation", + [3]byte{0, 54, 118}: "ARRIS Group, Inc.", + [3]byte{0, 54, 248}: "Conti Temic microelectronic GmbH", + [3]byte{0, 54, 254}: "SuperVision", + [3]byte{0, 55, 109}: "Murata Manufacturing Co., Ltd.", + [3]byte{0, 55, 183}: "Sagemcom Broadband SAS", + [3]byte{0, 56, 223}: "Cisco Systems, Inc", + [3]byte{0, 58, 125}: "Cisco Systems, Inc", + [3]byte{0, 58, 152}: "Cisco Systems, Inc", + [3]byte{0, 58, 153}: "Cisco Systems, Inc", + [3]byte{0, 58, 154}: "Cisco Systems, Inc", + [3]byte{0, 58, 155}: "Cisco Systems, Inc", + [3]byte{0, 58, 156}: "Cisco Systems, Inc", + [3]byte{0, 58, 157}: "NEC Platforms, Ltd.", + [3]byte{0, 58, 175}: "BlueBit Ltd.", + [3]byte{0, 60, 197}: "WONWOO Engineering Co., Ltd", + [3]byte{0, 61, 65}: "Hatteland Computer AS", + [3]byte{0, 62, 225}: "Apple, Inc.", + [3]byte{0, 64, 0}: "PCI COMPONENTES DA AMZONIA LTD", + [3]byte{0, 64, 1}: "Zero One Technology Co. Ltd.", + [3]byte{0, 64, 2}: "PERLE SYSTEMS LIMITED", + [3]byte{0, 64, 3}: "Emerson Process Management Power & Water Solutions, Inc.", + [3]byte{0, 64, 4}: "ICM CO. LTD.", + [3]byte{0, 64, 5}: "ANI COMMUNICATIONS INC.", + [3]byte{0, 64, 6}: "SAMPO TECHNOLOGY CORPORATION", + [3]byte{0, 64, 7}: "TELMAT INFORMATIQUE", + [3]byte{0, 64, 8}: "A PLUS INFO CORPORATION", + [3]byte{0, 64, 9}: "TACHIBANA TECTRON CO., LTD.", + [3]byte{0, 64, 10}: "PIVOTAL TECHNOLOGIES, INC.", + [3]byte{0, 64, 11}: "Cisco Systems, Inc", + [3]byte{0, 64, 12}: "GENERAL MICRO SYSTEMS, INC.", + [3]byte{0, 64, 13}: "LANNET DATA COMMUNICATIONS,LTD", + [3]byte{0, 64, 14}: "MEMOTEC, INC.", + [3]byte{0, 64, 15}: "DATACOM TECHNOLOGIES", + [3]byte{0, 64, 16}: "SONIC SYSTEMS, INC.", + [3]byte{0, 64, 17}: "ANDOVER CONTROLS CORPORATION", + [3]byte{0, 64, 18}: "WINDATA, INC.", + [3]byte{0, 64, 19}: "NTT DATA COMM. SYSTEMS CORP.", + [3]byte{0, 64, 20}: "COMSOFT GMBH", + [3]byte{0, 64, 21}: "ASCOM INFRASYS AG", + [3]byte{0, 64, 22}: "ADC - Global Connectivity Solutions Division", + [3]byte{0, 64, 23}: "Silex Technology America", + [3]byte{0, 64, 24}: "ADOBE SYSTEMS, INC.", + [3]byte{0, 64, 25}: "AEON SYSTEMS, INC.", + [3]byte{0, 64, 26}: "FUJI ELECTRIC CO., LTD.", + [3]byte{0, 64, 27}: "PRINTER SYSTEMS CORP.", + [3]byte{0, 64, 28}: "AST RESEARCH, INC.", + [3]byte{0, 64, 29}: "INVISIBLE SOFTWARE, INC.", + [3]byte{0, 64, 30}: "ICC", + [3]byte{0, 64, 31}: "COLORGRAPH LTD", + [3]byte{0, 64, 32}: "CommScope Inc", + [3]byte{0, 64, 33}: "RASTER GRAPHICS", + [3]byte{0, 64, 34}: "KLEVER COMPUTERS, INC.", + [3]byte{0, 64, 35}: "LOGIC CORPORATION", + [3]byte{0, 64, 36}: "COMPAC INC.", + [3]byte{0, 64, 37}: "MOLECULAR DYNAMICS", + [3]byte{0, 64, 38}: "BUFFALO.INC", + [3]byte{0, 64, 39}: "SMC MASSACHUSETTS, INC.", + [3]byte{0, 64, 40}: "NETCOMM LIMITED", + [3]byte{0, 64, 41}: "Compex", + [3]byte{0, 64, 42}: "Canoga Perkins Corporation", + [3]byte{0, 64, 43}: "TRIGEM COMPUTER, INC.", + [3]byte{0, 64, 44}: "ISIS DISTRIBUTED SYSTEMS, INC.", + [3]byte{0, 64, 45}: "HARRIS ADACOM CORPORATION", + [3]byte{0, 64, 46}: "PRECISION SOFTWARE, INC.", + [3]byte{0, 64, 47}: "XLNT DESIGNS INC.", + [3]byte{0, 64, 48}: "GK COMPUTER", + [3]byte{0, 64, 49}: "KOKUSAI ELECTRIC CO., LTD", + [3]byte{0, 64, 50}: "DIGITAL COMMUNICATIONS", + [3]byte{0, 64, 51}: "ADDTRON TECHNOLOGY CO., LTD.", + [3]byte{0, 64, 52}: "BUSTEK CORPORATION", + [3]byte{0, 64, 53}: "OPCOM", + [3]byte{0, 64, 54}: "Zoom Telephonics, Inc", + [3]byte{0, 64, 55}: "SEA-ILAN, INC.", + [3]byte{0, 64, 56}: "TALENT ELECTRIC INCORPORATED", + [3]byte{0, 64, 57}: "OPTEC DAIICHI DENKO CO., LTD.", + [3]byte{0, 64, 58}: "IMPACT TECHNOLOGIES", + [3]byte{0, 64, 59}: "SYNERJET INTERNATIONAL CORP.", + [3]byte{0, 64, 60}: "FORKS, INC.", + [3]byte{0, 64, 61}: "Teradata Corporation", + [3]byte{0, 64, 62}: "RASTER OPS CORPORATION", + [3]byte{0, 64, 63}: "SSANGYONG COMPUTER SYSTEMS", + [3]byte{0, 64, 64}: "RING ACCESS, INC.", + [3]byte{0, 64, 65}: "FUJIKURA LTD.", + [3]byte{0, 64, 66}: "N.A.T. GMBH", + [3]byte{0, 64, 67}: "Nokia Siemens Networks GmbH & Co. KG.", + [3]byte{0, 64, 68}: "QNIX COMPUTER CO., LTD.", + [3]byte{0, 64, 69}: "TWINHEAD CORPORATION", + [3]byte{0, 64, 70}: "UDC RESEARCH LIMITED", + [3]byte{0, 64, 71}: "WIND RIVER SYSTEMS", + [3]byte{0, 64, 72}: "SMD INFORMATICA S.A.", + [3]byte{0, 64, 73}: "Roche Diagnostics International Ltd.", + [3]byte{0, 64, 74}: "WEST AUSTRALIAN DEPARTMENT", + [3]byte{0, 64, 75}: "MAPLE COMPUTER SYSTEMS", + [3]byte{0, 64, 76}: "HYPERTEC PTY LTD.", + [3]byte{0, 64, 77}: "TELECOMMUNICATIONS TECHNIQUES", + [3]byte{0, 64, 78}: "FLUENT, INC.", + [3]byte{0, 64, 79}: "SPACE & NAVAL WARFARE SYSTEMS", + [3]byte{0, 64, 80}: "IRONICS, INCORPORATED", + [3]byte{0, 64, 81}: "GRACILIS, INC.", + [3]byte{0, 64, 82}: "STAR TECHNOLOGIES, INC.", + [3]byte{0, 64, 83}: "AMPRO COMPUTERS", + [3]byte{0, 64, 84}: "CONNECTION MACHINES SERVICES", + [3]byte{0, 64, 85}: "METRONIX GMBH", + [3]byte{0, 64, 86}: "MCM JAPAN LTD.", + [3]byte{0, 64, 87}: "LOCKHEED - SANDERS", + [3]byte{0, 64, 88}: "KRONOS, INC.", + [3]byte{0, 64, 89}: "YOSHIDA KOGYO K. K.", + [3]byte{0, 64, 90}: "GOLDSTAR INFORMATION & COMM.", + [3]byte{0, 64, 91}: "FUNASSET LIMITED", + [3]byte{0, 64, 92}: "FUTURE SYSTEMS, INC.", + [3]byte{0, 64, 93}: "STAR-TEK, INC.", + [3]byte{0, 64, 94}: "NORTH HILLS ISRAEL", + [3]byte{0, 64, 95}: "AFE COMPUTERS LTD.", + [3]byte{0, 64, 96}: "COMENDEC LTD", + [3]byte{0, 64, 97}: "DATATECH ENTERPRISES CO., LTD.", + [3]byte{0, 64, 98}: "E-SYSTEMS, INC./GARLAND DIV.", + [3]byte{0, 64, 99}: "VIA TECHNOLOGIES, INC.", + [3]byte{0, 64, 100}: "KLA INSTRUMENTS CORPORATION", + [3]byte{0, 64, 101}: "GTE SPACENET", + [3]byte{0, 64, 102}: "APRESIA Systems Ltd", + [3]byte{0, 64, 103}: "OMNIBYTE CORPORATION", + [3]byte{0, 64, 104}: "EXTENDED SYSTEMS", + [3]byte{0, 64, 105}: "LEMCOM SYSTEMS, INC.", + [3]byte{0, 64, 106}: "KENTEK INFORMATION SYSTEMS,INC", + [3]byte{0, 64, 107}: "SYSGEN", + [3]byte{0, 64, 108}: "COPERNIQUE", + [3]byte{0, 64, 109}: "LANCO, INC.", + [3]byte{0, 64, 110}: "COROLLARY, INC.", + [3]byte{0, 64, 111}: "SYNC RESEARCH INC.", + [3]byte{0, 64, 112}: "INTERWARE CO., LTD.", + [3]byte{0, 64, 113}: "ATM COMPUTER GMBH", + [3]byte{0, 64, 114}: "Applied Innovation Inc.", + [3]byte{0, 64, 115}: "BASS ASSOCIATES", + [3]byte{0, 64, 116}: "CABLE AND WIRELESS", + [3]byte{0, 64, 117}: "Tattile SRL ", + [3]byte{0, 64, 118}: "Sun Conversion Technologies", + [3]byte{0, 64, 119}: "MAXTON TECHNOLOGY CORPORATION", + [3]byte{0, 64, 120}: "WEARNES AUTOMATION PTE LTD", + [3]byte{0, 64, 121}: "JUKO MANUFACTURE COMPANY, LTD.", + [3]byte{0, 64, 122}: "SOCIETE D'EXPLOITATION DU CNIT", + [3]byte{0, 64, 123}: "SCIENTIFIC ATLANTA", + [3]byte{0, 64, 124}: "QUME CORPORATION", + [3]byte{0, 64, 125}: "EXTENSION TECHNOLOGY CORP.", + [3]byte{0, 64, 126}: "EVERGREEN SYSTEMS, INC.", + [3]byte{0, 64, 127}: "FLIR Systems", + [3]byte{0, 64, 128}: "ATHENIX CORPORATION", + [3]byte{0, 64, 129}: "MANNESMANN SCANGRAPHIC GMBH", + [3]byte{0, 64, 130}: "LABORATORY EQUIPMENT CORP.", + [3]byte{0, 64, 131}: "TDA INDUSTRIA DE PRODUTOS", + [3]byte{0, 64, 132}: "HONEYWELL ACS", + [3]byte{0, 64, 133}: "SAAB INSTRUMENTS AB", + [3]byte{0, 64, 134}: "MICHELS & KLEBERHOFF COMPUTER", + [3]byte{0, 64, 135}: "UBITREX CORPORATION", + [3]byte{0, 64, 136}: "MOBIUS TECHNOLOGIES, INC.", + [3]byte{0, 64, 137}: "MEIDENSHA CORPORATION", + [3]byte{0, 64, 138}: "TPS TELEPROCESSING SYS. GMBH", + [3]byte{0, 64, 139}: "RAYLAN CORPORATION", + [3]byte{0, 64, 140}: "AXIS COMMUNICATIONS AB", + [3]byte{0, 64, 141}: "THE GOODYEAR TIRE & RUBBER CO.", + [3]byte{0, 64, 142}: "Tattile SRL ", + [3]byte{0, 64, 143}: "WM-DATA MINFO AB", + [3]byte{0, 64, 144}: "ANSEL COMMUNICATIONS", + [3]byte{0, 64, 145}: "PROCOMP INDUSTRIA ELETRONICA", + [3]byte{0, 64, 146}: "ASP COMPUTER PRODUCTS, INC.", + [3]byte{0, 64, 147}: "PAXDATA NETWORKS LTD.", + [3]byte{0, 64, 148}: "SHOGRAPHICS, INC.", + [3]byte{0, 64, 149}: "R.P.T. INTERGROUPS INT'L LTD.", + [3]byte{0, 64, 150}: "Cisco Systems, Inc", + [3]byte{0, 64, 151}: "DATEX DIVISION OF", + [3]byte{0, 64, 152}: "DRESSLER GMBH & CO.", + [3]byte{0, 64, 153}: "NEWGEN SYSTEMS CORP.", + [3]byte{0, 64, 154}: "NETWORK EXPRESS, INC.", + [3]byte{0, 64, 155}: "HAL COMPUTER SYSTEMS INC.", + [3]byte{0, 64, 156}: "TRANSWARE", + [3]byte{0, 64, 157}: "DIGIBOARD, INC.", + [3]byte{0, 64, 158}: "CONCURRENT TECHNOLOGIES LTD.", + [3]byte{0, 64, 159}: "Telco Systems, Inc. ", + [3]byte{0, 64, 160}: "GOLDSTAR CO., LTD.", + [3]byte{0, 64, 161}: "ERGO COMPUTING", + [3]byte{0, 64, 162}: "KINGSTAR TECHNOLOGY INC.", + [3]byte{0, 64, 163}: "MICROUNITY SYSTEMS ENGINEERING", + [3]byte{0, 64, 164}: "ROSE ELECTRONICS", + [3]byte{0, 64, 165}: "CLINICOMP INTL.", + [3]byte{0, 64, 166}: "Cray, Inc.", + [3]byte{0, 64, 167}: "ITAUTEC PHILCO S.A.", + [3]byte{0, 64, 168}: "IMF INTERNATIONAL LTD.", + [3]byte{0, 64, 169}: "DATACOM INC.", + [3]byte{0, 64, 170}: "Metso Automation", + [3]byte{0, 64, 171}: "ROLAND DG CORPORATION", + [3]byte{0, 64, 172}: "SUPER WORKSTATION, INC.", + [3]byte{0, 64, 173}: "SMA REGELSYSTEME GMBH", + [3]byte{0, 64, 174}: "DELTA CONTROLS, INC.", + [3]byte{0, 64, 175}: "DIGITAL PRODUCTS, INC.", + [3]byte{0, 64, 176}: "BYTEX CORPORATION, ENGINEERING", + [3]byte{0, 64, 177}: "CODONICS INC.", + [3]byte{0, 64, 178}: "SYSTEMFORSCHUNG", + [3]byte{0, 64, 179}: "ParTech Inc.", + [3]byte{0, 64, 180}: "NEXTCOM K.K.", + [3]byte{0, 64, 181}: "VIDEO TECHNOLOGY COMPUTERS LTD", + [3]byte{0, 64, 182}: "COMPUTERM CORPORATION", + [3]byte{0, 64, 183}: "STEALTH COMPUTER SYSTEMS", + [3]byte{0, 64, 184}: "IDEA ASSOCIATES", + [3]byte{0, 64, 185}: "MACQ ELECTRONIQUE SA", + [3]byte{0, 64, 186}: "ALLIANT COMPUTER SYSTEMS CORP.", + [3]byte{0, 64, 187}: "GOLDSTAR CABLE CO., LTD.", + [3]byte{0, 64, 188}: "ALGORITHMICS LTD.", + [3]byte{0, 64, 189}: "STARLIGHT NETWORKS, INC.", + [3]byte{0, 64, 190}: "BOEING DEFENSE & SPACE", + [3]byte{0, 64, 191}: "CHANNEL SYSTEMS INTERN'L INC.", + [3]byte{0, 64, 192}: "VISTA CONTROLS CORPORATION", + [3]byte{0, 64, 193}: "BIZERBA-WERKE WILHEIM KRAUT", + [3]byte{0, 64, 194}: "APPLIED COMPUTING DEVICES", + [3]byte{0, 64, 195}: "FISCHER AND PORTER CO.", + [3]byte{0, 64, 196}: "KINKEI SYSTEM CORPORATION", + [3]byte{0, 64, 197}: "MICOM COMMUNICATIONS INC.", + [3]byte{0, 64, 198}: "FIBERNET RESEARCH, INC.", + [3]byte{0, 64, 199}: "RUBY TECH CORPORATION", + [3]byte{0, 64, 200}: "MILAN TECHNOLOGY CORPORATION", + [3]byte{0, 64, 201}: "NCUBE", + [3]byte{0, 64, 202}: "FIRST INTERNAT'L COMPUTER, INC", + [3]byte{0, 64, 203}: "LANWAN TECHNOLOGIES", + [3]byte{0, 64, 204}: "SILCOM MANUF'G TECHNOLOGY INC.", + [3]byte{0, 64, 205}: "TERA MICROSYSTEMS, INC.", + [3]byte{0, 64, 206}: "NET-SOURCE, INC.", + [3]byte{0, 64, 207}: "STRAWBERRY TREE, INC.", + [3]byte{0, 64, 208}: "MITAC INTERNATIONAL CORP.", + [3]byte{0, 64, 209}: "FUKUDA DENSHI CO., LTD.", + [3]byte{0, 64, 210}: "PAGINE CORPORATION", + [3]byte{0, 64, 211}: "KIMPSION INTERNATIONAL CORP.", + [3]byte{0, 64, 212}: "GAGE TALKER CORP.", + [3]byte{0, 64, 213}: "Sartorius Mechatronics T&H GmbH ", + [3]byte{0, 64, 214}: "LOCAMATION B.V.", + [3]byte{0, 64, 215}: "STUDIO GEN INC.", + [3]byte{0, 64, 216}: "OCEAN OFFICE AUTOMATION LTD.", + [3]byte{0, 64, 217}: "AMERICAN MEGATRENDS INC.", + [3]byte{0, 64, 218}: "TELSPEC LTD", + [3]byte{0, 64, 219}: "ADVANCED TECHNICAL SOLUTIONS", + [3]byte{0, 64, 220}: "TRITEC ELECTRONIC GMBH", + [3]byte{0, 64, 221}: "HONG TECHNOLOGIES", + [3]byte{0, 64, 222}: "Elsag Datamat spa", + [3]byte{0, 64, 223}: "DIGALOG SYSTEMS, INC.", + [3]byte{0, 64, 224}: "ATOMWIDE LTD.", + [3]byte{0, 64, 225}: "MARNER INTERNATIONAL, INC.", + [3]byte{0, 64, 226}: "MESA RIDGE TECHNOLOGIES, INC.", + [3]byte{0, 64, 227}: "QUIN SYSTEMS LTD", + [3]byte{0, 64, 228}: "E-M TECHNOLOGY, INC.", + [3]byte{0, 64, 229}: "SYBUS CORPORATION", + [3]byte{0, 64, 230}: "C.A.E.N.", + [3]byte{0, 64, 231}: "ARNOS INSTRUMENTS & COMPUTER", + [3]byte{0, 64, 232}: "CHARLES RIVER DATA SYSTEMS,INC", + [3]byte{0, 64, 233}: "ACCORD SYSTEMS, INC.", + [3]byte{0, 64, 234}: "PLAIN TREE SYSTEMS INC", + [3]byte{0, 64, 235}: "MARTIN MARIETTA CORPORATION", + [3]byte{0, 64, 236}: "MIKASA SYSTEM ENGINEERING", + [3]byte{0, 64, 237}: "NETWORK CONTROLS INT'NATL INC.", + [3]byte{0, 64, 238}: "OPTIMEM", + [3]byte{0, 64, 239}: "HYPERCOM, INC.", + [3]byte{0, 64, 240}: "MicroBrain,Inc.", + [3]byte{0, 64, 241}: "CHUO ELECTRONICS CO., LTD.", + [3]byte{0, 64, 242}: "JANICH & KLASS COMPUTERTECHNIK", + [3]byte{0, 64, 243}: "NETCOR", + [3]byte{0, 64, 244}: "CAMEO COMMUNICATIONS, INC.", + [3]byte{0, 64, 245}: "OEM ENGINES", + [3]byte{0, 64, 246}: "KATRON COMPUTERS INC.", + [3]byte{0, 64, 247}: "Polaroid Corporation", + [3]byte{0, 64, 248}: "SYSTEMHAUS DISCOM", + [3]byte{0, 64, 249}: "COMBINET", + [3]byte{0, 64, 250}: "MICROBOARDS, INC.", + [3]byte{0, 64, 251}: "CASCADE COMMUNICATIONS", + [3]byte{0, 64, 252}: "IBR COMPUTER TECHNIK GMBH", + [3]byte{0, 64, 253}: "LXE", + [3]byte{0, 64, 254}: "SYMPLEX COMMUNICATIONS", + [3]byte{0, 64, 255}: "TELEBIT CORPORATION", + [3]byte{0, 65, 180}: "Wuxi Zhongxing Optoelectronics Technology Co.,Ltd.", + [3]byte{0, 65, 210}: "Cisco Systems, Inc", + [3]byte{0, 66, 82}: "RLX Technologies", + [3]byte{0, 66, 90}: "Cisco Systems, Inc", + [3]byte{0, 66, 104}: "Cisco Systems, Inc", + [3]byte{0, 67, 255}: "KETRON S.R.L.", + [3]byte{0, 69, 1}: "Versus Technology, Inc.", + [3]byte{0, 70, 75}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 74, 119}: "zte corporation", + [3]byte{0, 75, 243}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{0, 77, 50}: "Andon Health Co.,Ltd.", + [3]byte{0, 80, 0}: "NEXO COMMUNICATIONS, INC.", + [3]byte{0, 80, 1}: "YAMASHITA SYSTEMS CORP.", + [3]byte{0, 80, 2}: "OMNISEC AG", + [3]byte{0, 80, 3}: "Xrite Inc", + [3]byte{0, 80, 4}: "3COM CORPORATION", + [3]byte{0, 80, 6}: "TAC AB", + [3]byte{0, 80, 7}: "SIEMENS TELECOMMUNICATION SYSTEMS LIMITED", + [3]byte{0, 80, 8}: "TIVA MICROCOMPUTER CORP. (TMC)", + [3]byte{0, 80, 9}: "PHILIPS BROADBAND NETWORKS", + [3]byte{0, 80, 10}: "IRIS TECHNOLOGIES, INC.", + [3]byte{0, 80, 11}: "Cisco Systems, Inc", + [3]byte{0, 80, 12}: "e-Tek Labs, Inc.", + [3]byte{0, 80, 13}: "SATORI ELECTORIC CO., LTD.", + [3]byte{0, 80, 14}: "CHROMATIS NETWORKS, INC.", + [3]byte{0, 80, 15}: "Cisco Systems, Inc", + [3]byte{0, 80, 16}: "NovaNET Learning, Inc.", + [3]byte{0, 80, 18}: "CBL - GMBH", + [3]byte{0, 80, 19}: "Chaparral Network Storage", + [3]byte{0, 80, 20}: "Cisco Systems, Inc", + [3]byte{0, 80, 21}: "BRIGHT STAR ENGINEERING", + [3]byte{0, 80, 22}: "Molex Canada Ltd", + [3]byte{0, 80, 23}: "RSR S.R.L.", + [3]byte{0, 80, 24}: "AMIT, Inc.", + [3]byte{0, 80, 25}: "SPRING TIDE NETWORKS, INC.", + [3]byte{0, 80, 26}: "IQinVision", + [3]byte{0, 80, 27}: "ABL CANADA, INC.", + [3]byte{0, 80, 28}: "JATOM SYSTEMS, INC.", + [3]byte{0, 80, 30}: "Grass Valley, A Belden Brand", + [3]byte{0, 80, 31}: "MRG SYSTEMS, LTD.", + [3]byte{0, 80, 32}: "MEDIASTAR CO., LTD.", + [3]byte{0, 80, 33}: "EIS INTERNATIONAL, INC.", + [3]byte{0, 80, 34}: "ZONET TECHNOLOGY, INC.", + [3]byte{0, 80, 35}: "PG DESIGN ELECTRONICS, INC.", + [3]byte{0, 80, 36}: "NAVIC SYSTEMS, INC.", + [3]byte{0, 80, 38}: "COSYSTEMS, INC.", + [3]byte{0, 80, 39}: "GENICOM CORPORATION", + [3]byte{0, 80, 40}: "AVAL COMMUNICATIONS", + [3]byte{0, 80, 41}: "1394 PRINTER WORKING GROUP", + [3]byte{0, 80, 42}: "Cisco Systems, Inc", + [3]byte{0, 80, 43}: "GENRAD LTD.", + [3]byte{0, 80, 44}: "SOYO COMPUTER, INC.", + [3]byte{0, 80, 45}: "ACCEL, INC.", + [3]byte{0, 80, 46}: "CAMBEX CORPORATION", + [3]byte{0, 80, 47}: "TollBridge Technologies, Inc.", + [3]byte{0, 80, 48}: "FUTURE PLUS SYSTEMS", + [3]byte{0, 80, 49}: "AEROFLEX LABORATORIES, INC.", + [3]byte{0, 80, 50}: "PICAZO COMMUNICATIONS, INC.", + [3]byte{0, 80, 51}: "MAYAN NETWORKS", + [3]byte{0, 80, 54}: "NETCAM, LTD.", + [3]byte{0, 80, 55}: "KOGA ELECTRONICS CO.", + [3]byte{0, 80, 56}: "DAIN TELECOM CO., LTD.", + [3]byte{0, 80, 57}: "MARINER NETWORKS", + [3]byte{0, 80, 58}: "DATONG ELECTRONICS LTD.", + [3]byte{0, 80, 59}: "MEDIAFIRE CORPORATION", + [3]byte{0, 80, 60}: "TSINGHUA NOVEL ELECTRONICS", + [3]byte{0, 80, 62}: "Cisco Systems, Inc", + [3]byte{0, 80, 63}: "ANCHOR GAMES", + [3]byte{0, 80, 64}: "Panasonic Electric Works Co., Ltd.", + [3]byte{0, 80, 65}: "Coretronic Corporation", + [3]byte{0, 80, 66}: "SCI MANUFACTURING SINGAPORE PTE, LTD.", + [3]byte{0, 80, 67}: "MARVELL SEMICONDUCTOR, INC.", + [3]byte{0, 80, 68}: "ASACA CORPORATION", + [3]byte{0, 80, 69}: "RIOWORKS SOLUTIONS, INC.", + [3]byte{0, 80, 70}: "MENICX INTERNATIONAL CO., LTD.", + [3]byte{0, 80, 71}: "Private", + [3]byte{0, 80, 72}: "INFOLIBRIA", + [3]byte{0, 80, 73}: "Arbor Networks Inc", + [3]byte{0, 80, 74}: "ELTECO A.S.", + [3]byte{0, 80, 75}: "BARCONET N.V.", + [3]byte{0, 80, 76}: "Galil Motion Control", + [3]byte{0, 80, 77}: "Tokyo Electron Device Limited", + [3]byte{0, 80, 78}: "SIERRA MONITOR CORP.", + [3]byte{0, 80, 79}: "OLENCOM ELECTRONICS", + [3]byte{0, 80, 80}: "Cisco Systems, Inc", + [3]byte{0, 80, 81}: "IWATSU ELECTRIC CO., LTD.", + [3]byte{0, 80, 82}: "TIARA NETWORKS, INC.", + [3]byte{0, 80, 83}: "Cisco Systems, Inc", + [3]byte{0, 80, 84}: "Cisco Systems, Inc", + [3]byte{0, 80, 85}: "DOMS A/S", + [3]byte{0, 80, 86}: "VMware, Inc.", + [3]byte{0, 80, 87}: "BROADBAND ACCESS SYSTEMS", + [3]byte{0, 80, 88}: "Sangoma Technologies", + [3]byte{0, 80, 89}: "iBAHN", + [3]byte{0, 80, 90}: "NETWORK ALCHEMY, INC.", + [3]byte{0, 80, 91}: "KAWASAKI LSI U.S.A., INC.", + [3]byte{0, 80, 92}: "TUNDO CORPORATION", + [3]byte{0, 80, 94}: "DIGITEK MICROLOGIC S.A.", + [3]byte{0, 80, 95}: "BRAND INNOVATORS", + [3]byte{0, 80, 96}: "TANDBERG TELECOM AS", + [3]byte{0, 80, 98}: "KOUWELL ELECTRONICS CORP. **", + [3]byte{0, 80, 99}: "OY COMSEL SYSTEM AB", + [3]byte{0, 80, 100}: "CAE ELECTRONICS", + [3]byte{0, 80, 101}: "TDK-Lambda Corporation", + [3]byte{0, 80, 102}: "AtecoM GmbH advanced telecomunication modules", + [3]byte{0, 80, 103}: "AEROCOMM, INC.", + [3]byte{0, 80, 104}: "ELECTRONIC INDUSTRIES ASSOCIATION", + [3]byte{0, 80, 105}: "PixStream Incorporated", + [3]byte{0, 80, 106}: "EDEVA, INC.", + [3]byte{0, 80, 107}: "SPX-ATEG", + [3]byte{0, 80, 108}: "Beijer Electronics Products AB", + [3]byte{0, 80, 109}: "VIDEOJET SYSTEMS", + [3]byte{0, 80, 110}: "CORDER ENGINEERING CORPORATION", + [3]byte{0, 80, 111}: "G-CONNECT", + [3]byte{0, 80, 112}: "CHAINTECH COMPUTER CO., LTD.", + [3]byte{0, 80, 113}: "AIWA CO., LTD.", + [3]byte{0, 80, 114}: "CORVIS CORPORATION", + [3]byte{0, 80, 115}: "Cisco Systems, Inc", + [3]byte{0, 80, 116}: "ADVANCED HI-TECH CORP.", + [3]byte{0, 80, 117}: "KESTREL SOLUTIONS", + [3]byte{0, 80, 118}: "IBM Corp", + [3]byte{0, 80, 119}: "PROLIFIC TECHNOLOGY, INC.", + [3]byte{0, 80, 120}: "MEGATON HOUSE, LTD.", + [3]byte{0, 80, 121}: "Private", + [3]byte{0, 80, 122}: "XPEED, INC.", + [3]byte{0, 80, 123}: "MERLOT COMMUNICATIONS", + [3]byte{0, 80, 124}: "VIDEOCON AG", + [3]byte{0, 80, 125}: "IFP", + [3]byte{0, 80, 126}: "NEWER TECHNOLOGY", + [3]byte{0, 80, 127}: "DrayTek Corp.", + [3]byte{0, 80, 128}: "Cisco Systems, Inc", + [3]byte{0, 80, 129}: "MURATA MACHINERY, LTD.", + [3]byte{0, 80, 130}: "FORESSON CORPORATION", + [3]byte{0, 80, 131}: "GILBARCO, INC.", + [3]byte{0, 80, 132}: "ATL PRODUCTS", + [3]byte{0, 80, 134}: "TELKOM SA, LTD.", + [3]byte{0, 80, 135}: "TERASAKI ELECTRIC CO., LTD.", + [3]byte{0, 80, 136}: "AMANO CORPORATION", + [3]byte{0, 80, 137}: "SAFETY MANAGEMENT SYSTEMS", + [3]byte{0, 80, 139}: "Hewlett Packard", + [3]byte{0, 80, 140}: "RSI SYSTEMS", + [3]byte{0, 80, 141}: "ABIT COMPUTER CORPORATION", + [3]byte{0, 80, 142}: "OPTIMATION, INC.", + [3]byte{0, 80, 143}: "ASITA TECHNOLOGIES INT'L LTD.", + [3]byte{0, 80, 144}: "DCTRI", + [3]byte{0, 80, 145}: "NETACCESS, INC.", + [3]byte{0, 80, 146}: "Rigaku Corporation Osaka Plant", + [3]byte{0, 80, 147}: "BOEING", + [3]byte{0, 80, 148}: "ARRIS Group, Inc.", + [3]byte{0, 80, 149}: "PERACOM NETWORKS", + [3]byte{0, 80, 150}: "SALIX TECHNOLOGIES, INC.", + [3]byte{0, 80, 151}: "MMC-EMBEDDED COMPUTERTECHNIK GmbH", + [3]byte{0, 80, 152}: "GLOBALOOP, LTD.", + [3]byte{0, 80, 153}: "3COM EUROPE, LTD.", + [3]byte{0, 80, 154}: "TAG ELECTRONIC SYSTEMS", + [3]byte{0, 80, 155}: "SWITCHCORE AB", + [3]byte{0, 80, 156}: "BETA RESEARCH", + [3]byte{0, 80, 157}: "THE INDUSTREE B.V.", + [3]byte{0, 80, 158}: "Les Technologies SoftAcoustik Inc.", + [3]byte{0, 80, 159}: "HORIZON COMPUTER", + [3]byte{0, 80, 160}: "DELTA COMPUTER SYSTEMS, INC.", + [3]byte{0, 80, 161}: "CARLO GAVAZZI, INC.", + [3]byte{0, 80, 162}: "Cisco Systems, Inc", + [3]byte{0, 80, 163}: "TransMedia Communications, Inc.", + [3]byte{0, 80, 164}: "IO TECH, INC.", + [3]byte{0, 80, 165}: "CAPITOL BUSINESS SYSTEMS, LTD.", + [3]byte{0, 80, 166}: "OPTRONICS", + [3]byte{0, 80, 167}: "Cisco Systems, Inc", + [3]byte{0, 80, 168}: "OpenCon Systems, Inc.", + [3]byte{0, 80, 169}: "MOLDAT WIRELESS TECHNOLGIES", + [3]byte{0, 80, 170}: "KONICA MINOLTA HOLDINGS, INC.", + [3]byte{0, 80, 171}: "NALTEC, Inc.", + [3]byte{0, 80, 172}: "MAPLE COMPUTER CORPORATION", + [3]byte{0, 80, 173}: "CommUnique Wireless Corp.", + [3]byte{0, 80, 174}: "FDK Co., Ltd", + [3]byte{0, 80, 175}: "INTERGON, INC.", + [3]byte{0, 80, 176}: "TECHNOLOGY ATLANTA CORPORATION", + [3]byte{0, 80, 177}: "GIDDINGS & LEWIS", + [3]byte{0, 80, 178}: "BRODEL GmbH", + [3]byte{0, 80, 179}: "VOICEBOARD CORPORATION", + [3]byte{0, 80, 180}: "SATCHWELL CONTROL SYSTEMS, LTD", + [3]byte{0, 80, 181}: "FICHET-BAUCHE", + [3]byte{0, 80, 182}: "GOOD WAY IND. CO., LTD.", + [3]byte{0, 80, 183}: "BOSER TECHNOLOGY CO., LTD.", + [3]byte{0, 80, 184}: "INOVA COMPUTERS GMBH & CO. KG", + [3]byte{0, 80, 185}: "XITRON TECHNOLOGIES, INC.", + [3]byte{0, 80, 186}: "D-Link Corporation", + [3]byte{0, 80, 187}: "CMS TECHNOLOGIES", + [3]byte{0, 80, 188}: "HAMMER STORAGE SOLUTIONS", + [3]byte{0, 80, 189}: "Cisco Systems, Inc", + [3]byte{0, 80, 190}: "FAST MULTIMEDIA AG", + [3]byte{0, 80, 191}: "Metalligence Technology Corp.", + [3]byte{0, 80, 192}: "GATAN, INC.", + [3]byte{0, 80, 193}: "GEMFLEX NETWORKS, LTD.", + [3]byte{0, 80, 194}: "IEEE Registration Authority", + [3]byte{0, 80, 196}: "IMD", + [3]byte{0, 80, 197}: "ADS Technologies, Inc", + [3]byte{0, 80, 198}: "LOOP TELECOMMUNICATION INTERNATIONAL, INC.", + [3]byte{0, 80, 199}: "Private", + [3]byte{0, 80, 200}: "Addonics Technologies, Inc.", + [3]byte{0, 80, 201}: "MASPRO DENKOH CORP.", + [3]byte{0, 80, 202}: "NET TO NET TECHNOLOGIES", + [3]byte{0, 80, 203}: "JETTER", + [3]byte{0, 80, 204}: "XYRATEX", + [3]byte{0, 80, 205}: "DIGIANSWER A/S", + [3]byte{0, 80, 206}: "LG INTERNATIONAL CORP.", + [3]byte{0, 80, 207}: "VANLINK COMMUNICATION TECHNOLOGY RESEARCH INSTITUTE", + [3]byte{0, 80, 208}: "MINERVA SYSTEMS", + [3]byte{0, 80, 209}: "Cisco Systems, Inc", + [3]byte{0, 80, 210}: "CMC Electronics Inc", + [3]byte{0, 80, 211}: "DIGITAL AUDIO PROCESSING PTY. LTD.", + [3]byte{0, 80, 212}: "JOOHONG INFORMATION &", + [3]byte{0, 80, 213}: "AD SYSTEMS CORP.", + [3]byte{0, 80, 214}: "ATLAS COPCO TOOLS AB", + [3]byte{0, 80, 215}: "TELSTRAT", + [3]byte{0, 80, 216}: "UNICORN COMPUTER CORP.", + [3]byte{0, 80, 217}: "ENGETRON-ENGENHARIA ELETRONICA IND. e COM. LTDA", + [3]byte{0, 80, 218}: "3COM CORPORATION", + [3]byte{0, 80, 219}: "CONTEMPORARY CONTROL", + [3]byte{0, 80, 220}: "TAS TELEFONBAU A. SCHWABE GMBH & CO. KG", + [3]byte{0, 80, 221}: "SERRA SOLDADURA, S.A.", + [3]byte{0, 80, 222}: "SIGNUM SYSTEMS CORP.", + [3]byte{0, 80, 223}: "AirFiber, Inc.", + [3]byte{0, 80, 225}: "NS TECH ELECTRONICS SDN BHD", + [3]byte{0, 80, 226}: "Cisco Systems, Inc", + [3]byte{0, 80, 227}: "ARRIS Group, Inc.", + [3]byte{0, 80, 228}: "Apple, Inc.", + [3]byte{0, 80, 230}: "HAKUSAN CORPORATION", + [3]byte{0, 80, 231}: "PARADISE INNOVATIONS (ASIA)", + [3]byte{0, 80, 232}: "NOMADIX INC.", + [3]byte{0, 80, 234}: "XEL COMMUNICATIONS, INC.", + [3]byte{0, 80, 235}: "ALPHA-TOP CORPORATION", + [3]byte{0, 80, 236}: "OLICOM A/S", + [3]byte{0, 80, 237}: "ANDA NETWORKS", + [3]byte{0, 80, 238}: "TEK DIGITEL CORPORATION", + [3]byte{0, 80, 239}: "SPE Systemhaus GmbH", + [3]byte{0, 80, 240}: "Cisco Systems, Inc", + [3]byte{0, 80, 241}: "Intel Corporation", + [3]byte{0, 80, 242}: "MICROSOFT CORP.", + [3]byte{0, 80, 243}: "GLOBAL NET INFORMATION CO., Ltd.", + [3]byte{0, 80, 244}: "SIGMATEK GMBH & CO. KG", + [3]byte{0, 80, 246}: "PAN-INTERNATIONAL INDUSTRIAL CORP.", + [3]byte{0, 80, 247}: "VENTURE MANUFACTURING (SINGAPORE) LTD.", + [3]byte{0, 80, 248}: "ENTREGA TECHNOLOGIES, INC.", + [3]byte{0, 80, 249}: "Sensormatic Electronics LLC", + [3]byte{0, 80, 250}: "OXTEL, LTD.", + [3]byte{0, 80, 251}: "VSK ELECTRONICS", + [3]byte{0, 80, 252}: "Edimax Technology Co. Ltd.", + [3]byte{0, 80, 253}: "VISIONCOMM CO., LTD.", + [3]byte{0, 80, 254}: "PCTVnet ASA", + [3]byte{0, 80, 255}: "HAKKO ELECTRONICS CO., LTD.", + [3]byte{0, 82, 24}: "Wuxi Keboda Electron Co.Ltd", + [3]byte{0, 84, 159}: "Avaya Inc", + [3]byte{0, 84, 175}: "Continental Automotive Systems Inc.", + [3]byte{0, 84, 189}: "Swelaser AB", + [3]byte{0, 85, 218}: "IEEE Registration Authority", + [3]byte{0, 86, 43}: "Cisco Systems, Inc", + [3]byte{0, 86, 205}: "Apple, Inc.", + [3]byte{0, 87, 210}: "Cisco Systems, Inc", + [3]byte{0, 89, 7}: "LenovoEMC Products USA, LLC", + [3]byte{0, 89, 121}: "Networked Energy Services", + [3]byte{0, 89, 172}: "KPN. B.V.", + [3]byte{0, 89, 220}: "Cisco Systems, Inc", + [3]byte{0, 90, 19}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 90, 57}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{0, 91, 161}: "shanghai huayuan chuangxin software CO., LTD.", + [3]byte{0, 92, 177}: "Gospell DIGITAL TECHNOLOGY CO., LTD", + [3]byte{0, 93, 3}: "Xilinx, Inc", + [3]byte{0, 95, 134}: "Cisco Systems, Inc", + [3]byte{0, 96, 0}: "XYCOM INC.", + [3]byte{0, 96, 1}: "InnoSys, Inc.", + [3]byte{0, 96, 2}: "SCREEN SUBTITLING SYSTEMS, LTD", + [3]byte{0, 96, 3}: "TERAOKA WEIGH SYSTEM PTE, LTD.", + [3]byte{0, 96, 4}: "COMPUTADORES MODULARES SA", + [3]byte{0, 96, 5}: "FEEDBACK DATA LTD.", + [3]byte{0, 96, 6}: "SOTEC CO., LTD", + [3]byte{0, 96, 7}: "ACRES GAMING, INC.", + [3]byte{0, 96, 8}: "3COM CORPORATION", + [3]byte{0, 96, 9}: "Cisco Systems, Inc", + [3]byte{0, 96, 10}: "SORD COMPUTER CORPORATION", + [3]byte{0, 96, 11}: "LOGWARE GmbH", + [3]byte{0, 96, 12}: "Eurotech Inc.", + [3]byte{0, 96, 13}: "Digital Logic GmbH", + [3]byte{0, 96, 14}: "WAVENET INTERNATIONAL, INC.", + [3]byte{0, 96, 15}: "Westell Technologies Inc.", + [3]byte{0, 96, 16}: "NETWORK MACHINES, INC.", + [3]byte{0, 96, 17}: "CRYSTAL SEMICONDUCTOR CORP.", + [3]byte{0, 96, 18}: "POWER COMPUTING CORPORATION", + [3]byte{0, 96, 19}: "NETSTAL MASCHINEN AG", + [3]byte{0, 96, 20}: "EDEC CO., LTD.", + [3]byte{0, 96, 21}: "NET2NET CORPORATION", + [3]byte{0, 96, 22}: "CLARIION", + [3]byte{0, 96, 23}: "TOKIMEC INC.", + [3]byte{0, 96, 24}: "STELLAR ONE CORPORATION", + [3]byte{0, 96, 25}: "Roche Diagnostics", + [3]byte{0, 96, 26}: "KEITHLEY INSTRUMENTS", + [3]byte{0, 96, 27}: "MESA ELECTRONICS", + [3]byte{0, 96, 28}: "TELXON CORPORATION", + [3]byte{0, 96, 29}: "LUCENT TECHNOLOGIES", + [3]byte{0, 96, 30}: "SOFTLAB, INC.", + [3]byte{0, 96, 31}: "STALLION TECHNOLOGIES", + [3]byte{0, 96, 32}: "PIVOTAL NETWORKING, INC.", + [3]byte{0, 96, 33}: "DSC CORPORATION", + [3]byte{0, 96, 34}: "VICOM SYSTEMS, INC.", + [3]byte{0, 96, 35}: "PERICOM SEMICONDUCTOR CORP.", + [3]byte{0, 96, 36}: "GRADIENT TECHNOLOGIES, INC.", + [3]byte{0, 96, 37}: "ACTIVE IMAGING PLC", + [3]byte{0, 96, 38}: "VIKING Modular Solutions", + [3]byte{0, 96, 39}: "Superior Modular Products", + [3]byte{0, 96, 40}: "MACROVISION CORPORATION", + [3]byte{0, 96, 41}: "CARY PERIPHERALS INC.", + [3]byte{0, 96, 42}: "SYMICRON COMPUTER COMMUNICATIONS, LTD.", + [3]byte{0, 96, 43}: "PEAK AUDIO", + [3]byte{0, 96, 44}: "LINX Data Terminals, Inc.", + [3]byte{0, 96, 45}: "ALERTON TECHNOLOGIES, INC.", + [3]byte{0, 96, 46}: "CYCLADES CORPORATION", + [3]byte{0, 96, 47}: "Cisco Systems, Inc", + [3]byte{0, 96, 48}: "VILLAGE TRONIC ENTWICKLUNG", + [3]byte{0, 96, 49}: "HRK SYSTEMS", + [3]byte{0, 96, 50}: "I-CUBE, INC.", + [3]byte{0, 96, 51}: "ACUITY IMAGING, INC.", + [3]byte{0, 96, 52}: "ROBERT BOSCH GmbH", + [3]byte{0, 96, 53}: "DALLAS SEMICONDUCTOR, INC.", + [3]byte{0, 96, 54}: "AIT Austrian Institute of Technology GmbH", + [3]byte{0, 96, 55}: "NXP Semiconductors", + [3]byte{0, 96, 56}: "Nortel Networks", + [3]byte{0, 96, 57}: "SanCom Technology, Inc.", + [3]byte{0, 96, 58}: "QUICK CONTROLS LTD.", + [3]byte{0, 96, 59}: "AMTEC spa", + [3]byte{0, 96, 60}: "HAGIWARA SYS-COM CO., LTD.", + [3]byte{0, 96, 61}: "3CX", + [3]byte{0, 96, 62}: "Cisco Systems, Inc", + [3]byte{0, 96, 63}: "PATAPSCO DESIGNS", + [3]byte{0, 96, 64}: "NETRO CORP.", + [3]byte{0, 96, 65}: "Yokogawa Digital Computer Corporation", + [3]byte{0, 96, 66}: "TKS (USA), INC.", + [3]byte{0, 96, 67}: "iDirect, INC.", + [3]byte{0, 96, 68}: "LITTON/POLY-SCIENTIFIC", + [3]byte{0, 96, 69}: "PATHLIGHT TECHNOLOGIES", + [3]byte{0, 96, 70}: "VMETRO, INC.", + [3]byte{0, 96, 71}: "Cisco Systems, Inc", + [3]byte{0, 96, 72}: "EMC CORPORATION", + [3]byte{0, 96, 73}: "VINA TECHNOLOGIES", + [3]byte{0, 96, 74}: "SAIC IDEAS GROUP", + [3]byte{0, 96, 75}: "Safe-com GmbH & Co. KG", + [3]byte{0, 96, 76}: "Sagemcom Broadband SAS", + [3]byte{0, 96, 77}: "MMC NETWORKS, INC.", + [3]byte{0, 96, 78}: "CYCLE COMPUTER CORPORATION, INC.", + [3]byte{0, 96, 79}: "Tattile SRL ", + [3]byte{0, 96, 80}: "INTERNIX INC.", + [3]byte{0, 96, 81}: "QUALITY SEMICONDUCTOR", + [3]byte{0, 96, 82}: "PERIPHERALS ENTERPRISE CO., Ltd.", + [3]byte{0, 96, 83}: "TOYODA MACHINE WORKS, LTD.", + [3]byte{0, 96, 84}: "CONTROLWARE GMBH", + [3]byte{0, 96, 85}: "CORNELL UNIVERSITY", + [3]byte{0, 96, 86}: "NETWORK TOOLS, INC.", + [3]byte{0, 96, 87}: "Murata Manufacturing Co., Ltd.", + [3]byte{0, 96, 88}: "COPPER MOUNTAIN COMMUNICATIONS, INC.", + [3]byte{0, 96, 89}: "TECHNICAL COMMUNICATIONS CORP.", + [3]byte{0, 96, 90}: "CELCORE, INC.", + [3]byte{0, 96, 91}: "IntraServer Technology, Inc.", + [3]byte{0, 96, 92}: "Cisco Systems, Inc", + [3]byte{0, 96, 93}: "SCANIVALVE CORP.", + [3]byte{0, 96, 94}: "LIBERTY TECHNOLOGY NETWORKING", + [3]byte{0, 96, 95}: "NIPPON UNISOFT CORPORATION", + [3]byte{0, 96, 96}: "Data Innovations North America", + [3]byte{0, 96, 97}: "WHISTLE COMMUNICATIONS CORP.", + [3]byte{0, 96, 98}: "TELESYNC, INC.", + [3]byte{0, 96, 99}: "PSION DACOM PLC.", + [3]byte{0, 96, 100}: "NETCOMM LIMITED", + [3]byte{0, 96, 101}: "BERNECKER & RAINER INDUSTRIE-ELEKTRONIC GmbH", + [3]byte{0, 96, 102}: "LACROIX Trafic", + [3]byte{0, 96, 103}: "ACER NETXUS INC.", + [3]byte{0, 96, 104}: "Dialogic Corporation", + [3]byte{0, 96, 105}: "Brocade Communications Systems, Inc.", + [3]byte{0, 96, 106}: "MITSUBISHI WIRELESS COMMUNICATIONS. INC.", + [3]byte{0, 96, 107}: "Synclayer Inc.", + [3]byte{0, 96, 108}: "ARESCOM", + [3]byte{0, 96, 109}: "DIGITAL EQUIPMENT CORP.", + [3]byte{0, 96, 110}: "DAVICOM SEMICONDUCTOR, INC.", + [3]byte{0, 96, 111}: "CLARION CORPORATION OF AMERICA", + [3]byte{0, 96, 112}: "Cisco Systems, Inc", + [3]byte{0, 96, 113}: "MIDAS LAB, INC.", + [3]byte{0, 96, 114}: "VXL INSTRUMENTS, LIMITED", + [3]byte{0, 96, 115}: "REDCREEK COMMUNICATIONS, INC.", + [3]byte{0, 96, 116}: "QSC LLC", + [3]byte{0, 96, 117}: "PENTEK, INC.", + [3]byte{0, 96, 118}: "SCHLUMBERGER TECHNOLOGIES RETAIL PETROLEUM SYSTEMS", + [3]byte{0, 96, 119}: "PRISA NETWORKS", + [3]byte{0, 96, 120}: "POWER MEASUREMENT LTD.", + [3]byte{0, 96, 121}: "Mainstream Data, Inc.", + [3]byte{0, 96, 122}: "DVS GMBH", + [3]byte{0, 96, 123}: "FORE SYSTEMS, INC.", + [3]byte{0, 96, 124}: "WaveAccess, Ltd.", + [3]byte{0, 96, 125}: "SENTIENT NETWORKS INC.", + [3]byte{0, 96, 126}: "GIGALABS, INC.", + [3]byte{0, 96, 127}: "AURORA TECHNOLOGIES, INC.", + [3]byte{0, 96, 128}: "MICROTRONIX DATACOM LTD.", + [3]byte{0, 96, 129}: "TV/COM INTERNATIONAL", + [3]byte{0, 96, 130}: "NOVALINK TECHNOLOGIES, INC.", + [3]byte{0, 96, 131}: "Cisco Systems, Inc", + [3]byte{0, 96, 132}: "DIGITAL VIDEO", + [3]byte{0, 96, 133}: "Storage Concepts", + [3]byte{0, 96, 134}: "LOGIC REPLACEMENT TECH. LTD.", + [3]byte{0, 96, 135}: "KANSAI ELECTRIC CO., LTD.", + [3]byte{0, 96, 136}: "WHITE MOUNTAIN DSP, INC.", + [3]byte{0, 96, 137}: "XATA", + [3]byte{0, 96, 138}: "CITADEL COMPUTER", + [3]byte{0, 96, 139}: "ConferTech International", + [3]byte{0, 96, 140}: "3COM CORPORATION", + [3]byte{0, 96, 141}: "UNIPULSE CORP.", + [3]byte{0, 96, 142}: "HE ELECTRONICS, TECHNOLOGIE & SYSTEMTECHNIK GmbH", + [3]byte{0, 96, 143}: "TEKRAM TECHNOLOGY CO., LTD.", + [3]byte{0, 96, 144}: "Artiza Networks Inc", + [3]byte{0, 96, 145}: "FIRST PACIFIC NETWORKS, INC.", + [3]byte{0, 96, 146}: "MICRO/SYS, INC.", + [3]byte{0, 96, 147}: "VARIAN", + [3]byte{0, 96, 148}: "IBM Corp", + [3]byte{0, 96, 149}: "ACCU-TIME SYSTEMS, INC.", + [3]byte{0, 96, 150}: "T.S. MICROTECH INC.", + [3]byte{0, 96, 151}: "3COM CORPORATION", + [3]byte{0, 96, 152}: "HT COMMUNICATIONS", + [3]byte{0, 96, 153}: "SBE, Inc.", + [3]byte{0, 96, 154}: "NJK TECHNO CO.", + [3]byte{0, 96, 155}: "AstroNova, Inc", + [3]byte{0, 96, 156}: "Perkin-Elmer Incorporated", + [3]byte{0, 96, 157}: "PMI FOOD EQUIPMENT GROUP", + [3]byte{0, 96, 158}: "ASC X3 - INFORMATION TECHNOLOGY STANDARDS SECRETARIATS", + [3]byte{0, 96, 159}: "PHAST CORPORATION", + [3]byte{0, 96, 160}: "SWITCHED NETWORK TECHNOLOGIES, INC.", + [3]byte{0, 96, 161}: "VPNet, Inc.", + [3]byte{0, 96, 162}: "NIHON UNISYS LIMITED CO.", + [3]byte{0, 96, 163}: "CONTINUUM TECHNOLOGY CORP.", + [3]byte{0, 96, 164}: "GEW Technologies (PTY)Ltd", + [3]byte{0, 96, 165}: "PERFORMANCE TELECOM CORP.", + [3]byte{0, 96, 166}: "PARTICLE MEASURING SYSTEMS", + [3]byte{0, 96, 167}: "MICROSENS GmbH & CO. KG", + [3]byte{0, 96, 168}: "TIDOMAT AB", + [3]byte{0, 96, 169}: "GESYTEC MBH", + [3]byte{0, 96, 170}: "INTELLIGENT DEVICES INC. (IDI)", + [3]byte{0, 96, 171}: "LARSCOM INCORPORATED", + [3]byte{0, 96, 172}: "RESILIENCE CORPORATION", + [3]byte{0, 96, 173}: "MegaChips Corporation", + [3]byte{0, 96, 174}: "TRIO INFORMATION SYSTEMS AB", + [3]byte{0, 96, 175}: "PACIFIC MICRO DATA, INC.", + [3]byte{0, 96, 176}: "Hewlett Packard", + [3]byte{0, 96, 177}: "Input/Output, Inc.", + [3]byte{0, 96, 178}: "PROCESS CONTROL CORP.", + [3]byte{0, 96, 179}: "Z-COM, INC.", + [3]byte{0, 96, 180}: "GLENAYRE R&D INC.", + [3]byte{0, 96, 181}: "KEBA GmbH", + [3]byte{0, 96, 182}: "LAND COMPUTER CO., LTD.", + [3]byte{0, 96, 183}: "CHANNELMATIC, INC.", + [3]byte{0, 96, 184}: "CORELIS Inc.", + [3]byte{0, 96, 185}: "NEC Platforms, Ltd", + [3]byte{0, 96, 186}: "SAHARA NETWORKS, INC.", + [3]byte{0, 96, 187}: "Cabletron Systems, Inc.", + [3]byte{0, 96, 188}: "KeunYoung Electronics & Communication Co., Ltd.", + [3]byte{0, 96, 189}: "Enginuity Communications", + [3]byte{0, 96, 190}: "WEBTRONICS", + [3]byte{0, 96, 191}: "MACRAIGOR SYSTEMS, INC.", + [3]byte{0, 96, 192}: "Nera Networks AS", + [3]byte{0, 96, 193}: "WaveSpan Corporation", + [3]byte{0, 96, 194}: "MPL AG", + [3]byte{0, 96, 195}: "NETVISION CORPORATION", + [3]byte{0, 96, 196}: "SOLITON SYSTEMS K.K.", + [3]byte{0, 96, 197}: "ANCOT CORP.", + [3]byte{0, 96, 198}: "DCS AG", + [3]byte{0, 96, 199}: "AMATI COMMUNICATIONS CORP.", + [3]byte{0, 96, 200}: "KUKA WELDING SYSTEMS & ROBOTS", + [3]byte{0, 96, 201}: "ControlNet, Inc.", + [3]byte{0, 96, 202}: "HARMONIC SYSTEMS INCORPORATED", + [3]byte{0, 96, 203}: "HITACHI ZOSEN CORPORATION", + [3]byte{0, 96, 204}: "EMTRAK, INCORPORATED", + [3]byte{0, 96, 205}: "VideoServer, Inc.", + [3]byte{0, 96, 206}: "ACCLAIM COMMUNICATIONS", + [3]byte{0, 96, 207}: "ALTEON NETWORKS, INC.", + [3]byte{0, 96, 208}: "SNMP RESEARCH INCORPORATED", + [3]byte{0, 96, 209}: "CASCADE COMMUNICATIONS", + [3]byte{0, 96, 210}: "LUCENT TECHNOLOGIES TAIWAN TELECOMMUNICATIONS CO., LTD.", + [3]byte{0, 96, 211}: "AT&T", + [3]byte{0, 96, 212}: "ELDAT COMMUNICATION LTD.", + [3]byte{0, 96, 213}: "AMADA MIYACHI Co., Ltd", + [3]byte{0, 96, 214}: "NovAtel Inc.", + [3]byte{0, 96, 215}: "ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE (EPFL)", + [3]byte{0, 96, 216}: "ELMIC SYSTEMS, INC.", + [3]byte{0, 96, 217}: "TRANSYS NETWORKS INC.", + [3]byte{0, 96, 218}: "Red Lion Controls, LP", + [3]byte{0, 96, 219}: "NTP ELEKTRONIK A/S", + [3]byte{0, 96, 220}: "NEC Magnus Communications,Ltd.", + [3]byte{0, 96, 221}: "MYRICOM, INC.", + [3]byte{0, 96, 222}: "Kayser-Threde GmbH", + [3]byte{0, 96, 223}: "Brocade Communications Systems, Inc.", + [3]byte{0, 96, 224}: "AXIOM TECHNOLOGY CO., LTD.", + [3]byte{0, 96, 225}: "ORCKIT COMMUNICATIONS LTD.", + [3]byte{0, 96, 226}: "QUEST ENGINEERING & DEVELOPMENT", + [3]byte{0, 96, 227}: "ARBIN INSTRUMENTS", + [3]byte{0, 96, 228}: "COMPUSERVE, INC.", + [3]byte{0, 96, 229}: "FUJI AUTOMATION CO., LTD.", + [3]byte{0, 96, 230}: "SHOMITI SYSTEMS INCORPORATED", + [3]byte{0, 96, 231}: "RANDATA", + [3]byte{0, 96, 232}: "HITACHI COMPUTER PRODUCTS (AMERICA), INC.", + [3]byte{0, 96, 233}: "ATOP TECHNOLOGIES, INC.", + [3]byte{0, 96, 234}: "StreamLogic", + [3]byte{0, 96, 235}: "FOURTHTRACK SYSTEMS", + [3]byte{0, 96, 236}: "HERMARY OPTO ELECTRONICS INC.", + [3]byte{0, 96, 237}: "RICARDO TEST AUTOMATION LTD.", + [3]byte{0, 96, 238}: "APOLLO", + [3]byte{0, 96, 239}: "FLYTECH TECHNOLOGY CO., LTD.", + [3]byte{0, 96, 240}: "JOHNSON & JOHNSON MEDICAL, INC", + [3]byte{0, 96, 241}: "EXP COMPUTER, INC.", + [3]byte{0, 96, 242}: "LASERGRAPHICS, INC.", + [3]byte{0, 96, 243}: "Performance Analysis Broadband, Spirent plc", + [3]byte{0, 96, 244}: "ADVANCED COMPUTER SOLUTIONS, Inc.", + [3]byte{0, 96, 245}: "ICON WEST, INC.", + [3]byte{0, 96, 246}: "NEXTEST COMMUNICATIONS PRODUCTS, INC.", + [3]byte{0, 96, 247}: "DATAFUSION SYSTEMS", + [3]byte{0, 96, 248}: "Loran International Technologies Inc.", + [3]byte{0, 96, 249}: "DIAMOND LANE COMMUNICATIONS", + [3]byte{0, 96, 250}: "EDUCATIONAL TECHNOLOGY RESOURCES, INC.", + [3]byte{0, 96, 251}: "PACKETEER, INC.", + [3]byte{0, 96, 252}: "CONSERVATION THROUGH INNOVATION LTD.", + [3]byte{0, 96, 253}: "NetICs, Inc.", + [3]byte{0, 96, 254}: "LYNX SYSTEM DEVELOPERS, INC.", + [3]byte{0, 96, 255}: "QuVis, Inc.", + [3]byte{0, 97, 113}: "Apple, Inc.", + [3]byte{0, 98, 236}: "Cisco Systems, Inc", + [3]byte{0, 100, 64}: "Cisco Systems, Inc", + [3]byte{0, 100, 166}: "Maquet CardioVascular", + [3]byte{0, 102, 75}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 107, 142}: "Shanghai Feixun Communication Co.,Ltd.", + [3]byte{0, 107, 158}: "Vizio, Inc", + [3]byte{0, 107, 160}: "SHENZHEN UNIVERSAL INTELLISYS PTE LTD", + [3]byte{0, 107, 241}: "Cisco Systems, Inc", + [3]byte{0, 108, 188}: "Cisco Systems, Inc", + [3]byte{0, 108, 253}: "Sichuan Changhong Electric Ltd.", + [3]byte{0, 109, 82}: "Apple, Inc.", + [3]byte{0, 109, 251}: "Vutrix Technologies Ltd", + [3]byte{0, 111, 100}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 112, 176}: "M/A-COM INC. COMPANIES", + [3]byte{0, 112, 179}: "DATA RECALL LTD.", + [3]byte{0, 113, 194}: "PEGATRON CORPORATION", + [3]byte{0, 113, 204}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{0, 115, 141}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{0, 115, 224}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 116, 156}: "Ruijie Networks Co.,LTD", + [3]byte{0, 117, 50}: "INID BV", + [3]byte{0, 117, 225}: "Ampt, LLC", + [3]byte{0, 118, 134}: "Cisco Systems, Inc", + [3]byte{0, 120, 136}: "Cisco Systems, Inc", + [3]byte{0, 120, 158}: "Sagemcom Broadband SAS", + [3]byte{0, 120, 205}: "Ignition Design Labs", + [3]byte{0, 123, 24}: "SENTRY Co., LTD.", + [3]byte{0, 125, 250}: "Volkswagen Group of America", + [3]byte{0, 126, 86}: "China Dragon Technology Limited", + [3]byte{0, 127, 40}: "Actiontec Electronics, Inc", + [3]byte{0, 128, 0}: "MULTITECH SYSTEMS, INC.", + [3]byte{0, 128, 1}: "PERIPHONICS CORPORATION", + [3]byte{0, 128, 2}: "SATELCOM (UK) LTD", + [3]byte{0, 128, 3}: "HYTEC ELECTRONICS LTD.", + [3]byte{0, 128, 4}: "ANTLOW COMMUNICATIONS, LTD.", + [3]byte{0, 128, 5}: "CACTUS COMPUTER INC.", + [3]byte{0, 128, 6}: "COMPUADD CORPORATION", + [3]byte{0, 128, 7}: "DLOG NC-SYSTEME", + [3]byte{0, 128, 8}: "DYNATECH COMPUTER SYSTEMS", + [3]byte{0, 128, 9}: "JUPITER SYSTEMS, INC.", + [3]byte{0, 128, 10}: "JAPAN COMPUTER CORP.", + [3]byte{0, 128, 11}: "CSK CORPORATION", + [3]byte{0, 128, 12}: "VIDECOM LIMITED", + [3]byte{0, 128, 13}: "VOSSWINKEL F.U.", + [3]byte{0, 128, 14}: "ATLANTIX CORPORATION", + [3]byte{0, 128, 15}: "STANDARD MICROSYSTEMS", + [3]byte{0, 128, 16}: "COMMODORE INTERNATIONAL", + [3]byte{0, 128, 17}: "DIGITAL SYSTEMS INT'L. INC.", + [3]byte{0, 128, 18}: "INTEGRATED MEASUREMENT SYSTEMS", + [3]byte{0, 128, 19}: "THOMAS-CONRAD CORPORATION", + [3]byte{0, 128, 20}: "ESPRIT SYSTEMS", + [3]byte{0, 128, 21}: "SEIKO SYSTEMS, INC.", + [3]byte{0, 128, 22}: "WANDEL AND GOLTERMANN", + [3]byte{0, 128, 23}: "PFU LIMITED", + [3]byte{0, 128, 24}: "KOBE STEEL, LTD.", + [3]byte{0, 128, 25}: "DAYNA COMMUNICATIONS, INC.", + [3]byte{0, 128, 26}: "BELL ATLANTIC", + [3]byte{0, 128, 27}: "KODIAK TECHNOLOGY", + [3]byte{0, 128, 28}: "NEWPORT SYSTEMS SOLUTIONS", + [3]byte{0, 128, 29}: "INTEGRATED INFERENCE MACHINES", + [3]byte{0, 128, 30}: "XINETRON, INC.", + [3]byte{0, 128, 31}: "KRUPP ATLAS ELECTRONIK GMBH", + [3]byte{0, 128, 32}: "NETWORK PRODUCTS", + [3]byte{0, 128, 33}: "Alcatel Canada Inc.", + [3]byte{0, 128, 34}: "SCAN-OPTICS", + [3]byte{0, 128, 35}: "INTEGRATED BUSINESS NETWORKS", + [3]byte{0, 128, 36}: "KALPANA, INC.", + [3]byte{0, 128, 37}: "Telit Wireless Solutions GmbH", + [3]byte{0, 128, 38}: "NETWORK PRODUCTS CORPORATION", + [3]byte{0, 128, 39}: "ADAPTIVE SYSTEMS, INC.", + [3]byte{0, 128, 40}: "TRADPOST (HK) LTD", + [3]byte{0, 128, 41}: "EAGLE TECHNOLOGY, INC.", + [3]byte{0, 128, 42}: "TEST SYSTEMS & SIMULATIONS INC", + [3]byte{0, 128, 43}: "INTEGRATED MARKETING CO", + [3]byte{0, 128, 44}: "THE SAGE GROUP PLC", + [3]byte{0, 128, 45}: "XYLOGICS INC", + [3]byte{0, 128, 46}: "CASTLE ROCK COMPUTING", + [3]byte{0, 128, 47}: "NATIONAL INSTRUMENTS CORP.", + [3]byte{0, 128, 48}: "NEXUS ELECTRONICS", + [3]byte{0, 128, 49}: "BASYS, CORP.", + [3]byte{0, 128, 50}: "ACCESS CO., LTD.", + [3]byte{0, 128, 51}: "EMS Aviation, Inc.", + [3]byte{0, 128, 52}: "SMT GOUPIL", + [3]byte{0, 128, 53}: "TECHNOLOGY WORKS, INC.", + [3]byte{0, 128, 54}: "REFLEX MANUFACTURING SYSTEMS", + [3]byte{0, 128, 55}: "Ericsson Group", + [3]byte{0, 128, 56}: "DATA RESEARCH & APPLICATIONS", + [3]byte{0, 128, 57}: "ALCATEL STC AUSTRALIA", + [3]byte{0, 128, 58}: "VARITYPER, INC.", + [3]byte{0, 128, 59}: "APT COMMUNICATIONS, INC.", + [3]byte{0, 128, 60}: "TVS ELECTRONICS LTD", + [3]byte{0, 128, 61}: "SURIGIKEN CO., LTD.", + [3]byte{0, 128, 62}: "SYNERNETICS", + [3]byte{0, 128, 63}: "TATUNG COMPANY", + [3]byte{0, 128, 64}: "JOHN FLUKE MANUFACTURING CO.", + [3]byte{0, 128, 65}: "VEB KOMBINAT ROBOTRON", + [3]byte{0, 128, 66}: "Artesyn Embedded Technologies", + [3]byte{0, 128, 67}: "NETWORLD, INC.", + [3]byte{0, 128, 68}: "SYSTECH COMPUTER CORP.", + [3]byte{0, 128, 69}: "MATSUSHITA ELECTRIC IND. CO", + [3]byte{0, 128, 70}: "Tattile SRL ", + [3]byte{0, 128, 71}: "IN-NET CORP.", + [3]byte{0, 128, 72}: "COMPEX INCORPORATED", + [3]byte{0, 128, 73}: "NISSIN ELECTRIC CO., LTD.", + [3]byte{0, 128, 74}: "PRO-LOG", + [3]byte{0, 128, 75}: "EAGLE TECHNOLOGIES PTY.LTD.", + [3]byte{0, 128, 76}: "CONTEC CO., LTD.", + [3]byte{0, 128, 77}: "CYCLONE MICROSYSTEMS, INC.", + [3]byte{0, 128, 78}: "APEX COMPUTER COMPANY", + [3]byte{0, 128, 79}: "DAIKIN INDUSTRIES, LTD.", + [3]byte{0, 128, 80}: "ZIATECH CORPORATION", + [3]byte{0, 128, 81}: "FIBERMUX", + [3]byte{0, 128, 82}: "TECHNICALLY ELITE CONCEPTS", + [3]byte{0, 128, 83}: "INTELLICOM, INC.", + [3]byte{0, 128, 84}: "FRONTIER TECHNOLOGIES CORP.", + [3]byte{0, 128, 85}: "FERMILAB", + [3]byte{0, 128, 86}: "SPHINX Electronics GmbH & Co KG", + [3]byte{0, 128, 87}: "ADSOFT, LTD.", + [3]byte{0, 128, 88}: "PRINTER SYSTEMS CORP.", + [3]byte{0, 128, 89}: "STANLEY ELECTRIC CO., LTD", + [3]byte{0, 128, 90}: "TULIP COMPUTERS INTERNAT'L B.V", + [3]byte{0, 128, 91}: "CONDOR SYSTEMS, INC.", + [3]byte{0, 128, 92}: "AGILIS CORPORATION", + [3]byte{0, 128, 93}: "CANSTAR", + [3]byte{0, 128, 94}: "LSI LOGIC CORPORATION", + [3]byte{0, 128, 95}: "Hewlett Packard", + [3]byte{0, 128, 96}: "NETWORK INTERFACE CORPORATION", + [3]byte{0, 128, 97}: "LITTON SYSTEMS, INC.", + [3]byte{0, 128, 98}: "INTERFACE CO.", + [3]byte{0, 128, 99}: "Hirschmann Automation and Control GmbH", + [3]byte{0, 128, 100}: "WYSE TECHNOLOGY LLC", + [3]byte{0, 128, 101}: "CYBERGRAPHIC SYSTEMS PTY LTD.", + [3]byte{0, 128, 102}: "ARCOM CONTROL SYSTEMS, LTD.", + [3]byte{0, 128, 103}: "SQUARE D COMPANY", + [3]byte{0, 128, 104}: "YAMATECH SCIENTIFIC LTD.", + [3]byte{0, 128, 105}: "COMPUTONE SYSTEMS", + [3]byte{0, 128, 106}: "ERI (EMPAC RESEARCH INC.)", + [3]byte{0, 128, 107}: "SCHMID TELECOMMUNICATION", + [3]byte{0, 128, 108}: "CEGELEC PROJECTS LTD", + [3]byte{0, 128, 109}: "CENTURY SYSTEMS CORP.", + [3]byte{0, 128, 110}: "NIPPON STEEL CORPORATION", + [3]byte{0, 128, 111}: "ONELAN LTD.", + [3]byte{0, 128, 112}: "COMPUTADORAS MICRON", + [3]byte{0, 128, 113}: "SAI TECHNOLOGY", + [3]byte{0, 128, 114}: "MICROPLEX SYSTEMS LTD.", + [3]byte{0, 128, 115}: "DWB ASSOCIATES", + [3]byte{0, 128, 116}: "FISHER CONTROLS", + [3]byte{0, 128, 117}: "PARSYTEC GMBH", + [3]byte{0, 128, 118}: "MCNC", + [3]byte{0, 128, 119}: "Brother industries, LTD.", + [3]byte{0, 128, 120}: "PRACTICAL PERIPHERALS, INC.", + [3]byte{0, 128, 121}: "MICROBUS DESIGNS LTD.", + [3]byte{0, 128, 122}: "AITECH SYSTEMS LTD.", + [3]byte{0, 128, 123}: "ARTEL COMMUNICATIONS CORP.", + [3]byte{0, 128, 124}: "FIBERCOM, INC.", + [3]byte{0, 128, 125}: "EQUINOX SYSTEMS INC.", + [3]byte{0, 128, 126}: "SOUTHERN PACIFIC LTD.", + [3]byte{0, 128, 127}: "DY-4 INCORPORATED", + [3]byte{0, 128, 128}: "DATAMEDIA CORPORATION", + [3]byte{0, 128, 129}: "KENDALL SQUARE RESEARCH CORP.", + [3]byte{0, 128, 130}: "PEP MODULAR COMPUTERS GMBH", + [3]byte{0, 128, 131}: "AMDAHL", + [3]byte{0, 128, 132}: "THE CLOUD INC.", + [3]byte{0, 128, 133}: "H-THREE SYSTEMS CORPORATION", + [3]byte{0, 128, 134}: "COMPUTER GENERATION INC.", + [3]byte{0, 128, 135}: "OKI ELECTRIC INDUSTRY CO., LTD", + [3]byte{0, 128, 136}: "VICTOR COMPANY OF JAPAN, LTD.", + [3]byte{0, 128, 137}: "TECNETICS (PTY) LTD.", + [3]byte{0, 128, 138}: "SUMMIT MICROSYSTEMS CORP.", + [3]byte{0, 128, 139}: "DACOLL LIMITED", + [3]byte{0, 128, 140}: "NetScout Systems, Inc.", + [3]byte{0, 128, 141}: "WESTCOAST TECHNOLOGY B.V.", + [3]byte{0, 128, 142}: "RADSTONE TECHNOLOGY", + [3]byte{0, 128, 143}: "C. ITOH ELECTRONICS, INC.", + [3]byte{0, 128, 144}: "MICROTEK INTERNATIONAL, INC.", + [3]byte{0, 128, 145}: "TOKYO ELECTRIC CO.,LTD", + [3]byte{0, 128, 146}: "Silex Technology, Inc.", + [3]byte{0, 128, 147}: "XYRON CORPORATION", + [3]byte{0, 128, 148}: "ALFA LAVAL AUTOMATION AB", + [3]byte{0, 128, 149}: "BASIC MERTON HANDELSGES.M.B.H.", + [3]byte{0, 128, 150}: "HUMAN DESIGNED SYSTEMS, INC.", + [3]byte{0, 128, 151}: "CENTRALP AUTOMATISMES", + [3]byte{0, 128, 152}: "TDK CORPORATION", + [3]byte{0, 128, 153}: "Eaton Industries GmbH", + [3]byte{0, 128, 154}: "NOVUS NETWORKS LTD", + [3]byte{0, 128, 155}: "JUSTSYSTEM CORPORATION", + [3]byte{0, 128, 156}: "LUXCOM, INC.", + [3]byte{0, 128, 157}: "Commscraft Ltd.", + [3]byte{0, 128, 158}: "DATUS GMBH", + [3]byte{0, 128, 159}: "ALE International", + [3]byte{0, 128, 160}: "Hewlett Packard", + [3]byte{0, 128, 161}: "MICROTEST, INC.", + [3]byte{0, 128, 162}: "CREATIVE ELECTRONIC SYSTEMS", + [3]byte{0, 128, 163}: "Lantronix", + [3]byte{0, 128, 164}: "LIBERTY ELECTRONICS", + [3]byte{0, 128, 165}: "SPEED INTERNATIONAL", + [3]byte{0, 128, 166}: "REPUBLIC TECHNOLOGY, INC.", + [3]byte{0, 128, 167}: "Honeywell International Inc", + [3]byte{0, 128, 168}: "VITACOM CORPORATION", + [3]byte{0, 128, 169}: "CLEARPOINT RESEARCH", + [3]byte{0, 128, 170}: "MAXPEED", + [3]byte{0, 128, 171}: "DUKANE NETWORK INTEGRATION", + [3]byte{0, 128, 172}: "IMLOGIX, DIVISION OF GENESYS", + [3]byte{0, 128, 173}: "CNET TECHNOLOGY, INC.", + [3]byte{0, 128, 174}: "HUGHES NETWORK SYSTEMS", + [3]byte{0, 128, 175}: "ALLUMER CO., LTD.", + [3]byte{0, 128, 176}: "ADVANCED INFORMATION", + [3]byte{0, 128, 177}: "SOFTCOM A/S", + [3]byte{0, 128, 178}: "NETWORK EQUIPMENT TECHNOLOGIES", + [3]byte{0, 128, 179}: "AVAL DATA CORPORATION", + [3]byte{0, 128, 180}: "SOPHIA SYSTEMS", + [3]byte{0, 128, 181}: "UNITED NETWORKS INC.", + [3]byte{0, 128, 182}: "THEMIS COMPUTER", + [3]byte{0, 128, 183}: "STELLAR COMPUTER", + [3]byte{0, 128, 184}: "DMG MORI B.U.G. CO., LTD.", + [3]byte{0, 128, 185}: "ARCHE TECHNOLIGIES INC.", + [3]byte{0, 128, 186}: "SPECIALIX (ASIA) PTE, LTD", + [3]byte{0, 128, 187}: "HUGHES LAN SYSTEMS", + [3]byte{0, 128, 188}: "HITACHI ENGINEERING CO., LTD", + [3]byte{0, 128, 189}: "THE FURUKAWA ELECTRIC CO., LTD", + [3]byte{0, 128, 190}: "ARIES RESEARCH", + [3]byte{0, 128, 191}: "TAKAOKA ELECTRIC MFG. CO. LTD.", + [3]byte{0, 128, 192}: "PENRIL DATACOMM", + [3]byte{0, 128, 193}: "LANEX CORPORATION", + [3]byte{0, 128, 194}: "IEEE 802.1", + [3]byte{0, 128, 195}: "BICC INFORMATION SYSTEMS & SVC", + [3]byte{0, 128, 196}: "DOCUMENT TECHNOLOGIES, INC.", + [3]byte{0, 128, 197}: "NOVELLCO DE MEXICO", + [3]byte{0, 128, 198}: "NATIONAL DATACOMM CORPORATION", + [3]byte{0, 128, 199}: "XIRCOM", + [3]byte{0, 128, 200}: "D-LINK SYSTEMS, INC.", + [3]byte{0, 128, 201}: "ALBERTA MICROELECTRONIC CENTRE", + [3]byte{0, 128, 202}: "NETCOM RESEARCH INCORPORATED", + [3]byte{0, 128, 203}: "FALCO DATA PRODUCTS", + [3]byte{0, 128, 204}: "MICROWAVE BYPASS SYSTEMS", + [3]byte{0, 128, 205}: "MICRONICS COMPUTER, INC.", + [3]byte{0, 128, 206}: "BROADCAST TELEVISION SYSTEMS", + [3]byte{0, 128, 207}: "EMBEDDED PERFORMANCE INC.", + [3]byte{0, 128, 208}: "COMPUTER PERIPHERALS, INC.", + [3]byte{0, 128, 209}: "KIMTRON CORPORATION", + [3]byte{0, 128, 210}: "SHINNIHONDENKO CO., LTD.", + [3]byte{0, 128, 211}: "SHIVA CORP.", + [3]byte{0, 128, 212}: "CHASE RESEARCH LTD.", + [3]byte{0, 128, 213}: "CADRE TECHNOLOGIES", + [3]byte{0, 128, 214}: "NUVOTECH, INC.", + [3]byte{0, 128, 215}: "Fantum Engineering", + [3]byte{0, 128, 216}: "NETWORK PERIPHERALS INC.", + [3]byte{0, 128, 217}: "EMK Elektronik GmbH & Co. KG", + [3]byte{0, 128, 218}: "Bruel & Kjaer Sound & Vibration Measurement A/S", + [3]byte{0, 128, 219}: "GRAPHON CORPORATION", + [3]byte{0, 128, 220}: "PICKER INTERNATIONAL", + [3]byte{0, 128, 221}: "GMX INC/GIMIX", + [3]byte{0, 128, 222}: "GIPSI S.A.", + [3]byte{0, 128, 223}: "ADC CODENOLL TECHNOLOGY CORP.", + [3]byte{0, 128, 224}: "XTP SYSTEMS, INC.", + [3]byte{0, 128, 225}: "STMicroelectronics SRL", + [3]byte{0, 128, 226}: "T.D.I. CO., LTD.", + [3]byte{0, 128, 227}: "CORAL NETWORK CORPORATION", + [3]byte{0, 128, 228}: "NORTHWEST DIGITAL SYSTEMS, INC", + [3]byte{0, 128, 229}: "NetApp", + [3]byte{0, 128, 230}: "PEER NETWORKS, INC.", + [3]byte{0, 128, 231}: "DRS Technologies UK Limited", + [3]byte{0, 128, 232}: "CUMULUS CORPORATIION", + [3]byte{0, 128, 233}: "Madge Ltd.", + [3]byte{0, 128, 234}: "ADVA Optical Networking Ltd.", + [3]byte{0, 128, 235}: "COMPCONTROL B.V.", + [3]byte{0, 128, 236}: "SUPERCOMPUTING SOLUTIONS, INC.", + [3]byte{0, 128, 237}: "IQ TECHNOLOGIES, INC.", + [3]byte{0, 128, 238}: "THOMSON CSF", + [3]byte{0, 128, 239}: "RATIONAL", + [3]byte{0, 128, 240}: "Panasonic Communications Co., Ltd.", + [3]byte{0, 128, 241}: "OPUS SYSTEMS", + [3]byte{0, 128, 242}: "RAYCOM SYSTEMS INC", + [3]byte{0, 128, 243}: "SUN ELECTRONICS CORP.", + [3]byte{0, 128, 244}: "TELEMECANIQUE ELECTRIQUE", + [3]byte{0, 128, 245}: "Quantel Ltd", + [3]byte{0, 128, 246}: "SYNERGY MICROSYSTEMS", + [3]byte{0, 128, 247}: "Zenith Electronics Corporation", + [3]byte{0, 128, 248}: "MIZAR, INC.", + [3]byte{0, 128, 249}: "HEURIKON CORPORATION", + [3]byte{0, 128, 250}: "RWT GMBH", + [3]byte{0, 128, 251}: "BVM LIMITED", + [3]byte{0, 128, 252}: "AVATAR CORPORATION", + [3]byte{0, 128, 253}: "EXSCEED CORPRATION", + [3]byte{0, 128, 254}: "AZURE TECHNOLOGIES, INC.", + [3]byte{0, 128, 255}: "SOC. DE TELEINFORMATIQUE RTC", + [3]byte{0, 129, 196}: "Cisco Systems, Inc", + [3]byte{0, 132, 237}: "Private", + [3]byte{0, 134, 160}: "Private", + [3]byte{0, 135, 1}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 135, 49}: "Cisco Systems, Inc", + [3]byte{0, 136, 101}: "Apple, Inc.", + [3]byte{0, 138, 150}: "Cisco Systems, Inc", + [3]byte{0, 139, 67}: "RFTECH", + [3]byte{0, 140, 16}: "Black Box Corp.", + [3]byte{0, 140, 84}: "ADB Broadband Italia", + [3]byte{0, 140, 250}: "INVENTEC Corporation", + [3]byte{0, 141, 78}: "CJSC NII STT", + [3]byte{0, 141, 218}: "Link One Co., Ltd.", + [3]byte{0, 142, 115}: "Cisco Systems, Inc", + [3]byte{0, 142, 242}: "NETGEAR", + [3]byte{0, 144, 0}: "DIAMOND MULTIMEDIA", + [3]byte{0, 144, 1}: "NISHIMU ELECTRONICS INDUSTRIES CO., LTD.", + [3]byte{0, 144, 2}: "ALLGON AB", + [3]byte{0, 144, 3}: "APLIO", + [3]byte{0, 144, 4}: "3COM EUROPE LTD.", + [3]byte{0, 144, 5}: "PROTECH SYSTEMS CO., LTD.", + [3]byte{0, 144, 6}: "Hamamatsu Photonics K.K.", + [3]byte{0, 144, 7}: "DOMEX TECHNOLOGY CORP.", + [3]byte{0, 144, 8}: "HanA Systems Inc.", + [3]byte{0, 144, 9}: "I Controls, Inc.", + [3]byte{0, 144, 10}: "PROTON ELECTRONIC INDUSTRIAL CO., LTD.", + [3]byte{0, 144, 11}: "LANNER ELECTRONICS, INC.", + [3]byte{0, 144, 12}: "Cisco Systems, Inc", + [3]byte{0, 144, 13}: "Overland Storage Inc.", + [3]byte{0, 144, 14}: "HANDLINK TECHNOLOGIES, INC.", + [3]byte{0, 144, 15}: "KAWASAKI HEAVY INDUSTRIES, LTD", + [3]byte{0, 144, 16}: "SIMULATION LABORATORIES, INC.", + [3]byte{0, 144, 17}: "WAVTrace, Inc.", + [3]byte{0, 144, 18}: "GLOBESPAN SEMICONDUCTOR, INC.", + [3]byte{0, 144, 19}: "SAMSAN CORP.", + [3]byte{0, 144, 20}: "ROTORK INSTRUMENTS, LTD.", + [3]byte{0, 144, 21}: "CENTIGRAM COMMUNICATIONS CORP.", + [3]byte{0, 144, 22}: "ZAC", + [3]byte{0, 144, 23}: "Zypcom, Inc", + [3]byte{0, 144, 24}: "ITO ELECTRIC INDUSTRY CO, LTD.", + [3]byte{0, 144, 25}: "HERMES ELECTRONICS CO., LTD.", + [3]byte{0, 144, 26}: "UNISPHERE SOLUTIONS", + [3]byte{0, 144, 27}: "DIGITAL CONTROLS", + [3]byte{0, 144, 28}: "mps Software Gmbh", + [3]byte{0, 144, 29}: "PEC (NZ) LTD.", + [3]byte{0, 144, 30}: "Selesta Ingegneria S.p.A.", + [3]byte{0, 144, 31}: "ADTEC PRODUCTIONS, INC.", + [3]byte{0, 144, 32}: "PHILIPS ANALYTICAL X-RAY B.V.", + [3]byte{0, 144, 33}: "Cisco Systems, Inc", + [3]byte{0, 144, 34}: "IVEX", + [3]byte{0, 144, 35}: "ZILOG INC.", + [3]byte{0, 144, 36}: "PIPELINKS, INC.", + [3]byte{0, 144, 37}: "BAE Systems Australia (Electronic Systems) Pty Ltd", + [3]byte{0, 144, 38}: "ADVANCED SWITCHING COMMUNICATIONS, INC.", + [3]byte{0, 144, 39}: "Intel Corporation", + [3]byte{0, 144, 40}: "NIPPON SIGNAL CO., LTD.", + [3]byte{0, 144, 41}: "CRYPTO AG", + [3]byte{0, 144, 42}: "COMMUNICATION DEVICES, INC.", + [3]byte{0, 144, 43}: "Cisco Systems, Inc", + [3]byte{0, 144, 44}: "DATA & CONTROL EQUIPMENT LTD.", + [3]byte{0, 144, 45}: "DATA ELECTRONICS (AUST.) PTY, LTD.", + [3]byte{0, 144, 46}: "NAMCO LIMITED", + [3]byte{0, 144, 47}: "NETCORE SYSTEMS, INC.", + [3]byte{0, 144, 48}: "HONEYWELL-DATING", + [3]byte{0, 144, 49}: "MYSTICOM, LTD.", + [3]byte{0, 144, 50}: "PELCOMBE GROUP LTD.", + [3]byte{0, 144, 51}: "INNOVAPHONE AG", + [3]byte{0, 144, 52}: "IMAGIC, INC.", + [3]byte{0, 144, 53}: "ALPHA TELECOM, INC.", + [3]byte{0, 144, 54}: "ens, inc.", + [3]byte{0, 144, 55}: "ACUCOMM, INC.", + [3]byte{0, 144, 56}: "FOUNTAIN TECHNOLOGIES, INC.", + [3]byte{0, 144, 57}: "SHASTA NETWORKS", + [3]byte{0, 144, 58}: "NIHON MEDIA TOOL INC.", + [3]byte{0, 144, 59}: "TriEMS Research Lab, Inc.", + [3]byte{0, 144, 60}: "ATLANTIC NETWORK SYSTEMS", + [3]byte{0, 144, 61}: "BIOPAC SYSTEMS, INC.", + [3]byte{0, 144, 62}: "N.V. PHILIPS INDUSTRIAL ACTIVITIES", + [3]byte{0, 144, 63}: "AZTEC RADIOMEDIA", + [3]byte{0, 144, 64}: "Siemens Network Convergence LLC", + [3]byte{0, 144, 65}: "APPLIED DIGITAL ACCESS", + [3]byte{0, 144, 66}: "ECCS, Inc.", + [3]byte{0, 144, 67}: "Tattile SRL ", + [3]byte{0, 144, 68}: "ASSURED DIGITAL, INC.", + [3]byte{0, 144, 69}: "Marconi Communications", + [3]byte{0, 144, 70}: "DEXDYNE, LTD.", + [3]byte{0, 144, 71}: "GIGA FAST E. LTD.", + [3]byte{0, 144, 72}: "ZEAL CORPORATION", + [3]byte{0, 144, 73}: "ENTRIDIA CORPORATION", + [3]byte{0, 144, 74}: "CONCUR SYSTEM TECHNOLOGIES", + [3]byte{0, 144, 75}: "Gemtek Technology Co., Ltd.", + [3]byte{0, 144, 76}: "Epigram, Inc.", + [3]byte{0, 144, 77}: "SPEC S.A.", + [3]byte{0, 144, 78}: "DELEM BV", + [3]byte{0, 144, 79}: "ABB POWER T&D COMPANY, INC.", + [3]byte{0, 144, 80}: "Teleste Corporation", + [3]byte{0, 144, 81}: "ULTIMATE TECHNOLOGY CORP.", + [3]byte{0, 144, 82}: "SELCOM ELETTRONICA S.R.L.", + [3]byte{0, 144, 83}: "DAEWOO ELECTRONICS CO., LTD.", + [3]byte{0, 144, 84}: "INNOVATIVE SEMICONDUCTORS, INC", + [3]byte{0, 144, 85}: "PARKER HANNIFIN CORPORATION COMPUMOTOR DIVISION", + [3]byte{0, 144, 86}: "TELESTREAM, INC.", + [3]byte{0, 144, 87}: "AANetcom, Inc.", + [3]byte{0, 144, 88}: "Ultra Electronics Command & Control Systems", + [3]byte{0, 144, 89}: "TELECOM DEVICE K.K.", + [3]byte{0, 144, 90}: "DEARBORN GROUP, INC.", + [3]byte{0, 144, 91}: "RAYMOND AND LAE ENGINEERING", + [3]byte{0, 144, 92}: "EDMI", + [3]byte{0, 144, 93}: "NETCOM SICHERHEITSTECHNIK GMBH", + [3]byte{0, 144, 94}: "RAULAND-BORG CORPORATION", + [3]byte{0, 144, 95}: "Cisco Systems, Inc", + [3]byte{0, 144, 96}: "SYSTEM CREATE CORP.", + [3]byte{0, 144, 97}: "PACIFIC RESEARCH & ENGINEERING CORPORATION", + [3]byte{0, 144, 98}: "ICP VORTEX COMPUTERSYSTEME GmbH", + [3]byte{0, 144, 99}: "COHERENT COMMUNICATIONS SYSTEMS CORPORATION", + [3]byte{0, 144, 100}: "Thomson Inc.", + [3]byte{0, 144, 101}: "FINISAR CORPORATION", + [3]byte{0, 144, 102}: "Troika Networks, Inc.", + [3]byte{0, 144, 103}: "WalkAbout Computers, Inc.", + [3]byte{0, 144, 104}: "DVT CORP.", + [3]byte{0, 144, 105}: "Juniper Networks", + [3]byte{0, 144, 106}: "TURNSTONE SYSTEMS, INC.", + [3]byte{0, 144, 107}: "APPLIED RESOURCES, INC.", + [3]byte{0, 144, 108}: "Sartorius Hamburg GmbH", + [3]byte{0, 144, 109}: "Cisco Systems, Inc", + [3]byte{0, 144, 110}: "PRAXON, INC.", + [3]byte{0, 144, 111}: "Cisco Systems, Inc", + [3]byte{0, 144, 112}: "NEO NETWORKS, INC.", + [3]byte{0, 144, 113}: "Applied Innovation Inc.", + [3]byte{0, 144, 114}: "SIMRAD AS", + [3]byte{0, 144, 115}: "GAIO TECHNOLOGY", + [3]byte{0, 144, 116}: "ARGON NETWORKS, INC.", + [3]byte{0, 144, 117}: "NEC DO BRASIL S.A.", + [3]byte{0, 144, 118}: "FMT AIRCRAFT GATE SUPPORT SYSTEMS AB", + [3]byte{0, 144, 119}: "ADVANCED FIBRE COMMUNICATIONS", + [3]byte{0, 144, 120}: "MER TELEMANAGEMENT SOLUTIONS, LTD.", + [3]byte{0, 144, 121}: "ClearOne, Inc.", + [3]byte{0, 144, 122}: "Spectralink, Inc", + [3]byte{0, 144, 123}: "E-TECH, INC.", + [3]byte{0, 144, 124}: "DIGITALCAST, INC.", + [3]byte{0, 144, 125}: "Lake Communications", + [3]byte{0, 144, 126}: "VETRONIX CORP.", + [3]byte{0, 144, 127}: "WatchGuard Technologies, Inc.", + [3]byte{0, 144, 128}: "NOT LIMITED, INC.", + [3]byte{0, 144, 129}: "ALOHA NETWORKS, INC.", + [3]byte{0, 144, 130}: "FORCE INSTITUTE", + [3]byte{0, 144, 131}: "TURBO COMMUNICATION, INC.", + [3]byte{0, 144, 132}: "ATECH SYSTEM", + [3]byte{0, 144, 133}: "GOLDEN ENTERPRISES, INC.", + [3]byte{0, 144, 134}: "Cisco Systems, Inc", + [3]byte{0, 144, 135}: "ITIS", + [3]byte{0, 144, 136}: "BAXALL SECURITY LTD.", + [3]byte{0, 144, 137}: "SOFTCOM MICROSYSTEMS, INC.", + [3]byte{0, 144, 138}: "BAYLY COMMUNICATIONS, INC.", + [3]byte{0, 144, 139}: "Tattile SRL", + [3]byte{0, 144, 140}: "ETREND ELECTRONICS, INC.", + [3]byte{0, 144, 141}: "VICKERS ELECTRONICS SYSTEMS", + [3]byte{0, 144, 142}: "Nortel Networks Broadband Access", + [3]byte{0, 144, 143}: "AUDIO CODES LTD.", + [3]byte{0, 144, 144}: "I-BUS", + [3]byte{0, 144, 145}: "DigitalScape, Inc.", + [3]byte{0, 144, 146}: "Cisco Systems, Inc", + [3]byte{0, 144, 147}: "NANAO CORPORATION", + [3]byte{0, 144, 148}: "OSPREY TECHNOLOGIES, INC.", + [3]byte{0, 144, 149}: "UNIVERSAL AVIONICS", + [3]byte{0, 144, 150}: "ASKEY COMPUTER CORP", + [3]byte{0, 144, 151}: "Sycamore Networks", + [3]byte{0, 144, 152}: "SBC DESIGNS, INC.", + [3]byte{0, 144, 153}: "ALLIED TELESIS, K.K.", + [3]byte{0, 144, 154}: "ONE WORLD SYSTEMS, INC.", + [3]byte{0, 144, 155}: "MARKEM-IMAJE", + [3]byte{0, 144, 156}: "ARRIS Group, Inc.", + [3]byte{0, 144, 157}: "NovaTech Process Solutions, LLC", + [3]byte{0, 144, 158}: "Critical IO, LLC", + [3]byte{0, 144, 159}: "DIGI-DATA CORPORATION", + [3]byte{0, 144, 160}: "8X8 INC.", + [3]byte{0, 144, 161}: "Flying Pig Systems/High End Systems Inc.", + [3]byte{0, 144, 162}: "CyberTAN Technology Inc.", + [3]byte{0, 144, 163}: "Corecess Inc.", + [3]byte{0, 144, 164}: "ALTIGA NETWORKS", + [3]byte{0, 144, 165}: "SPECTRA LOGIC", + [3]byte{0, 144, 166}: "Cisco Systems, Inc", + [3]byte{0, 144, 167}: "CLIENTEC CORPORATION", + [3]byte{0, 144, 168}: "NineTiles Networks, Ltd.", + [3]byte{0, 144, 169}: "WESTERN DIGITAL", + [3]byte{0, 144, 170}: "INDIGO ACTIVE VISION SYSTEMS LIMITED", + [3]byte{0, 144, 171}: "Cisco Systems, Inc", + [3]byte{0, 144, 172}: "OPTIVISION, INC.", + [3]byte{0, 144, 173}: "ASPECT ELECTRONICS, INC.", + [3]byte{0, 144, 174}: "ITALTEL S.p.A/RF-UP-I", + [3]byte{0, 144, 175}: "J. MORITA MFG. CORP.", + [3]byte{0, 144, 176}: "VADEM", + [3]byte{0, 144, 177}: "Cisco Systems, Inc", + [3]byte{0, 144, 178}: "AVICI SYSTEMS INC.", + [3]byte{0, 144, 179}: "AGRANAT SYSTEMS", + [3]byte{0, 144, 180}: "WILLOWBROOK TECHNOLOGIES", + [3]byte{0, 144, 181}: "NIKON CORPORATION", + [3]byte{0, 144, 182}: "FIBEX SYSTEMS", + [3]byte{0, 144, 183}: "DIGITAL LIGHTWAVE, INC.", + [3]byte{0, 144, 184}: "ROHDE & SCHWARZ GMBH & CO. KG", + [3]byte{0, 144, 185}: "BERAN INSTRUMENTS LTD.", + [3]byte{0, 144, 186}: "VALID NETWORKS, INC.", + [3]byte{0, 144, 187}: "TAINET COMMUNICATION SYSTEM Corp.", + [3]byte{0, 144, 188}: "TELEMANN CO., LTD.", + [3]byte{0, 144, 189}: "OMNIA COMMUNICATIONS, INC.", + [3]byte{0, 144, 190}: "IBC/INTEGRATED BUSINESS COMPUTERS", + [3]byte{0, 144, 191}: "Cisco Systems, Inc", + [3]byte{0, 144, 192}: "K.J. LAW ENGINEERS, INC.", + [3]byte{0, 144, 193}: "Peco II, Inc.", + [3]byte{0, 144, 194}: "JK microsystems, Inc.", + [3]byte{0, 144, 195}: "TOPIC SEMICONDUCTOR CORP.", + [3]byte{0, 144, 196}: "JAVELIN SYSTEMS, INC.", + [3]byte{0, 144, 197}: "INTERNET MAGIC, INC.", + [3]byte{0, 144, 198}: "OPTIM SYSTEMS, INC.", + [3]byte{0, 144, 199}: "ICOM INC.", + [3]byte{0, 144, 200}: "WAVERIDER COMMUNICATIONS (CANADA) INC.", + [3]byte{0, 144, 201}: "DPAC Technologies", + [3]byte{0, 144, 202}: "ACCORD VIDEO TELECOMMUNICATIONS, LTD.", + [3]byte{0, 144, 203}: "Wireless OnLine, Inc.", + [3]byte{0, 144, 204}: "PLANEX COMMUNICATIONS INC.", + [3]byte{0, 144, 205}: "ENT-EMPRESA NACIONAL DE TELECOMMUNICACOES, S.A.", + [3]byte{0, 144, 206}: "TETRA GmbH", + [3]byte{0, 144, 207}: "NORTEL", + [3]byte{0, 144, 208}: "Thomson Telecom Belgium", + [3]byte{0, 144, 209}: "LEICHU ENTERPRISE CO., LTD.", + [3]byte{0, 144, 210}: "ARTEL VIDEO SYSTEMS", + [3]byte{0, 144, 211}: "GIESECKE & DEVRIENT GmbH", + [3]byte{0, 144, 212}: "BindView Development Corp.", + [3]byte{0, 144, 213}: "EUPHONIX, INC.", + [3]byte{0, 144, 214}: "Crystal Group, Inc.", + [3]byte{0, 144, 215}: "NetBoost Corp.", + [3]byte{0, 144, 216}: "WHITECROSS SYSTEMS", + [3]byte{0, 144, 217}: "Cisco Systems, Inc", + [3]byte{0, 144, 218}: "DYNARC, INC.", + [3]byte{0, 144, 219}: "NEXT LEVEL COMMUNICATIONS", + [3]byte{0, 144, 220}: "TECO INFORMATION SYSTEMS", + [3]byte{0, 144, 221}: "MIHARU COMMUNICATIONS Inc", + [3]byte{0, 144, 222}: "CARDKEY SYSTEMS, INC.", + [3]byte{0, 144, 223}: "MITSUBISHI CHEMICAL AMERICA, INC.", + [3]byte{0, 144, 224}: "SYSTRAN CORP.", + [3]byte{0, 144, 225}: "TELENA S.P.A.", + [3]byte{0, 144, 226}: "DISTRIBUTED PROCESSING TECHNOLOGY", + [3]byte{0, 144, 227}: "AVEX ELECTRONICS INC.", + [3]byte{0, 144, 228}: "NEC AMERICA, INC.", + [3]byte{0, 144, 229}: "TEKNEMA, INC.", + [3]byte{0, 144, 230}: "ALi Corporation", + [3]byte{0, 144, 231}: "HORSCH ELEKTRONIK AG", + [3]byte{0, 144, 232}: "MOXA TECHNOLOGIES CORP., LTD.", + [3]byte{0, 144, 233}: "JANZ COMPUTER AG", + [3]byte{0, 144, 234}: "ALPHA TECHNOLOGIES, INC.", + [3]byte{0, 144, 235}: "SENTRY TELECOM SYSTEMS", + [3]byte{0, 144, 236}: "PYRESCOM", + [3]byte{0, 144, 237}: "CENTRAL SYSTEM RESEARCH CO., LTD.", + [3]byte{0, 144, 238}: "PERSONAL COMMUNICATIONS TECHNOLOGIES", + [3]byte{0, 144, 239}: "INTEGRIX, INC.", + [3]byte{0, 144, 240}: "Harmonic Video Systems Ltd.", + [3]byte{0, 144, 241}: "DOT HILL SYSTEMS CORPORATION", + [3]byte{0, 144, 242}: "Cisco Systems, Inc", + [3]byte{0, 144, 243}: "ASPECT COMMUNICATIONS", + [3]byte{0, 144, 244}: "LIGHTNING INSTRUMENTATION", + [3]byte{0, 144, 245}: "CLEVO CO.", + [3]byte{0, 144, 246}: "ESCALATE NETWORKS, INC.", + [3]byte{0, 144, 247}: "NBASE COMMUNICATIONS LTD.", + [3]byte{0, 144, 248}: "MEDIATRIX TELECOM", + [3]byte{0, 144, 249}: "Imagine Communications", + [3]byte{0, 144, 250}: "Emulex Corporation", + [3]byte{0, 144, 251}: "PORTWELL, INC.", + [3]byte{0, 144, 252}: "NETWORK COMPUTING DEVICES", + [3]byte{0, 144, 253}: "CopperCom, Inc.", + [3]byte{0, 144, 254}: "ELECOM CO., LTD. (LANEED DIV.)", + [3]byte{0, 144, 255}: "TELLUS TECHNOLOGY INC.", + [3]byte{0, 145, 214}: "Crystal Group, Inc.", + [3]byte{0, 145, 250}: "Synapse Product Development", + [3]byte{0, 146, 250}: "SHENZHEN WISKY TECHNOLOGY CO.,LTD", + [3]byte{0, 147, 99}: "Uni-Link Technology Co., Ltd.", + [3]byte{0, 149, 105}: "LSD Science and Technology Co.,Ltd.", + [3]byte{0, 151, 255}: "Heimann Sensor GmbH", + [3]byte{0, 154, 205}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 156, 2}: "Hewlett Packard", + [3]byte{0, 157, 142}: "CARDIAC RECORDERS, INC.", + [3]byte{0, 158, 30}: "Cisco Systems, Inc", + [3]byte{0, 158, 200}: "Xiaomi Communications Co Ltd", + [3]byte{0, 160, 0}: "CENTILLION NETWORKS, INC.", + [3]byte{0, 160, 1}: "DRS Signal Solutions", + [3]byte{0, 160, 2}: "LEEDS & NORTHRUP AUSTRALIA PTY LTD", + [3]byte{0, 160, 3}: "Siemens Switzerland Ltd., I B T HVP", + [3]byte{0, 160, 4}: "NETPOWER, INC.", + [3]byte{0, 160, 5}: "DANIEL INSTRUMENTS, LTD.", + [3]byte{0, 160, 6}: "IMAGE DATA PROCESSING SYSTEM GROUP", + [3]byte{0, 160, 7}: "APEXX TECHNOLOGY, INC.", + [3]byte{0, 160, 8}: "NETCORP", + [3]byte{0, 160, 9}: "WHITETREE NETWORK", + [3]byte{0, 160, 10}: "Airspan", + [3]byte{0, 160, 11}: "COMPUTEX CO., LTD.", + [3]byte{0, 160, 12}: "KINGMAX TECHNOLOGY, INC.", + [3]byte{0, 160, 13}: "THE PANDA PROJECT", + [3]byte{0, 160, 14}: "NetScout Systems, Inc.", + [3]byte{0, 160, 15}: "Broadband Technologies", + [3]byte{0, 160, 16}: "SYSLOGIC DATENTECHNIK AG", + [3]byte{0, 160, 17}: "MUTOH INDUSTRIES LTD.", + [3]byte{0, 160, 18}: "Telco Systems, Inc. ", + [3]byte{0, 160, 19}: "TELTREND LTD.", + [3]byte{0, 160, 20}: "CSIR", + [3]byte{0, 160, 21}: "WYLE", + [3]byte{0, 160, 22}: "MICROPOLIS CORP.", + [3]byte{0, 160, 23}: "J B M CORPORATION", + [3]byte{0, 160, 24}: "CREATIVE CONTROLLERS, INC.", + [3]byte{0, 160, 25}: "NEBULA CONSULTANTS, INC.", + [3]byte{0, 160, 26}: "BINAR ELEKTRONIK AB", + [3]byte{0, 160, 27}: "PREMISYS COMMUNICATIONS, INC.", + [3]byte{0, 160, 28}: "NASCENT NETWORKS CORPORATION", + [3]byte{0, 160, 29}: "Red Lion Controls, LP", + [3]byte{0, 160, 30}: "EST CORPORATION", + [3]byte{0, 160, 31}: "TRICORD SYSTEMS, INC.", + [3]byte{0, 160, 32}: "CITICORP/TTI", + [3]byte{0, 160, 33}: "General Dynamics", + [3]byte{0, 160, 34}: "CENTRE FOR DEVELOPMENT OF ADVANCED COMPUTING", + [3]byte{0, 160, 35}: "APPLIED CREATIVE TECHNOLOGY, INC.", + [3]byte{0, 160, 36}: "3COM CORPORATION", + [3]byte{0, 160, 37}: "REDCOM LABS INC.", + [3]byte{0, 160, 38}: "TELDAT, S.A.", + [3]byte{0, 160, 39}: "FIREPOWER SYSTEMS, INC.", + [3]byte{0, 160, 40}: "CONNER PERIPHERALS", + [3]byte{0, 160, 41}: "COULTER CORPORATION", + [3]byte{0, 160, 42}: "TRANCELL SYSTEMS", + [3]byte{0, 160, 43}: "TRANSITIONS RESEARCH CORP.", + [3]byte{0, 160, 44}: "interWAVE Communications", + [3]byte{0, 160, 45}: "1394 Trade Association", + [3]byte{0, 160, 46}: "BRAND COMMUNICATIONS, LTD.", + [3]byte{0, 160, 47}: "ADB Broadband Italia", + [3]byte{0, 160, 48}: "CAPTOR NV/SA", + [3]byte{0, 160, 49}: "HAZELTINE CORPORATION, MS 1-17", + [3]byte{0, 160, 50}: "GES SINGAPORE PTE. LTD.", + [3]byte{0, 160, 51}: "imc MeBsysteme GmbH", + [3]byte{0, 160, 52}: "AXEL", + [3]byte{0, 160, 53}: "CYLINK CORPORATION", + [3]byte{0, 160, 54}: "APPLIED NETWORK TECHNOLOGY", + [3]byte{0, 160, 55}: "Mindray DS USA, Inc.", + [3]byte{0, 160, 56}: "EMAIL ELECTRONICS", + [3]byte{0, 160, 57}: "ROSS TECHNOLOGY, INC.", + [3]byte{0, 160, 58}: "KUBOTEK CORPORATION", + [3]byte{0, 160, 59}: "TOSHIN ELECTRIC CO., LTD.", + [3]byte{0, 160, 60}: "EG&G NUCLEAR INSTRUMENTS", + [3]byte{0, 160, 61}: "OPTO-22", + [3]byte{0, 160, 62}: "ATM FORUM", + [3]byte{0, 160, 63}: "COMPUTER SOCIETY MICROPROCESSOR & MICROPROCESSOR STANDARDS C", + [3]byte{0, 160, 64}: "Apple, Inc.", + [3]byte{0, 160, 65}: "INFICON", + [3]byte{0, 160, 66}: "SPUR PRODUCTS CORP.", + [3]byte{0, 160, 67}: "AMERICAN TECHNOLOGY LABS, INC.", + [3]byte{0, 160, 68}: "NTT IT CO., LTD.", + [3]byte{0, 160, 69}: "PHOENIX CONTACT Electronics GmbH", + [3]byte{0, 160, 70}: "SCITEX CORP. LTD.", + [3]byte{0, 160, 71}: "INTEGRATED FITNESS CORP.", + [3]byte{0, 160, 72}: "QUESTECH, LTD.", + [3]byte{0, 160, 73}: "DIGITECH INDUSTRIES, INC.", + [3]byte{0, 160, 74}: "NISSHIN ELECTRIC CO., LTD.", + [3]byte{0, 160, 75}: "TFL LAN INC.", + [3]byte{0, 160, 76}: "INNOVATIVE SYSTEMS & TECHNOLOGIES, INC.", + [3]byte{0, 160, 77}: "EDA INSTRUMENTS, INC.", + [3]byte{0, 160, 78}: "VOELKER TECHNOLOGIES, INC.", + [3]byte{0, 160, 79}: "AMERITEC CORP.", + [3]byte{0, 160, 80}: "CYPRESS SEMICONDUCTOR", + [3]byte{0, 160, 81}: "ANGIA COMMUNICATIONS. INC.", + [3]byte{0, 160, 82}: "STANILITE ELECTRONICS PTY. LTD", + [3]byte{0, 160, 83}: "COMPACT DEVICES, INC.", + [3]byte{0, 160, 84}: "Private", + [3]byte{0, 160, 85}: "Data Device Corporation", + [3]byte{0, 160, 86}: "MICROPROSS", + [3]byte{0, 160, 87}: "LANCOM Systems GmbH", + [3]byte{0, 160, 88}: "GLORY, LTD.", + [3]byte{0, 160, 89}: "HAMILTON HALLMARK", + [3]byte{0, 160, 90}: "KOFAX IMAGE PRODUCTS", + [3]byte{0, 160, 91}: "MARQUIP, INC.", + [3]byte{0, 160, 92}: "INVENTORY CONVERSION, INC./", + [3]byte{0, 160, 93}: "CS COMPUTER SYSTEME GmbH", + [3]byte{0, 160, 94}: "MYRIAD LOGIC INC.", + [3]byte{0, 160, 95}: "BTG Electronics Design BV", + [3]byte{0, 160, 96}: "ACER PERIPHERALS, INC.", + [3]byte{0, 160, 97}: "PURITAN BENNETT", + [3]byte{0, 160, 98}: "AES PRODATA", + [3]byte{0, 160, 99}: "JRL SYSTEMS, INC.", + [3]byte{0, 160, 100}: "KVB/ANALECT", + [3]byte{0, 160, 101}: "Symantec Corporation", + [3]byte{0, 160, 102}: "ISA CO., LTD.", + [3]byte{0, 160, 103}: "NETWORK SERVICES GROUP", + [3]byte{0, 160, 104}: "BHP LIMITED", + [3]byte{0, 160, 105}: "Symmetricom, Inc.", + [3]byte{0, 160, 106}: "Verilink Corporation", + [3]byte{0, 160, 107}: "DMS DORSCH MIKROSYSTEM GMBH", + [3]byte{0, 160, 108}: "SHINDENGEN ELECTRIC MFG. CO., LTD.", + [3]byte{0, 160, 109}: "MANNESMANN TALLY CORPORATION", + [3]byte{0, 160, 110}: "AUSTRON, INC.", + [3]byte{0, 160, 111}: "Color Sentinel Systems, LLC", + [3]byte{0, 160, 112}: "COASTCOM", + [3]byte{0, 160, 113}: "VIDEO LOTTERY TECHNOLOGIES,INC", + [3]byte{0, 160, 114}: "OVATION SYSTEMS LTD.", + [3]byte{0, 160, 115}: "COM21, INC.", + [3]byte{0, 160, 116}: "PERCEPTION TECHNOLOGY", + [3]byte{0, 160, 117}: "MICRON TECHNOLOGY, INC.", + [3]byte{0, 160, 118}: "CARDWARE LAB, INC.", + [3]byte{0, 160, 119}: "FUJITSU NEXION, INC.", + [3]byte{0, 160, 120}: "Marconi Communications", + [3]byte{0, 160, 121}: "ALPS ELECTRIC (USA), INC.", + [3]byte{0, 160, 122}: "ADVANCED PERIPHERALS TECHNOLOGIES, INC.", + [3]byte{0, 160, 123}: "DAWN COMPUTER INCORPORATION", + [3]byte{0, 160, 124}: "TONYANG NYLON CO., LTD.", + [3]byte{0, 160, 125}: "SEEQ TECHNOLOGY, INC.", + [3]byte{0, 160, 126}: "AVID TECHNOLOGY, INC.", + [3]byte{0, 160, 127}: "GSM-SYNTEL, LTD.", + [3]byte{0, 160, 128}: "Tattile SRL ", + [3]byte{0, 160, 129}: "ALCATEL DATA NETWORKS", + [3]byte{0, 160, 130}: "NKT ELEKTRONIK A/S", + [3]byte{0, 160, 131}: "ASIMMPHONY TURKEY", + [3]byte{0, 160, 132}: "Dataplex Pty Ltd", + [3]byte{0, 160, 133}: "Private", + [3]byte{0, 160, 134}: "AMBER WAVE SYSTEMS, INC.", + [3]byte{0, 160, 135}: "Microsemi Corporation", + [3]byte{0, 160, 136}: "ESSENTIAL COMMUNICATIONS", + [3]byte{0, 160, 137}: "XPOINT TECHNOLOGIES, INC.", + [3]byte{0, 160, 138}: "BROOKTROUT TECHNOLOGY, INC.", + [3]byte{0, 160, 139}: "ASTON ELECTRONIC DESIGNS LTD.", + [3]byte{0, 160, 140}: "MultiMedia LANs, Inc.", + [3]byte{0, 160, 141}: "JACOMO CORPORATION", + [3]byte{0, 160, 142}: "Check Point Software Technologies", + [3]byte{0, 160, 143}: "DESKNET SYSTEMS, INC.", + [3]byte{0, 160, 144}: "TimeStep Corporation", + [3]byte{0, 160, 145}: "APPLICOM INTERNATIONAL", + [3]byte{0, 160, 146}: "H. BOLLMANN MANUFACTURERS, LTD", + [3]byte{0, 160, 147}: "B/E AEROSPACE, Inc.", + [3]byte{0, 160, 148}: "COMSAT CORPORATION", + [3]byte{0, 160, 149}: "ACACIA NETWORKS, INC.", + [3]byte{0, 160, 150}: "MITSUMI ELECTRIC CO., LTD.", + [3]byte{0, 160, 151}: "JC INFORMATION SYSTEMS", + [3]byte{0, 160, 152}: "NetApp", + [3]byte{0, 160, 153}: "K-NET LTD.", + [3]byte{0, 160, 154}: "NIHON KOHDEN AMERICA", + [3]byte{0, 160, 155}: "QPSX COMMUNICATIONS, LTD.", + [3]byte{0, 160, 156}: "Xyplex, Inc.", + [3]byte{0, 160, 157}: "JOHNATHON FREEMAN TECHNOLOGIES", + [3]byte{0, 160, 158}: "ICTV", + [3]byte{0, 160, 159}: "COMMVISION CORP.", + [3]byte{0, 160, 160}: "COMPACT DATA, LTD.", + [3]byte{0, 160, 161}: "EPIC DATA INC.", + [3]byte{0, 160, 162}: "DIGICOM S.P.A.", + [3]byte{0, 160, 163}: "RELIABLE POWER METERS", + [3]byte{0, 160, 164}: "Oracle Corporation ", + [3]byte{0, 160, 165}: "TEKNOR MICROSYSTEME, INC.", + [3]byte{0, 160, 166}: "M.I. SYSTEMS, K.K.", + [3]byte{0, 160, 167}: "VORAX CORPORATION", + [3]byte{0, 160, 168}: "RENEX CORPORATION", + [3]byte{0, 160, 169}: "NAVTEL COMMUNICATIONS INC.", + [3]byte{0, 160, 170}: "SPACELABS MEDICAL", + [3]byte{0, 160, 171}: "NETCS INFORMATIONSTECHNIK GMBH", + [3]byte{0, 160, 172}: "GILAT SATELLITE NETWORKS, LTD.", + [3]byte{0, 160, 173}: "MARCONI SPA", + [3]byte{0, 160, 174}: "NUCOM SYSTEMS, INC.", + [3]byte{0, 160, 175}: "WMS INDUSTRIES", + [3]byte{0, 160, 176}: "I-O DATA DEVICE, INC.", + [3]byte{0, 160, 177}: "FIRST VIRTUAL CORPORATION", + [3]byte{0, 160, 178}: "SHIMA SEIKI", + [3]byte{0, 160, 179}: "ZYKRONIX", + [3]byte{0, 160, 180}: "TEXAS MICROSYSTEMS, INC.", + [3]byte{0, 160, 181}: "3H TECHNOLOGY", + [3]byte{0, 160, 182}: "SANRITZ AUTOMATION CO., LTD.", + [3]byte{0, 160, 183}: "CORDANT, INC.", + [3]byte{0, 160, 184}: "NetApp", + [3]byte{0, 160, 185}: "EAGLE TECHNOLOGY, INC.", + [3]byte{0, 160, 186}: "PATTON ELECTRONICS CO.", + [3]byte{0, 160, 187}: "HILAN GMBH", + [3]byte{0, 160, 188}: "VIASAT, INCORPORATED", + [3]byte{0, 160, 189}: "I-TECH CORP.", + [3]byte{0, 160, 190}: "INTEGRATED CIRCUIT SYSTEMS, INC. COMMUNICATIONS GROUP", + [3]byte{0, 160, 191}: "WIRELESS DATA GROUP MOTOROLA", + [3]byte{0, 160, 192}: "DIGITAL LINK CORP.", + [3]byte{0, 160, 193}: "ORTIVUS MEDICAL AB", + [3]byte{0, 160, 194}: "R.A. SYSTEMS CO., LTD.", + [3]byte{0, 160, 195}: "UNICOMPUTER GMBH", + [3]byte{0, 160, 196}: "CRISTIE ELECTRONICS LTD.", + [3]byte{0, 160, 197}: "ZyXEL Communications Corporation", + [3]byte{0, 160, 198}: "Qualcomm Inc.", + [3]byte{0, 160, 199}: "TADIRAN TELECOMMUNICATIONS", + [3]byte{0, 160, 200}: "Adtran Inc", + [3]byte{0, 160, 201}: "Intel Corporation", + [3]byte{0, 160, 202}: "FUJITSU DENSO LTD.", + [3]byte{0, 160, 203}: "ARK TELECOMMUNICATIONS, INC.", + [3]byte{0, 160, 204}: "LITE-ON COMMUNICATIONS, INC.", + [3]byte{0, 160, 205}: "DR. JOHANNES HEIDENHAIN GmbH", + [3]byte{0, 160, 206}: "Ecessa", + [3]byte{0, 160, 207}: "SOTAS, INC.", + [3]byte{0, 160, 208}: "TEN X TECHNOLOGY, INC.", + [3]byte{0, 160, 209}: "INVENTEC CORPORATION", + [3]byte{0, 160, 210}: "ALLIED TELESIS INTERNATIONAL CORPORATION", + [3]byte{0, 160, 211}: "INSTEM COMPUTER SYSTEMS, LTD.", + [3]byte{0, 160, 212}: "RADIOLAN, INC.", + [3]byte{0, 160, 213}: "SIERRA WIRELESS INC.", + [3]byte{0, 160, 214}: "SBE, Inc.", + [3]byte{0, 160, 215}: "KASTEN CHASE APPLIED RESEARCH", + [3]byte{0, 160, 216}: "SPECTRA - TEK", + [3]byte{0, 160, 217}: "CONVEX COMPUTER CORPORATION", + [3]byte{0, 160, 218}: "INTEGRATED SYSTEMS Technology, Inc.", + [3]byte{0, 160, 219}: "FISHER & PAYKEL PRODUCTION", + [3]byte{0, 160, 220}: "O.N. ELECTRONIC CO., LTD.", + [3]byte{0, 160, 221}: "AZONIX CORPORATION", + [3]byte{0, 160, 222}: "YAMAHA CORPORATION", + [3]byte{0, 160, 223}: "STS TECHNOLOGIES, INC.", + [3]byte{0, 160, 224}: "TENNYSON TECHNOLOGIES PTY LTD", + [3]byte{0, 160, 225}: "WESTPORT RESEARCH ASSOCIATES, INC.", + [3]byte{0, 160, 226}: "Keisokugiken Corporation", + [3]byte{0, 160, 227}: "XKL SYSTEMS CORP.", + [3]byte{0, 160, 228}: "OPTIQUEST", + [3]byte{0, 160, 229}: "NHC COMMUNICATIONS", + [3]byte{0, 160, 230}: "DIALOGIC CORPORATION", + [3]byte{0, 160, 231}: "CENTRAL DATA CORPORATION", + [3]byte{0, 160, 232}: "REUTERS HOLDINGS PLC", + [3]byte{0, 160, 233}: "ELECTRONIC RETAILING SYSTEMS INTERNATIONAL", + [3]byte{0, 160, 234}: "ETHERCOM CORP.", + [3]byte{0, 160, 235}: "Encore Networks, Inc.", + [3]byte{0, 160, 236}: "TRANSMITTON LTD.", + [3]byte{0, 160, 237}: "Brooks Automation, Inc.", + [3]byte{0, 160, 238}: "NASHOBA NETWORKS", + [3]byte{0, 160, 239}: "LUCIDATA LTD.", + [3]byte{0, 160, 240}: "TORONTO MICROELECTRONICS INC.", + [3]byte{0, 160, 241}: "MTI", + [3]byte{0, 160, 242}: "INFOTEK COMMUNICATIONS, INC.", + [3]byte{0, 160, 243}: "STAUBLI", + [3]byte{0, 160, 244}: "GE", + [3]byte{0, 160, 245}: "RADGUARD LTD.", + [3]byte{0, 160, 246}: "AutoGas Systems Inc.", + [3]byte{0, 160, 247}: "V.I COMPUTER CORP.", + [3]byte{0, 160, 248}: "Zebra Technologies Inc", + [3]byte{0, 160, 249}: "BINTEC COMMUNICATIONS GMBH", + [3]byte{0, 160, 250}: "Marconi Communication GmbH", + [3]byte{0, 160, 251}: "TORAY ENGINEERING CO., LTD.", + [3]byte{0, 160, 252}: "IMAGE SCIENCES, INC.", + [3]byte{0, 160, 253}: "SCITEX DIGITAL PRINTING, INC.", + [3]byte{0, 160, 254}: "BOSTON TECHNOLOGY, INC.", + [3]byte{0, 160, 255}: "TELLABS OPERATIONS, INC.", + [3]byte{0, 161, 222}: "ShenZhen ShiHua Technology CO.,LTD", + [3]byte{0, 162, 137}: "Cisco Systems, Inc", + [3]byte{0, 162, 218}: "INAT GmbH", + [3]byte{0, 162, 238}: "Cisco Systems, Inc", + [3]byte{0, 162, 245}: "Guangzhou Yuanyun Network Technology Co.,Ltd", + [3]byte{0, 162, 255}: "abatec group AG", + [3]byte{0, 165, 9}: "WigWag Inc.", + [3]byte{0, 166, 202}: "Cisco Systems, Inc", + [3]byte{0, 167, 66}: "Cisco Systems, Inc", + [3]byte{0, 167, 132}: "ITX security", + [3]byte{0, 170, 0}: "Intel Corporation", + [3]byte{0, 170, 1}: "Intel Corporation", + [3]byte{0, 170, 2}: "Intel Corporation", + [3]byte{0, 170, 60}: "OLIVETTI TELECOM SPA (OLTECO)", + [3]byte{0, 170, 112}: "LG Electronics (Mobile Communications)", + [3]byte{0, 172, 224}: "ARRIS Group, Inc.", + [3]byte{0, 174, 250}: "Murata Manufacturing Co., Ltd.", + [3]byte{0, 175, 31}: "Cisco Systems, Inc", + [3]byte{0, 176, 9}: "Grass Valley, A Belden Brand", + [3]byte{0, 176, 23}: "InfoGear Technology Corp.", + [3]byte{0, 176, 25}: "UTC CCS", + [3]byte{0, 176, 28}: "Westport Technologies", + [3]byte{0, 176, 30}: "Rantic Labs, Inc.", + [3]byte{0, 176, 42}: "ORSYS GmbH", + [3]byte{0, 176, 45}: "ViaGate Technologies, Inc.", + [3]byte{0, 176, 51}: "OAO Izhevskiy radiozavod", + [3]byte{0, 176, 59}: "HiQ Networks", + [3]byte{0, 176, 72}: "Marconi Communications Inc.", + [3]byte{0, 176, 74}: "Cisco Systems, Inc", + [3]byte{0, 176, 82}: "Atheros Communications", + [3]byte{0, 176, 100}: "Cisco Systems, Inc", + [3]byte{0, 176, 105}: "Honewell Oy", + [3]byte{0, 176, 109}: "Jones Futurex Inc.", + [3]byte{0, 176, 128}: "Mannesmann Ipulsys B.V.", + [3]byte{0, 176, 134}: "LocSoft Limited", + [3]byte{0, 176, 142}: "Cisco Systems, Inc", + [3]byte{0, 176, 145}: "Transmeta Corp.", + [3]byte{0, 176, 148}: "Alaris, Inc.", + [3]byte{0, 176, 154}: "Morrow Technologies Corp.", + [3]byte{0, 176, 157}: "Point Grey Research Inc.", + [3]byte{0, 176, 172}: "SIAE-Microelettronica S.p.A.", + [3]byte{0, 176, 174}: "Symmetricom", + [3]byte{0, 176, 179}: "XSTREAMIS PLC", + [3]byte{0, 176, 194}: "Cisco Systems, Inc", + [3]byte{0, 176, 199}: "Tellabs Operations, Inc.", + [3]byte{0, 176, 206}: "Viveris Technologies", + [3]byte{0, 176, 208}: "Dell Inc.", + [3]byte{0, 176, 219}: "Nextcell, Inc.", + [3]byte{0, 176, 223}: "Starboard Storage Systems", + [3]byte{0, 176, 225}: "Cisco Systems, Inc", + [3]byte{0, 176, 231}: "British Federal Ltd.", + [3]byte{0, 176, 236}: "EACEM", + [3]byte{0, 176, 238}: "Ajile Systems, Inc.", + [3]byte{0, 176, 240}: "CALY NETWORKS", + [3]byte{0, 176, 245}: "NetWorth Technologies, Inc.", + [3]byte{0, 179, 56}: "Kontron Design Manufacturing Services (M) Sdn. Bhd", + [3]byte{0, 179, 66}: "MacroSAN Technologies Co., Ltd.", + [3]byte{0, 179, 98}: "Apple, Inc.", + [3]byte{0, 181, 109}: "David Electronics Co., LTD.", + [3]byte{0, 181, 214}: "Omnibit Inc.", + [3]byte{0, 183, 141}: "Nanjing Shining Electric Automation Co., Ltd", + [3]byte{0, 185, 246}: "Shenzhen Super Rich Electronics Co.,Ltd", + [3]byte{0, 186, 192}: "Biometric Access Company", + [3]byte{0, 187, 1}: "OCTOTHORPE CORP.", + [3]byte{0, 187, 58}: "Private", + [3]byte{0, 187, 142}: "HME Co., Ltd.", + [3]byte{0, 187, 193}: "CANON INC.", + [3]byte{0, 187, 240}: "UNGERMANN-BASS INC.", + [3]byte{0, 189, 39}: "Exar Corp.", + [3]byte{0, 189, 58}: "Nokia Corporation", + [3]byte{0, 189, 130}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{0, 191, 21}: "Genetec Inc.", + [3]byte{0, 192, 0}: "LANOPTICS, LTD.", + [3]byte{0, 192, 1}: "DIATEK PATIENT MANAGMENT", + [3]byte{0, 192, 2}: "SERCOMM CORPORATION", + [3]byte{0, 192, 3}: "GLOBALNET COMMUNICATIONS", + [3]byte{0, 192, 4}: "JAPAN BUSINESS COMPUTER CO.LTD", + [3]byte{0, 192, 5}: "LIVINGSTON ENTERPRISES, INC.", + [3]byte{0, 192, 6}: "NIPPON AVIONICS CO., LTD.", + [3]byte{0, 192, 7}: "PINNACLE DATA SYSTEMS, INC.", + [3]byte{0, 192, 8}: "SECO SRL", + [3]byte{0, 192, 9}: "KT TECHNOLOGY (S) PTE LTD", + [3]byte{0, 192, 10}: "MICRO CRAFT", + [3]byte{0, 192, 11}: "NORCONTROL A.S.", + [3]byte{0, 192, 12}: "RELIA TECHNOLGIES", + [3]byte{0, 192, 13}: "ADVANCED LOGIC RESEARCH, INC.", + [3]byte{0, 192, 14}: "PSITECH, INC.", + [3]byte{0, 192, 15}: "QUANTUM SOFTWARE SYSTEMS LTD.", + [3]byte{0, 192, 16}: "HIRAKAWA HEWTECH CORP.", + [3]byte{0, 192, 17}: "INTERACTIVE COMPUTING DEVICES", + [3]byte{0, 192, 18}: "NETSPAN CORPORATION", + [3]byte{0, 192, 19}: "NETRIX", + [3]byte{0, 192, 20}: "TELEMATICS CALABASAS INT'L,INC", + [3]byte{0, 192, 21}: "NEW MEDIA CORPORATION", + [3]byte{0, 192, 22}: "ELECTRONIC THEATRE CONTROLS", + [3]byte{0, 192, 23}: "NetScout Systems, Inc.", + [3]byte{0, 192, 24}: "LANART CORPORATION", + [3]byte{0, 192, 25}: "LEAP TECHNOLOGY, INC.", + [3]byte{0, 192, 26}: "COROMETRICS MEDICAL SYSTEMS", + [3]byte{0, 192, 27}: "SOCKET COMMUNICATIONS, INC.", + [3]byte{0, 192, 28}: "INTERLINK COMMUNICATIONS LTD.", + [3]byte{0, 192, 29}: "GRAND JUNCTION NETWORKS, INC.", + [3]byte{0, 192, 30}: "LA FRANCAISE DES JEUX", + [3]byte{0, 192, 31}: "S.E.R.C.E.L.", + [3]byte{0, 192, 32}: "ARCO ELECTRONIC, CONTROL LTD.", + [3]byte{0, 192, 33}: "NETEXPRESS", + [3]byte{0, 192, 34}: "LASERMASTER TECHNOLOGIES, INC.", + [3]byte{0, 192, 35}: "TUTANKHAMON ELECTRONICS", + [3]byte{0, 192, 36}: "EDEN SISTEMAS DE COMPUTACAO SA", + [3]byte{0, 192, 37}: "DATAPRODUCTS CORPORATION", + [3]byte{0, 192, 38}: "LANS TECHNOLOGY CO., LTD.", + [3]byte{0, 192, 39}: "CIPHER SYSTEMS, INC.", + [3]byte{0, 192, 40}: "JASCO CORPORATION", + [3]byte{0, 192, 41}: "Nexans Deutschland GmbH - ANS", + [3]byte{0, 192, 42}: "OHKURA ELECTRIC CO., LTD.", + [3]byte{0, 192, 43}: "GERLOFF GESELLSCHAFT FUR", + [3]byte{0, 192, 44}: "CENTRUM COMMUNICATIONS, INC.", + [3]byte{0, 192, 45}: "FUJI PHOTO FILM CO., LTD.", + [3]byte{0, 192, 46}: "NETWIZ", + [3]byte{0, 192, 47}: "OKUMA CORPORATION", + [3]byte{0, 192, 48}: "INTEGRATED ENGINEERING B. V.", + [3]byte{0, 192, 49}: "DESIGN RESEARCH SYSTEMS, INC.", + [3]byte{0, 192, 50}: "I-CUBED LIMITED", + [3]byte{0, 192, 51}: "TELEBIT COMMUNICATIONS APS", + [3]byte{0, 192, 52}: "TRANSACTION NETWORK", + [3]byte{0, 192, 53}: "QUINTAR COMPANY", + [3]byte{0, 192, 54}: "RAYTECH ELECTRONIC CORP.", + [3]byte{0, 192, 55}: "DYNATEM", + [3]byte{0, 192, 56}: "RASTER IMAGE PROCESSING SYSTEM", + [3]byte{0, 192, 57}: "Teridian Semiconductor Corporation", + [3]byte{0, 192, 58}: "MEN-MIKRO ELEKTRONIK GMBH", + [3]byte{0, 192, 59}: "MULTIACCESS COMPUTING CORP.", + [3]byte{0, 192, 60}: "TOWER TECH S.R.L.", + [3]byte{0, 192, 61}: "WIESEMANN & THEIS GMBH", + [3]byte{0, 192, 62}: "FA. GEBR. HELLER GMBH", + [3]byte{0, 192, 63}: "STORES AUTOMATED SYSTEMS, INC.", + [3]byte{0, 192, 64}: "ECCI", + [3]byte{0, 192, 65}: "DIGITAL TRANSMISSION SYSTEMS", + [3]byte{0, 192, 66}: "DATALUX CORP.", + [3]byte{0, 192, 67}: "STRATACOM", + [3]byte{0, 192, 68}: "EMCOM CORPORATION", + [3]byte{0, 192, 69}: "ISOLATION SYSTEMS, LTD.", + [3]byte{0, 192, 70}: "Blue Chip Technology Ltd", + [3]byte{0, 192, 71}: "UNIMICRO SYSTEMS, INC.", + [3]byte{0, 192, 72}: "BAY TECHNICAL ASSOCIATES", + [3]byte{0, 192, 73}: "U.S. ROBOTICS, INC.", + [3]byte{0, 192, 74}: "GROUP 2000 AG", + [3]byte{0, 192, 75}: "CREATIVE MICROSYSTEMS", + [3]byte{0, 192, 76}: "DEPARTMENT OF FOREIGN AFFAIRS", + [3]byte{0, 192, 77}: "MITEC, INC.", + [3]byte{0, 192, 78}: "COMTROL CORPORATION", + [3]byte{0, 192, 79}: "Dell Inc.", + [3]byte{0, 192, 80}: "TOYO DENKI SEIZO K.K.", + [3]byte{0, 192, 81}: "ADVANCED INTEGRATION RESEARCH", + [3]byte{0, 192, 82}: "BURR-BROWN", + [3]byte{0, 192, 83}: "Aspect Software Inc.", + [3]byte{0, 192, 84}: "NETWORK PERIPHERALS, LTD.", + [3]byte{0, 192, 85}: "MODULAR COMPUTING TECHNOLOGIES", + [3]byte{0, 192, 86}: "SOMELEC", + [3]byte{0, 192, 87}: "MYCO ELECTRONICS", + [3]byte{0, 192, 88}: "DATAEXPERT CORP.", + [3]byte{0, 192, 89}: "DENSO CORPORATION", + [3]byte{0, 192, 90}: "SEMAPHORE COMMUNICATIONS CORP.", + [3]byte{0, 192, 91}: "NETWORKS NORTHWEST, INC.", + [3]byte{0, 192, 92}: "ELONEX PLC", + [3]byte{0, 192, 93}: "L&N TECHNOLOGIES", + [3]byte{0, 192, 94}: "VARI-LITE, INC.", + [3]byte{0, 192, 95}: "FINE-PAL COMPANY LIMITED", + [3]byte{0, 192, 96}: "ID SCANDINAVIA AS", + [3]byte{0, 192, 97}: "SOLECTEK CORPORATION", + [3]byte{0, 192, 98}: "IMPULSE TECHNOLOGY", + [3]byte{0, 192, 99}: "MORNING STAR TECHNOLOGIES, INC", + [3]byte{0, 192, 100}: "GENERAL DATACOMM IND. INC.", + [3]byte{0, 192, 101}: "SCOPE COMMUNICATIONS, INC.", + [3]byte{0, 192, 102}: "DOCUPOINT, INC.", + [3]byte{0, 192, 103}: "UNITED BARCODE INDUSTRIES", + [3]byte{0, 192, 104}: "HME Clear-Com LTD.", + [3]byte{0, 192, 105}: "Axxcelera Broadband Wireless", + [3]byte{0, 192, 106}: "ZAHNER-ELEKTRIK GMBH & CO. KG", + [3]byte{0, 192, 107}: "OSI PLUS CORPORATION", + [3]byte{0, 192, 108}: "SVEC COMPUTER CORP.", + [3]byte{0, 192, 109}: "BOCA RESEARCH, INC.", + [3]byte{0, 192, 110}: "HAFT TECHNOLOGY, INC.", + [3]byte{0, 192, 111}: "KOMATSU LTD.", + [3]byte{0, 192, 112}: "SECTRA SECURE-TRANSMISSION AB", + [3]byte{0, 192, 113}: "AREANEX COMMUNICATIONS, INC.", + [3]byte{0, 192, 114}: "KNX LTD.", + [3]byte{0, 192, 115}: "XEDIA CORPORATION", + [3]byte{0, 192, 116}: "TOYODA AUTOMATIC LOOM", + [3]byte{0, 192, 117}: "XANTE CORPORATION", + [3]byte{0, 192, 118}: "I-DATA INTERNATIONAL A-S", + [3]byte{0, 192, 119}: "DAEWOO TELECOM LTD.", + [3]byte{0, 192, 120}: "COMPUTER SYSTEMS ENGINEERING", + [3]byte{0, 192, 121}: "FONSYS CO.,LTD.", + [3]byte{0, 192, 122}: "PRIVA B.V.", + [3]byte{0, 192, 123}: "ASCEND COMMUNICATIONS, INC.", + [3]byte{0, 192, 124}: "HIGHTECH INFORMATION", + [3]byte{0, 192, 125}: "RISC DEVELOPMENTS LTD.", + [3]byte{0, 192, 126}: "KUBOTA CORPORATION ELECTRONIC", + [3]byte{0, 192, 127}: "NUPON COMPUTING CORP.", + [3]byte{0, 192, 128}: "NETSTAR, INC.", + [3]byte{0, 192, 129}: "METRODATA LTD.", + [3]byte{0, 192, 130}: "MOORE PRODUCTS CO.", + [3]byte{0, 192, 131}: "TRACE MOUNTAIN PRODUCTS, INC.", + [3]byte{0, 192, 132}: "DATA LINK CORP. LTD.", + [3]byte{0, 192, 133}: "ELECTRONICS FOR IMAGING, INC.", + [3]byte{0, 192, 134}: "THE LYNK CORPORATION", + [3]byte{0, 192, 135}: "UUNET TECHNOLOGIES, INC.", + [3]byte{0, 192, 136}: "EKF ELEKTRONIK GMBH", + [3]byte{0, 192, 137}: "TELINDUS DISTRIBUTION", + [3]byte{0, 192, 138}: "Lauterbach GmbH", + [3]byte{0, 192, 139}: "RISQ MODULAR SYSTEMS, INC.", + [3]byte{0, 192, 140}: "PERFORMANCE TECHNOLOGIES, INC.", + [3]byte{0, 192, 141}: "TRONIX PRODUCT DEVELOPMENT", + [3]byte{0, 192, 142}: "NETWORK INFORMATION TECHNOLOGY", + [3]byte{0, 192, 143}: "Panasonic Electric Works Co., Ltd.", + [3]byte{0, 192, 144}: "PRAIM S.R.L.", + [3]byte{0, 192, 145}: "JABIL CIRCUIT, INC.", + [3]byte{0, 192, 146}: "MENNEN MEDICAL INC.", + [3]byte{0, 192, 147}: "ALTA RESEARCH CORP.", + [3]byte{0, 192, 148}: "VMX INC.", + [3]byte{0, 192, 149}: "ZNYX Networks, Inc.", + [3]byte{0, 192, 150}: "TAMURA CORPORATION", + [3]byte{0, 192, 151}: "ARCHIPEL SA", + [3]byte{0, 192, 152}: "CHUNTEX ELECTRONIC CO., LTD.", + [3]byte{0, 192, 153}: "YOSHIKI INDUSTRIAL CO.,LTD.", + [3]byte{0, 192, 154}: "PHOTONICS CORPORATION", + [3]byte{0, 192, 155}: "RELIANCE COMM/TEC, R-TEC", + [3]byte{0, 192, 156}: "HIOKI E.E. CORPORATION", + [3]byte{0, 192, 157}: "DISTRIBUTED SYSTEMS INT'L, INC", + [3]byte{0, 192, 158}: "CACHE COMPUTERS, INC.", + [3]byte{0, 192, 159}: "QUANTA COMPUTER INC.", + [3]byte{0, 192, 160}: "ADVANCE MICRO RESEARCH, INC.", + [3]byte{0, 192, 161}: "TOKYO DENSHI SEKEI CO.", + [3]byte{0, 192, 162}: "INTERMEDIUM A/S", + [3]byte{0, 192, 163}: "DUAL ENTERPRISES CORPORATION", + [3]byte{0, 192, 164}: "UNIGRAF OY", + [3]byte{0, 192, 165}: "DICKENS DATA SYSTEMS", + [3]byte{0, 192, 166}: "EXICOM AUSTRALIA PTY. LTD", + [3]byte{0, 192, 167}: "SEEL LTD.", + [3]byte{0, 192, 168}: "GVC CORPORATION", + [3]byte{0, 192, 169}: "BARRON MCCANN LTD.", + [3]byte{0, 192, 170}: "SILICON VALLEY COMPUTER", + [3]byte{0, 192, 171}: "Telco Systems, Inc. ", + [3]byte{0, 192, 172}: "GAMBIT COMPUTER COMMUNICATIONS", + [3]byte{0, 192, 173}: "MARBEN COMMUNICATION SYSTEMS", + [3]byte{0, 192, 174}: "TOWERCOM CO. INC. DBA PC HOUSE", + [3]byte{0, 192, 175}: "TEKLOGIX INC.", + [3]byte{0, 192, 176}: "GCC TECHNOLOGIES,INC.", + [3]byte{0, 192, 177}: "GENIUS NET CO.", + [3]byte{0, 192, 178}: "NORAND CORPORATION", + [3]byte{0, 192, 179}: "COMSTAT DATACOMM CORPORATION", + [3]byte{0, 192, 180}: "MYSON TECHNOLOGY, INC.", + [3]byte{0, 192, 181}: "CORPORATE NETWORK SYSTEMS,INC.", + [3]byte{0, 192, 182}: "Overland Storage, Inc.", + [3]byte{0, 192, 183}: "AMERICAN POWER CONVERSION CORP", + [3]byte{0, 192, 184}: "FRASER'S HILL LTD.", + [3]byte{0, 192, 185}: "FUNK SOFTWARE, INC.", + [3]byte{0, 192, 186}: "NETVANTAGE", + [3]byte{0, 192, 187}: "FORVAL CREATIVE, INC.", + [3]byte{0, 192, 188}: "TELECOM AUSTRALIA/CSSC", + [3]byte{0, 192, 189}: "INEX TECHNOLOGIES, INC.", + [3]byte{0, 192, 190}: "ALCATEL - SEL", + [3]byte{0, 192, 191}: "TECHNOLOGY CONCEPTS, LTD.", + [3]byte{0, 192, 192}: "SHORE MICROSYSTEMS, INC.", + [3]byte{0, 192, 193}: "QUAD/GRAPHICS, INC.", + [3]byte{0, 192, 194}: "INFINITE NETWORKS LTD.", + [3]byte{0, 192, 195}: "ACUSON COMPUTED SONOGRAPHY", + [3]byte{0, 192, 196}: "COMPUTER OPERATIONAL", + [3]byte{0, 192, 197}: "SID INFORMATICA", + [3]byte{0, 192, 198}: "PERSONAL MEDIA CORP.", + [3]byte{0, 192, 199}: "SPARKTRUM MICROSYSTEMS, INC.", + [3]byte{0, 192, 200}: "MICRO BYTE PTY. LTD.", + [3]byte{0, 192, 201}: "ELSAG BAILEY PROCESS", + [3]byte{0, 192, 202}: "ALFA, INC.", + [3]byte{0, 192, 203}: "CONTROL TECHNOLOGY CORPORATION", + [3]byte{0, 192, 204}: "TELESCIENCES CO SYSTEMS, INC.", + [3]byte{0, 192, 205}: "COMELTA, S.A.", + [3]byte{0, 192, 206}: "CEI SYSTEMS & ENGINEERING PTE", + [3]byte{0, 192, 207}: "IMATRAN VOIMA OY", + [3]byte{0, 192, 208}: "RATOC SYSTEM INC.", + [3]byte{0, 192, 209}: "COMTREE TECHNOLOGY CORPORATION", + [3]byte{0, 192, 210}: "SYNTELLECT, INC.", + [3]byte{0, 192, 211}: "OLYMPUS IMAGE SYSTEMS, INC.", + [3]byte{0, 192, 212}: "AXON NETWORKS, INC.", + [3]byte{0, 192, 213}: "Werbeagentur Jürgen Siebert", + [3]byte{0, 192, 214}: "J1 SYSTEMS, INC.", + [3]byte{0, 192, 215}: "TAIWAN TRADING CENTER DBA", + [3]byte{0, 192, 216}: "UNIVERSAL DATA SYSTEMS", + [3]byte{0, 192, 217}: "QUINTE NETWORK CONFIDENTIALITY", + [3]byte{0, 192, 218}: "NICE SYSTEMS LTD.", + [3]byte{0, 192, 219}: "IPC CORPORATION (PTE) LTD.", + [3]byte{0, 192, 220}: "EOS TECHNOLOGIES, INC.", + [3]byte{0, 192, 221}: "QLogic Corporation", + [3]byte{0, 192, 222}: "ZCOMM, INC.", + [3]byte{0, 192, 223}: "KYE Systems Corp.", + [3]byte{0, 192, 224}: "DSC COMMUNICATION CORP.", + [3]byte{0, 192, 225}: "SONIC SOLUTIONS", + [3]byte{0, 192, 226}: "CALCOMP, INC.", + [3]byte{0, 192, 227}: "OSITECH COMMUNICATIONS, INC.", + [3]byte{0, 192, 228}: "SIEMENS BUILDING", + [3]byte{0, 192, 229}: "GESPAC, S.A.", + [3]byte{0, 192, 230}: "Verilink Corporation", + [3]byte{0, 192, 231}: "FIBERDATA AB", + [3]byte{0, 192, 232}: "PLEXCOM, INC.", + [3]byte{0, 192, 233}: "OAK SOLUTIONS, LTD.", + [3]byte{0, 192, 234}: "ARRAY TECHNOLOGY LTD.", + [3]byte{0, 192, 235}: "SEH COMPUTERTECHNIK GMBH", + [3]byte{0, 192, 236}: "DAUPHIN TECHNOLOGY", + [3]byte{0, 192, 237}: "US ARMY ELECTRONIC", + [3]byte{0, 192, 238}: "KYOCERA CORPORATION", + [3]byte{0, 192, 239}: "ABIT CORPORATION", + [3]byte{0, 192, 240}: "Kingston Technology Company, Inc.", + [3]byte{0, 192, 241}: "SHINKO ELECTRIC CO., LTD.", + [3]byte{0, 192, 242}: "TRANSITION NETWORKS", + [3]byte{0, 192, 243}: "NETWORK COMMUNICATIONS CORP.", + [3]byte{0, 192, 244}: "INTERLINK SYSTEM CO., LTD.", + [3]byte{0, 192, 245}: "METACOMP, INC.", + [3]byte{0, 192, 246}: "CELAN TECHNOLOGY INC.", + [3]byte{0, 192, 247}: "ENGAGE COMMUNICATION, INC.", + [3]byte{0, 192, 248}: "ABOUT COMPUTING INC.", + [3]byte{0, 192, 249}: "Artesyn Embedded Technologies", + [3]byte{0, 192, 250}: "CANARY COMMUNICATIONS, INC.", + [3]byte{0, 192, 251}: "ADVANCED TECHNOLOGY LABS", + [3]byte{0, 192, 252}: "ELASTIC REALITY, INC.", + [3]byte{0, 192, 253}: "PROSUM", + [3]byte{0, 192, 254}: "APTEC COMPUTER SYSTEMS, INC.", + [3]byte{0, 192, 255}: "DOT HILL SYSTEMS CORPORATION", + [3]byte{0, 193, 79}: "DDL Co,.ltd.", + [3]byte{0, 193, 100}: "Cisco Systems, Inc", + [3]byte{0, 194, 198}: "Intel Corporate", + [3]byte{0, 197, 219}: "Datatech Sistemas Digitales Avanzados SL", + [3]byte{0, 198, 16}: "Apple, Inc.", + [3]byte{0, 200, 139}: "Cisco Systems, Inc", + [3]byte{0, 202, 229}: "Cisco Systems, Inc", + [3]byte{0, 203, 0}: "Private", + [3]byte{0, 203, 189}: "Cambridge Broadband Networks Ltd.", + [3]byte{0, 204, 252}: "Cisco Systems, Inc", + [3]byte{0, 205, 144}: "MAS Elektronik AG", + [3]byte{0, 205, 254}: "Apple, Inc.", + [3]byte{0, 207, 28}: "Communication Machinery Corporation", + [3]byte{0, 208, 0}: "FERRAN SCIENTIFIC, INC.", + [3]byte{0, 208, 1}: "VST TECHNOLOGIES, INC.", + [3]byte{0, 208, 2}: "DITECH CORPORATION", + [3]byte{0, 208, 3}: "COMDA ENTERPRISES CORP.", + [3]byte{0, 208, 4}: "PENTACOM LTD.", + [3]byte{0, 208, 5}: "ZHS ZEITMANAGEMENTSYSTEME", + [3]byte{0, 208, 6}: "Cisco Systems, Inc", + [3]byte{0, 208, 7}: "MIC ASSOCIATES, INC.", + [3]byte{0, 208, 8}: "MACTELL CORPORATION", + [3]byte{0, 208, 9}: "HSING TECH. ENTERPRISE CO. LTD", + [3]byte{0, 208, 10}: "LANACCESS TELECOM S.A.", + [3]byte{0, 208, 11}: "RHK TECHNOLOGY, INC.", + [3]byte{0, 208, 12}: "SNIJDER MICRO SYSTEMS", + [3]byte{0, 208, 13}: "MICROMERITICS INSTRUMENT", + [3]byte{0, 208, 14}: "PLURIS, INC.", + [3]byte{0, 208, 15}: "SPEECH DESIGN GMBH", + [3]byte{0, 208, 16}: "CONVERGENT NETWORKS, INC.", + [3]byte{0, 208, 17}: "PRISM VIDEO, INC.", + [3]byte{0, 208, 18}: "GATEWORKS CORP.", + [3]byte{0, 208, 19}: "PRIMEX AEROSPACE COMPANY", + [3]byte{0, 208, 20}: "ROOT, INC.", + [3]byte{0, 208, 21}: "UNIVEX MICROTECHNOLOGY CORP.", + [3]byte{0, 208, 22}: "SCM MICROSYSTEMS, INC.", + [3]byte{0, 208, 23}: "SYNTECH INFORMATION CO., LTD.", + [3]byte{0, 208, 24}: "QWES. COM, INC.", + [3]byte{0, 208, 25}: "DAINIPPON SCREEN CORPORATE", + [3]byte{0, 208, 26}: "URMET TLC S.P.A.", + [3]byte{0, 208, 27}: "MIMAKI ENGINEERING CO., LTD.", + [3]byte{0, 208, 28}: "SBS TECHNOLOGIES,", + [3]byte{0, 208, 29}: "FURUNO ELECTRIC CO., LTD.", + [3]byte{0, 208, 30}: "PINGTEL CORP.", + [3]byte{0, 208, 31}: "Senetas Security", + [3]byte{0, 208, 32}: "AIM SYSTEM, INC.", + [3]byte{0, 208, 33}: "REGENT ELECTRONICS CORP.", + [3]byte{0, 208, 34}: "INCREDIBLE TECHNOLOGIES, INC.", + [3]byte{0, 208, 35}: "INFORTREND TECHNOLOGY, INC.", + [3]byte{0, 208, 36}: "Cognex Corporation", + [3]byte{0, 208, 37}: "XROSSTECH, INC.", + [3]byte{0, 208, 38}: "HIRSCHMANN AUSTRIA GMBH", + [3]byte{0, 208, 39}: "APPLIED AUTOMATION, INC.", + [3]byte{0, 208, 40}: "Harmonic, Inc", + [3]byte{0, 208, 41}: "WAKEFERN FOOD CORPORATION", + [3]byte{0, 208, 42}: "Voxent Systems Ltd.", + [3]byte{0, 208, 43}: "JETCELL, INC.", + [3]byte{0, 208, 44}: "CAMPBELL SCIENTIFIC, INC.", + [3]byte{0, 208, 45}: "ADEMCO", + [3]byte{0, 208, 46}: "COMMUNICATION AUTOMATION CORP.", + [3]byte{0, 208, 47}: "VLSI TECHNOLOGY INC.", + [3]byte{0, 208, 48}: "Safetran Systems Corp", + [3]byte{0, 208, 49}: "INDUSTRIAL LOGIC CORPORATION", + [3]byte{0, 208, 50}: "YANO ELECTRIC CO., LTD.", + [3]byte{0, 208, 51}: "DALIAN DAXIAN NETWORK", + [3]byte{0, 208, 52}: "ORMEC SYSTEMS CORP.", + [3]byte{0, 208, 53}: "BEHAVIOR TECH. COMPUTER CORP.", + [3]byte{0, 208, 54}: "TECHNOLOGY ATLANTA CORP.", + [3]byte{0, 208, 55}: "ARRIS Group, Inc.", + [3]byte{0, 208, 56}: "FIVEMERE, LTD.", + [3]byte{0, 208, 57}: "UTILICOM, INC.", + [3]byte{0, 208, 58}: "ZONEWORX, INC.", + [3]byte{0, 208, 59}: "VISION PRODUCTS PTY. LTD.", + [3]byte{0, 208, 60}: "Vieo, Inc.", + [3]byte{0, 208, 61}: "GALILEO TECHNOLOGY, LTD.", + [3]byte{0, 208, 62}: "ROCKETCHIPS, INC.", + [3]byte{0, 208, 63}: "AMERICAN COMMUNICATION", + [3]byte{0, 208, 64}: "SYSMATE CO., LTD.", + [3]byte{0, 208, 65}: "AMIGO TECHNOLOGY CO., LTD.", + [3]byte{0, 208, 66}: "MAHLO GMBH & CO. UG", + [3]byte{0, 208, 67}: "ZONAL RETAIL DATA SYSTEMS", + [3]byte{0, 208, 68}: "ALIDIAN NETWORKS, INC.", + [3]byte{0, 208, 69}: "KVASER AB", + [3]byte{0, 208, 70}: "DOLBY LABORATORIES, INC.", + [3]byte{0, 208, 71}: "XN TECHNOLOGIES", + [3]byte{0, 208, 72}: "ECTON, INC.", + [3]byte{0, 208, 73}: "IMPRESSTEK CO., LTD.", + [3]byte{0, 208, 74}: "PRESENCE TECHNOLOGY GMBH", + [3]byte{0, 208, 75}: "LA CIE GROUP S.A.", + [3]byte{0, 208, 76}: "EUROTEL TELECOM LTD.", + [3]byte{0, 208, 77}: "DIV OF RESEARCH & STATISTICS", + [3]byte{0, 208, 78}: "LOGIBAG", + [3]byte{0, 208, 79}: "BITRONICS, INC.", + [3]byte{0, 208, 80}: "ISKRATEL", + [3]byte{0, 208, 81}: "O2 MICRO, INC.", + [3]byte{0, 208, 82}: "ASCEND COMMUNICATIONS, INC.", + [3]byte{0, 208, 83}: "CONNECTED SYSTEMS", + [3]byte{0, 208, 84}: "SAS INSTITUTE INC.", + [3]byte{0, 208, 85}: "KATHREIN-WERKE KG", + [3]byte{0, 208, 86}: "SOMAT CORPORATION", + [3]byte{0, 208, 87}: "ULTRAK, INC.", + [3]byte{0, 208, 88}: "Cisco Systems, Inc", + [3]byte{0, 208, 89}: "AMBIT MICROSYSTEMS CORP.", + [3]byte{0, 208, 90}: "SYMBIONICS, LTD.", + [3]byte{0, 208, 91}: "ACROLOOP MOTION CONTROL", + [3]byte{0, 208, 92}: "KATHREIN TechnoTrend GmbH", + [3]byte{0, 208, 93}: "INTELLIWORXX, INC.", + [3]byte{0, 208, 94}: "STRATABEAM TECHNOLOGY, INC.", + [3]byte{0, 208, 95}: "VALCOM, INC.", + [3]byte{0, 208, 96}: "Panasonic Europe Ltd.", + [3]byte{0, 208, 97}: "TREMON ENTERPRISES CO., LTD.", + [3]byte{0, 208, 98}: "DIGIGRAM", + [3]byte{0, 208, 99}: "Cisco Systems, Inc", + [3]byte{0, 208, 100}: "MULTITEL", + [3]byte{0, 208, 101}: "TOKO ELECTRIC", + [3]byte{0, 208, 102}: "WINTRISS ENGINEERING CORP.", + [3]byte{0, 208, 103}: "CAMPIO COMMUNICATIONS", + [3]byte{0, 208, 104}: "IWILL CORPORATION", + [3]byte{0, 208, 105}: "TECHNOLOGIC SYSTEMS", + [3]byte{0, 208, 106}: "LINKUP SYSTEMS CORPORATION", + [3]byte{0, 208, 107}: "SR TELECOM INC.", + [3]byte{0, 208, 108}: "SHAREWAVE, INC.", + [3]byte{0, 208, 109}: "ACRISON, INC.", + [3]byte{0, 208, 110}: "TRENDVIEW RECORDERS LTD.", + [3]byte{0, 208, 111}: "KMC CONTROLS", + [3]byte{0, 208, 112}: "LONG WELL ELECTRONICS CORP.", + [3]byte{0, 208, 113}: "ECHELON CORP.", + [3]byte{0, 208, 114}: "BROADLOGIC", + [3]byte{0, 208, 115}: "ACN ADVANCED COMMUNICATIONS", + [3]byte{0, 208, 116}: "TAQUA SYSTEMS, INC.", + [3]byte{0, 208, 117}: "ALARIS MEDICAL SYSTEMS, INC.", + [3]byte{0, 208, 118}: "Bank of America", + [3]byte{0, 208, 119}: "LUCENT TECHNOLOGIES", + [3]byte{0, 208, 120}: "Eltex of Sweden AB", + [3]byte{0, 208, 121}: "Cisco Systems, Inc", + [3]byte{0, 208, 122}: "AMAQUEST COMPUTER CORP.", + [3]byte{0, 208, 123}: "COMCAM INTERNATIONAL INC", + [3]byte{0, 208, 124}: "KOYO ELECTRONICS INC. CO.,LTD.", + [3]byte{0, 208, 125}: "COSINE COMMUNICATIONS", + [3]byte{0, 208, 126}: "KEYCORP LTD.", + [3]byte{0, 208, 127}: "STRATEGY & TECHNOLOGY, LIMITED", + [3]byte{0, 208, 128}: "EXABYTE CORPORATION", + [3]byte{0, 208, 129}: "RTD Embedded Technologies, Inc.", + [3]byte{0, 208, 130}: "IOWAVE INC.", + [3]byte{0, 208, 131}: "INVERTEX, INC.", + [3]byte{0, 208, 132}: "NEXCOMM SYSTEMS, INC.", + [3]byte{0, 208, 133}: "OTIS ELEVATOR COMPANY", + [3]byte{0, 208, 134}: "FOVEON, INC.", + [3]byte{0, 208, 135}: "MICROFIRST INC.", + [3]byte{0, 208, 136}: "ARRIS Group, Inc.", + [3]byte{0, 208, 137}: "DYNACOLOR, INC.", + [3]byte{0, 208, 138}: "PHOTRON USA", + [3]byte{0, 208, 139}: "ADVA Optical Networking Ltd.", + [3]byte{0, 208, 140}: "GENOA TECHNOLOGY, INC.", + [3]byte{0, 208, 141}: "PHOENIX GROUP, INC.", + [3]byte{0, 208, 142}: "Grass Valley, A Belden Brand", + [3]byte{0, 208, 143}: "ARDENT TECHNOLOGIES, INC.", + [3]byte{0, 208, 144}: "Cisco Systems, Inc", + [3]byte{0, 208, 145}: "SMARTSAN SYSTEMS, INC.", + [3]byte{0, 208, 146}: "GLENAYRE WESTERN MULTIPLEX", + [3]byte{0, 208, 147}: "TQ - COMPONENTS GMBH", + [3]byte{0, 208, 148}: "Seeion Control LLC", + [3]byte{0, 208, 149}: "Alcatel-Lucent Enterprise", + [3]byte{0, 208, 150}: "3COM EUROPE LTD.", + [3]byte{0, 208, 151}: "Cisco Systems, Inc", + [3]byte{0, 208, 152}: "Photon Dynamics Canada Inc.", + [3]byte{0, 208, 153}: "Elcard Wireless Systems Oy", + [3]byte{0, 208, 154}: "FILANET CORPORATION", + [3]byte{0, 208, 155}: "SPECTEL LTD.", + [3]byte{0, 208, 156}: "KAPADIA COMMUNICATIONS", + [3]byte{0, 208, 157}: "VERIS INDUSTRIES", + [3]byte{0, 208, 158}: "2Wire Inc", + [3]byte{0, 208, 159}: "NOVTEK TEST SYSTEMS", + [3]byte{0, 208, 160}: "MIPS DENMARK", + [3]byte{0, 208, 161}: "OSKAR VIERLING GMBH + CO. KG", + [3]byte{0, 208, 162}: "INTEGRATED DEVICE", + [3]byte{0, 208, 163}: "VOCAL DATA, INC.", + [3]byte{0, 208, 164}: "ALANTRO COMMUNICATIONS", + [3]byte{0, 208, 165}: "AMERICAN ARIUM", + [3]byte{0, 208, 166}: "LANBIRD TECHNOLOGY CO., LTD.", + [3]byte{0, 208, 167}: "TOKYO SOKKI KENKYUJO CO., LTD.", + [3]byte{0, 208, 168}: "NETWORK ENGINES, INC.", + [3]byte{0, 208, 169}: "SHINANO KENSHI CO., LTD.", + [3]byte{0, 208, 170}: "CHASE COMMUNICATIONS", + [3]byte{0, 208, 171}: "DELTAKABEL TELECOM CV", + [3]byte{0, 208, 172}: "Commscope, Inc", + [3]byte{0, 208, 173}: "TL INDUSTRIES", + [3]byte{0, 208, 174}: "ORESIS COMMUNICATIONS, INC.", + [3]byte{0, 208, 175}: "CUTLER-HAMMER, INC.", + [3]byte{0, 208, 176}: "BITSWITCH LTD.", + [3]byte{0, 208, 177}: "OMEGA ELECTRONICS SA", + [3]byte{0, 208, 178}: "Xiotech Corporation", + [3]byte{0, 208, 179}: "DRS Technologies Canada Ltd", + [3]byte{0, 208, 180}: "KATSUJIMA CO., LTD.", + [3]byte{0, 208, 181}: "IPricot formerly DotCom", + [3]byte{0, 208, 182}: "CRESCENT NETWORKS, INC.", + [3]byte{0, 208, 183}: "Intel Corporation", + [3]byte{0, 208, 184}: "Iomega Corporation", + [3]byte{0, 208, 185}: "MICROTEK INTERNATIONAL, INC.", + [3]byte{0, 208, 186}: "Cisco Systems, Inc", + [3]byte{0, 208, 187}: "Cisco Systems, Inc", + [3]byte{0, 208, 188}: "Cisco Systems, Inc", + [3]byte{0, 208, 189}: "Lattice Semiconductor Corp. (LPA)", + [3]byte{0, 208, 190}: "EMUTEC INC.", + [3]byte{0, 208, 191}: "PIVOTAL TECHNOLOGIES", + [3]byte{0, 208, 192}: "Cisco Systems, Inc", + [3]byte{0, 208, 193}: "HARMONIC DATA SYSTEMS, LTD.", + [3]byte{0, 208, 194}: "BALTHAZAR TECHNOLOGY AB", + [3]byte{0, 208, 195}: "VIVID TECHNOLOGY PTE, LTD.", + [3]byte{0, 208, 196}: "TERATECH CORPORATION", + [3]byte{0, 208, 197}: "COMPUTATIONAL SYSTEMS, INC.", + [3]byte{0, 208, 198}: "THOMAS & BETTS CORP.", + [3]byte{0, 208, 199}: "PATHWAY, INC.", + [3]byte{0, 208, 200}: "Prevas A/S", + [3]byte{0, 208, 201}: "ADVANTECH CO., LTD.", + [3]byte{0, 208, 202}: "Intrinsyc Software International Inc.", + [3]byte{0, 208, 203}: "DASAN CO., LTD.", + [3]byte{0, 208, 204}: "TECHNOLOGIES LYRE INC.", + [3]byte{0, 208, 205}: "ATAN TECHNOLOGY INC.", + [3]byte{0, 208, 206}: "ASYST ELECTRONIC", + [3]byte{0, 208, 207}: "MORETON BAY", + [3]byte{0, 208, 208}: "ZHONGXING TELECOM LTD.", + [3]byte{0, 208, 209}: "Sycamore Networks", + [3]byte{0, 208, 210}: "EPILOG CORPORATION", + [3]byte{0, 208, 211}: "Cisco Systems, Inc", + [3]byte{0, 208, 212}: "V-BITS, INC.", + [3]byte{0, 208, 213}: "GRUNDIG AG", + [3]byte{0, 208, 214}: "AETHRA TELECOMUNICAZIONI", + [3]byte{0, 208, 215}: "B2C2, INC.", + [3]byte{0, 208, 216}: "3Com Corporation", + [3]byte{0, 208, 217}: "DEDICATED MICROCOMPUTERS", + [3]byte{0, 208, 218}: "TAICOM DATA SYSTEMS CO., LTD.", + [3]byte{0, 208, 219}: "MCQUAY INTERNATIONAL", + [3]byte{0, 208, 220}: "MODULAR MINING SYSTEMS, INC.", + [3]byte{0, 208, 221}: "SUNRISE TELECOM, INC.", + [3]byte{0, 208, 222}: "PHILIPS MULTIMEDIA NETWORK", + [3]byte{0, 208, 223}: "KUZUMI ELECTRONICS, INC.", + [3]byte{0, 208, 224}: "DOOIN ELECTRONICS CO.", + [3]byte{0, 208, 225}: "AVIONITEK ISRAEL INC.", + [3]byte{0, 208, 226}: "MRT MICRO, INC.", + [3]byte{0, 208, 227}: "ELE-CHEM ENGINEERING CO., LTD.", + [3]byte{0, 208, 228}: "Cisco Systems, Inc", + [3]byte{0, 208, 229}: "SOLIDUM SYSTEMS CORP.", + [3]byte{0, 208, 230}: "IBOND INC.", + [3]byte{0, 208, 231}: "VCON TELECOMMUNICATION LTD.", + [3]byte{0, 208, 232}: "MAC SYSTEM CO., LTD.", + [3]byte{0, 208, 233}: "Advantage Century Telecommunication Corp.", + [3]byte{0, 208, 234}: "NEXTONE COMMUNICATIONS, INC.", + [3]byte{0, 208, 235}: "LIGHTERA NETWORKS, INC.", + [3]byte{0, 208, 236}: "NAKAYO Inc", + [3]byte{0, 208, 237}: "XIOX", + [3]byte{0, 208, 238}: "DICTAPHONE CORPORATION", + [3]byte{0, 208, 239}: "IGT", + [3]byte{0, 208, 240}: "CONVISION TECHNOLOGY GMBH", + [3]byte{0, 208, 241}: "SEGA ENTERPRISES, LTD.", + [3]byte{0, 208, 242}: "MONTEREY NETWORKS", + [3]byte{0, 208, 243}: "SOLARI DI UDINE SPA", + [3]byte{0, 208, 244}: "CARINTHIAN TECH INSTITUTE", + [3]byte{0, 208, 245}: "ORANGE MICRO, INC.", + [3]byte{0, 208, 246}: "Nokia", + [3]byte{0, 208, 247}: "NEXT NETS CORPORATION", + [3]byte{0, 208, 248}: "FUJIAN STAR TERMINAL", + [3]byte{0, 208, 249}: "ACUTE COMMUNICATIONS CORP.", + [3]byte{0, 208, 250}: "Thales e-Security Ltd.", + [3]byte{0, 208, 251}: "TEK MICROSYSTEMS, INCORPORATED", + [3]byte{0, 208, 252}: "GRANITE MICROSYSTEMS", + [3]byte{0, 208, 253}: "OPTIMA TELE.COM, INC.", + [3]byte{0, 208, 254}: "ASTRAL POINT", + [3]byte{0, 208, 255}: "Cisco Systems, Inc", + [3]byte{0, 209, 28}: "ACETEL", + [3]byte{0, 211, 24}: "SPG Controls", + [3]byte{0, 211, 141}: "Hotel Technology Next Generation", + [3]byte{0, 214, 50}: "GE Energy", + [3]byte{0, 215, 143}: "Cisco Systems, Inc", + [3]byte{0, 217, 209}: "Sony Interactive Entertainment Inc.", + [3]byte{0, 218, 85}: "Cisco Systems, Inc", + [3]byte{0, 219, 30}: "Albedo Telecom SL", + [3]byte{0, 219, 69}: "THAMWAY CO.,LTD.", + [3]byte{0, 219, 223}: "Intel Corporate", + [3]byte{0, 221, 0}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 1}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 2}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 3}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 4}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 5}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 6}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 7}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 8}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 9}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 10}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 11}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 12}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 13}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 14}: "UNGERMANN-BASS INC.", + [3]byte{0, 221, 15}: "UNGERMANN-BASS INC.", + [3]byte{0, 222, 251}: "Cisco Systems, Inc", + [3]byte{0, 224, 0}: "FUJITSU LIMITED", + [3]byte{0, 224, 1}: "STRAND LIGHTING LIMITED", + [3]byte{0, 224, 2}: "CROSSROADS SYSTEMS, INC.", + [3]byte{0, 224, 3}: "NOKIA WIRELESS BUSINESS COMMUN", + [3]byte{0, 224, 4}: "PMC-SIERRA, INC.", + [3]byte{0, 224, 5}: "TECHNICAL CORP.", + [3]byte{0, 224, 6}: "SILICON INTEGRATED SYS. CORP.", + [3]byte{0, 224, 7}: "Avaya ECS Ltd", + [3]byte{0, 224, 8}: "AMAZING CONTROLS! INC.", + [3]byte{0, 224, 9}: "MARATHON TECHNOLOGIES CORP.", + [3]byte{0, 224, 10}: "DIBA, INC.", + [3]byte{0, 224, 11}: "ROOFTOP COMMUNICATIONS CORP.", + [3]byte{0, 224, 12}: "MOTOROLA", + [3]byte{0, 224, 13}: "RADIANT SYSTEMS", + [3]byte{0, 224, 14}: "AVALON IMAGING SYSTEMS, INC.", + [3]byte{0, 224, 15}: "Shanghai Baud Data Communication Co.,Ltd.", + [3]byte{0, 224, 16}: "HESS SB-AUTOMATENBAU GmbH", + [3]byte{0, 224, 17}: "UNIDEN CORPORATION", + [3]byte{0, 224, 18}: "PLUTO TECHNOLOGIES INTERNATIONAL INC.", + [3]byte{0, 224, 19}: "EASTERN ELECTRONIC CO., LTD.", + [3]byte{0, 224, 20}: "Cisco Systems, Inc", + [3]byte{0, 224, 21}: "HEIWA CORPORATION", + [3]byte{0, 224, 22}: "RAPID CITY COMMUNICATIONS", + [3]byte{0, 224, 23}: "EXXACT GmbH", + [3]byte{0, 224, 24}: "ASUSTek COMPUTER INC.", + [3]byte{0, 224, 25}: "ING. GIORDANO ELETTRONICA", + [3]byte{0, 224, 26}: "COMTEC SYSTEMS. CO., LTD.", + [3]byte{0, 224, 27}: "SPHERE COMMUNICATIONS, INC.", + [3]byte{0, 224, 28}: "Cradlepoint, Inc", + [3]byte{0, 224, 29}: "WebTV NETWORKS, INC.", + [3]byte{0, 224, 30}: "Cisco Systems, Inc", + [3]byte{0, 224, 31}: "AVIDIA Systems, Inc.", + [3]byte{0, 224, 32}: "TECNOMEN OY", + [3]byte{0, 224, 33}: "FREEGATE CORP.", + [3]byte{0, 224, 34}: "Analog Devices, Inc.", + [3]byte{0, 224, 35}: "TELRAD", + [3]byte{0, 224, 36}: "GADZOOX NETWORKS", + [3]byte{0, 224, 37}: "dit Co., Ltd.", + [3]byte{0, 224, 38}: "Redlake MASD LLC", + [3]byte{0, 224, 39}: "DUX, INC.", + [3]byte{0, 224, 40}: "APTIX CORPORATION", + [3]byte{0, 224, 41}: "STANDARD MICROSYSTEMS CORP.", + [3]byte{0, 224, 42}: "TANDBERG TELEVISION AS", + [3]byte{0, 224, 43}: "Extreme Networks", + [3]byte{0, 224, 44}: "AST COMPUTER", + [3]byte{0, 224, 45}: "InnoMediaLogic, Inc.", + [3]byte{0, 224, 46}: "SPC ELECTRONICS CORPORATION", + [3]byte{0, 224, 47}: "MCNS HOLDINGS, L.P.", + [3]byte{0, 224, 48}: "MELITA INTERNATIONAL CORP.", + [3]byte{0, 224, 49}: "HAGIWARA ELECTRIC CO., LTD.", + [3]byte{0, 224, 50}: "MISYS FINANCIAL SYSTEMS, LTD.", + [3]byte{0, 224, 51}: "E.E.P.D. GmbH", + [3]byte{0, 224, 52}: "Cisco Systems, Inc", + [3]byte{0, 224, 53}: "Artesyn Embedded Technologies", + [3]byte{0, 224, 54}: "PIONEER CORPORATION", + [3]byte{0, 224, 55}: "CENTURY CORPORATION", + [3]byte{0, 224, 56}: "PROXIMA CORPORATION", + [3]byte{0, 224, 57}: "PARADYNE CORP.", + [3]byte{0, 224, 58}: "Cabletron Systems, Inc.", + [3]byte{0, 224, 59}: "PROMINET CORPORATION", + [3]byte{0, 224, 60}: "AdvanSys", + [3]byte{0, 224, 61}: "FOCON ELECTRONIC SYSTEMS A/S", + [3]byte{0, 224, 62}: "ALFATECH, INC.", + [3]byte{0, 224, 63}: "JATON CORPORATION", + [3]byte{0, 224, 64}: "DeskStation Technology, Inc.", + [3]byte{0, 224, 65}: "CSPI", + [3]byte{0, 224, 66}: "Pacom Systems Ltd.", + [3]byte{0, 224, 67}: "VitalCom", + [3]byte{0, 224, 68}: "LSICS CORPORATION", + [3]byte{0, 224, 69}: "TOUCHWAVE, INC.", + [3]byte{0, 224, 70}: "BENTLY NEVADA CORP.", + [3]byte{0, 224, 71}: "InFocus Corporation", + [3]byte{0, 224, 72}: "SDL COMMUNICATIONS, INC.", + [3]byte{0, 224, 73}: "MICROWI ELECTRONIC GmbH", + [3]byte{0, 224, 74}: "ZX Technologies, Inc", + [3]byte{0, 224, 75}: "JUMP INDUSTRIELLE COMPUTERTECHNIK GmbH", + [3]byte{0, 224, 76}: "REALTEK SEMICONDUCTOR CORP.", + [3]byte{0, 224, 77}: "INTERNET INITIATIVE JAPAN, INC", + [3]byte{0, 224, 78}: "SANYO DENKI CO., LTD.", + [3]byte{0, 224, 79}: "Cisco Systems, Inc", + [3]byte{0, 224, 80}: "EXECUTONE INFORMATION SYSTEMS, INC.", + [3]byte{0, 224, 81}: "TALX CORPORATION", + [3]byte{0, 224, 82}: "Brocade Communications Systems, Inc.", + [3]byte{0, 224, 83}: "CELLPORT LABS, INC.", + [3]byte{0, 224, 84}: "KODAI HITEC CO., LTD.", + [3]byte{0, 224, 85}: "INGENIERIA ELECTRONICA COMERCIAL INELCOM S.A.", + [3]byte{0, 224, 86}: "HOLONTECH CORPORATION", + [3]byte{0, 224, 87}: "HAN MICROTELECOM. CO., LTD.", + [3]byte{0, 224, 88}: "PHASE ONE DENMARK A/S", + [3]byte{0, 224, 89}: "CONTROLLED ENVIRONMENTS, LTD.", + [3]byte{0, 224, 90}: "GALEA NETWORK SECURITY", + [3]byte{0, 224, 91}: "WEST END SYSTEMS CORP.", + [3]byte{0, 224, 92}: "Panasonic Healthcare Co., Ltd.", + [3]byte{0, 224, 93}: "UNITEC CO., LTD.", + [3]byte{0, 224, 94}: "JAPAN AVIATION ELECTRONICS INDUSTRY, LTD.", + [3]byte{0, 224, 95}: "e-Net, Inc.", + [3]byte{0, 224, 96}: "SHERWOOD", + [3]byte{0, 224, 97}: "EdgePoint Networks, Inc.", + [3]byte{0, 224, 98}: "HOST ENGINEERING", + [3]byte{0, 224, 99}: "Cabletron Systems, Inc.", + [3]byte{0, 224, 100}: "SAMSUNG ELECTRONICS", + [3]byte{0, 224, 101}: "OPTICAL ACCESS INTERNATIONAL", + [3]byte{0, 224, 102}: "ProMax Systems, Inc.", + [3]byte{0, 224, 103}: "eac AUTOMATION-CONSULTING GmbH", + [3]byte{0, 224, 104}: "MERRIMAC SYSTEMS INC.", + [3]byte{0, 224, 105}: "JAYCOR", + [3]byte{0, 224, 106}: "KAPSCH AG", + [3]byte{0, 224, 107}: "W&G SPECIAL PRODUCTS", + [3]byte{0, 224, 108}: "Ultra Electronics Command & Control Systems", + [3]byte{0, 224, 109}: "COMPUWARE CORPORATION", + [3]byte{0, 224, 110}: "FAR SYSTEMS S.p.A.", + [3]byte{0, 224, 111}: "ARRIS Group, Inc.", + [3]byte{0, 224, 112}: "DH TECHNOLOGY", + [3]byte{0, 224, 113}: "EPIS MICROCOMPUTER", + [3]byte{0, 224, 114}: "LYNK", + [3]byte{0, 224, 115}: "NATIONAL AMUSEMENT NETWORK, INC.", + [3]byte{0, 224, 116}: "TIERNAN COMMUNICATIONS, INC.", + [3]byte{0, 224, 117}: "Verilink Corporation", + [3]byte{0, 224, 118}: "DEVELOPMENT CONCEPTS, INC.", + [3]byte{0, 224, 119}: "WEBGEAR, INC.", + [3]byte{0, 224, 120}: "BERKELEY NETWORKS", + [3]byte{0, 224, 121}: "A.T.N.R.", + [3]byte{0, 224, 122}: "MIKRODIDAKT AB", + [3]byte{0, 224, 123}: "BAY NETWORKS", + [3]byte{0, 224, 124}: "METTLER-TOLEDO, INC.", + [3]byte{0, 224, 125}: "NETRONIX, INC.", + [3]byte{0, 224, 126}: "WALT DISNEY IMAGINEERING", + [3]byte{0, 224, 127}: "LOGISTISTEM s.r.l.", + [3]byte{0, 224, 128}: "CONTROL RESOURCES CORPORATION", + [3]byte{0, 224, 129}: "TYAN COMPUTER CORP.", + [3]byte{0, 224, 130}: "ANERMA", + [3]byte{0, 224, 131}: "JATO TECHNOLOGIES, INC.", + [3]byte{0, 224, 132}: "COMPULITE R&D", + [3]byte{0, 224, 133}: "GLOBAL MAINTECH, INC.", + [3]byte{0, 224, 134}: "Emerson Network Power, Avocent Division", + [3]byte{0, 224, 135}: "LeCroy - Networking Productions Division", + [3]byte{0, 224, 136}: "LTX-Credence CORPORATION", + [3]byte{0, 224, 137}: "ION Networks, Inc.", + [3]byte{0, 224, 138}: "GEC AVERY, LTD.", + [3]byte{0, 224, 139}: "QLogic Corporation", + [3]byte{0, 224, 140}: "NEOPARADIGM LABS, INC.", + [3]byte{0, 224, 141}: "PRESSURE SYSTEMS, INC.", + [3]byte{0, 224, 142}: "UTSTARCOM", + [3]byte{0, 224, 143}: "Cisco Systems, Inc", + [3]byte{0, 224, 144}: "BECKMAN LAB. AUTOMATION DIV.", + [3]byte{0, 224, 145}: "LG Electronics", + [3]byte{0, 224, 146}: "ADMTEK INCORPORATED", + [3]byte{0, 224, 147}: "ACKFIN NETWORKS", + [3]byte{0, 224, 148}: "OSAI SRL", + [3]byte{0, 224, 149}: "ADVANCED-VISION TECHNOLGIES CORP.", + [3]byte{0, 224, 150}: "SHIMADZU CORPORATION", + [3]byte{0, 224, 151}: "CARRIER ACCESS CORPORATION", + [3]byte{0, 224, 152}: "AboCom", + [3]byte{0, 224, 153}: "SAMSON AG", + [3]byte{0, 224, 154}: "Positron Inc.", + [3]byte{0, 224, 155}: "ENGAGE NETWORKS, INC.", + [3]byte{0, 224, 156}: "MII", + [3]byte{0, 224, 157}: "SARNOFF CORPORATION", + [3]byte{0, 224, 158}: "Quantum Corporation", + [3]byte{0, 224, 159}: "PIXEL VISION", + [3]byte{0, 224, 160}: "WILTRON CO.", + [3]byte{0, 224, 161}: "HIMA PAUL HILDEBRANDT GmbH Co. KG", + [3]byte{0, 224, 162}: "MICROSLATE INC.", + [3]byte{0, 224, 163}: "Cisco Systems, Inc", + [3]byte{0, 224, 164}: "ESAOTE S.p.A.", + [3]byte{0, 224, 165}: "ComCore Semiconductor, Inc.", + [3]byte{0, 224, 166}: "TELOGY NETWORKS, INC.", + [3]byte{0, 224, 167}: "IPC INFORMATION SYSTEMS, INC.", + [3]byte{0, 224, 168}: "SAT GmbH & Co.", + [3]byte{0, 224, 169}: "FUNAI ELECTRIC CO., LTD.", + [3]byte{0, 224, 170}: "ELECTROSONIC LTD.", + [3]byte{0, 224, 171}: "DIMAT S.A.", + [3]byte{0, 224, 172}: "MIDSCO, INC.", + [3]byte{0, 224, 173}: "EES TECHNOLOGY, LTD.", + [3]byte{0, 224, 174}: "XAQTI CORPORATION", + [3]byte{0, 224, 175}: "GENERAL DYNAMICS INFORMATION SYSTEMS", + [3]byte{0, 224, 176}: "Cisco Systems, Inc", + [3]byte{0, 224, 177}: "Alcatel-Lucent Enterprise", + [3]byte{0, 224, 178}: "TELMAX COMMUNICATIONS CORP.", + [3]byte{0, 224, 179}: "EtherWAN Systems, Inc.", + [3]byte{0, 224, 180}: "TECHNO SCOPE CO., LTD.", + [3]byte{0, 224, 181}: "ARDENT COMMUNICATIONS CORP.", + [3]byte{0, 224, 182}: "Entrada Networks", + [3]byte{0, 224, 183}: "PI GROUP, LTD.", + [3]byte{0, 224, 184}: "GATEWAY 2000", + [3]byte{0, 224, 185}: "BYAS SYSTEMS", + [3]byte{0, 224, 186}: "BERGHOF AUTOMATIONSTECHNIK GmbH", + [3]byte{0, 224, 187}: "NBX CORPORATION", + [3]byte{0, 224, 188}: "SYMON COMMUNICATIONS, INC.", + [3]byte{0, 224, 189}: "INTERFACE SYSTEMS, INC.", + [3]byte{0, 224, 190}: "GENROCO INTERNATIONAL, INC.", + [3]byte{0, 224, 191}: "TORRENT NETWORKING TECHNOLOGIES CORP.", + [3]byte{0, 224, 192}: "SEIWA ELECTRIC MFG. CO., LTD.", + [3]byte{0, 224, 193}: "MEMOREX TELEX JAPAN, LTD.", + [3]byte{0, 224, 194}: "NECSY S.p.A.", + [3]byte{0, 224, 195}: "SAKAI SYSTEM DEVELOPMENT CORP.", + [3]byte{0, 224, 196}: "HORNER ELECTRIC, INC.", + [3]byte{0, 224, 197}: "BCOM ELECTRONICS INC.", + [3]byte{0, 224, 198}: "LINK2IT, L.L.C.", + [3]byte{0, 224, 199}: "EUROTECH SRL", + [3]byte{0, 224, 200}: "VIRTUAL ACCESS, LTD.", + [3]byte{0, 224, 201}: "AutomatedLogic Corporation", + [3]byte{0, 224, 202}: "BEST DATA PRODUCTS", + [3]byte{0, 224, 203}: "RESON, INC.", + [3]byte{0, 224, 204}: "HERO SYSTEMS, LTD.", + [3]byte{0, 224, 205}: "SAAB SENSIS CORPORATION", + [3]byte{0, 224, 206}: "ARN", + [3]byte{0, 224, 207}: "INTEGRATED DEVICE", + [3]byte{0, 224, 208}: "NETSPEED, INC.", + [3]byte{0, 224, 209}: "TELSIS LIMITED", + [3]byte{0, 224, 210}: "VERSANET COMMUNICATIONS, INC.", + [3]byte{0, 224, 211}: "DATENTECHNIK GmbH", + [3]byte{0, 224, 212}: "EXCELLENT COMPUTER", + [3]byte{0, 224, 213}: "Emulex Corporation", + [3]byte{0, 224, 214}: "COMPUTER & COMMUNICATION RESEARCH LAB.", + [3]byte{0, 224, 215}: "SUNSHINE ELECTRONICS, INC.", + [3]byte{0, 224, 216}: "LANBit Computer, Inc.", + [3]byte{0, 224, 217}: "TAZMO CO., LTD.", + [3]byte{0, 224, 218}: "Alcatel-Lucent Enterprise", + [3]byte{0, 224, 219}: "ViaVideo Communications, Inc.", + [3]byte{0, 224, 220}: "NEXWARE CORP.", + [3]byte{0, 224, 221}: "Zenith Electronics Corporation", + [3]byte{0, 224, 222}: "DATAX NV", + [3]byte{0, 224, 223}: "KEYMILE GmbH", + [3]byte{0, 224, 224}: "SI ELECTRONICS, LTD.", + [3]byte{0, 224, 225}: "G2 NETWORKS, INC.", + [3]byte{0, 224, 226}: "INNOVA CORP.", + [3]byte{0, 224, 227}: "SK-ELEKTRONIK GMBH", + [3]byte{0, 224, 228}: "FANUC ROBOTICS NORTH AMERICA, Inc.", + [3]byte{0, 224, 229}: "CINCO NETWORKS, INC.", + [3]byte{0, 224, 230}: "INCAA Computers", + [3]byte{0, 224, 231}: "RAYTHEON E-SYSTEMS, INC.", + [3]byte{0, 224, 232}: "GRETACODER Data Systems AG", + [3]byte{0, 224, 233}: "DATA LABS, INC.", + [3]byte{0, 224, 234}: "INNOVAT COMMUNICATIONS, INC.", + [3]byte{0, 224, 235}: "DIGICOM SYSTEMS, INCORPORATED", + [3]byte{0, 224, 236}: "CELESTICA INC.", + [3]byte{0, 224, 237}: "SILICOM, LTD.", + [3]byte{0, 224, 238}: "MAREL HF", + [3]byte{0, 224, 239}: "DIONEX", + [3]byte{0, 224, 240}: "ABLER TECHNOLOGY, INC.", + [3]byte{0, 224, 241}: "THAT CORPORATION", + [3]byte{0, 224, 242}: "ARLOTTO COMNET, INC.", + [3]byte{0, 224, 243}: "WebSprint Communications, Inc.", + [3]byte{0, 224, 244}: "INSIDE Technology A/S", + [3]byte{0, 224, 245}: "TELES AG", + [3]byte{0, 224, 246}: "DECISION EUROPE", + [3]byte{0, 224, 247}: "Cisco Systems, Inc", + [3]byte{0, 224, 248}: "DICNA CONTROL AB", + [3]byte{0, 224, 249}: "Cisco Systems, Inc", + [3]byte{0, 224, 250}: "TRL TECHNOLOGY, LTD.", + [3]byte{0, 224, 251}: "LEIGHTRONIX, INC.", + [3]byte{0, 224, 252}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 224, 253}: "A-TREND TECHNOLOGY CO., LTD.", + [3]byte{0, 224, 254}: "Cisco Systems, Inc", + [3]byte{0, 224, 255}: "SECURITY DYNAMICS TECHNOLOGIES, Inc.", + [3]byte{0, 225, 109}: "Cisco Systems, Inc", + [3]byte{0, 225, 117}: "AK-Systems Ltd", + [3]byte{0, 227, 178}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 228, 0}: "Sichuan Changhong Electric Ltd.", + [3]byte{0, 230, 102}: "ARIMA Communications Corp.", + [3]byte{0, 230, 211}: "NIXDORF COMPUTER CORP.", + [3]byte{0, 230, 232}: "Netzin Technology Corporation,.Ltd.", + [3]byte{0, 232, 171}: "Meggitt Training Systems, Inc.", + [3]byte{0, 235, 45}: "Sony Mobile Communications AB", + [3]byte{0, 235, 213}: "Cisco Systems, Inc", + [3]byte{0, 238, 189}: "HTC Corporation", + [3]byte{0, 240, 81}: "KWB Gmbh", + [3]byte{0, 242, 44}: "Shanghai B-star Technology Co.,Ltd.", + [3]byte{0, 242, 139}: "Cisco Systems, Inc", + [3]byte{0, 243, 219}: "WOO Sports", + [3]byte{0, 244, 3}: "Orbis Systems Oy", + [3]byte{0, 244, 111}: "Samsung Electronics Co.,Ltd", + [3]byte{0, 244, 185}: "Apple, Inc.", + [3]byte{0, 246, 99}: "Cisco Systems, Inc", + [3]byte{0, 247, 111}: "Apple, Inc.", + [3]byte{0, 248, 28}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{0, 248, 96}: "PT. Panggung Electric Citrabuana", + [3]byte{0, 248, 113}: "DGS Denmark A/S", + [3]byte{0, 250, 59}: "CLOOS ELECTRONIC GMBH", + [3]byte{0, 252, 88}: "WebSilicon Ltd.", + [3]byte{0, 252, 112}: "Intrepid Control Systems, Inc.", + [3]byte{0, 252, 141}: "Hitron Technologies. Inc", + [3]byte{0, 253, 69}: "Hewlett Packard Enterprise", + [3]byte{0, 253, 76}: "NEVATEC", + [3]byte{0, 254, 200}: "Cisco Systems, Inc", + [3]byte{2, 7, 1}: "RACAL-DATACOM", + [3]byte{2, 28, 124}: "PERQ SYSTEMS CORPORATION", + [3]byte{2, 96, 134}: "LOGIC REPLACEMENT TECH. LTD.", + [3]byte{2, 96, 140}: "3COM CORPORATION", + [3]byte{2, 112, 1}: "RACAL-DATACOM", + [3]byte{2, 112, 176}: "M/A-COM INC. COMPANIES", + [3]byte{2, 112, 179}: "DATA RECALL LTD.", + [3]byte{2, 157, 142}: "CARDIAC RECORDERS, INC.", + [3]byte{2, 170, 60}: "OLIVETTI TELECOMM SPA (OLTECO)", + [3]byte{2, 187, 1}: "OCTOTHORPE CORP.", + [3]byte{2, 192, 140}: "3COM CORPORATION", + [3]byte{2, 207, 28}: "Communication Machinery Corporation", + [3]byte{2, 230, 211}: "NIXDORF COMPUTER CORP.", + [3]byte{4, 2, 31}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 4, 234}: "Valens Semiconductor Ltd.", + [3]byte{4, 10, 131}: "Alcatel-Lucent", + [3]byte{4, 10, 224}: "XMIT AG COMPUTER NETWORKS", + [3]byte{4, 12, 206}: "Apple, Inc.", + [3]byte{4, 14, 194}: "ViewSonic Mobile China Limited", + [3]byte{4, 21, 82}: "Apple, Inc.", + [3]byte{4, 24, 15}: "Samsung Electronics Co.,Ltd", + [3]byte{4, 24, 182}: "Private", + [3]byte{4, 24, 214}: "Ubiquiti Networks Inc.", + [3]byte{4, 26, 4}: "WaveIP", + [3]byte{4, 27, 148}: "Host Mobility AB", + [3]byte{4, 27, 186}: "Samsung Electronics Co.,Ltd", + [3]byte{4, 29, 16}: "Dream Ware Inc.", + [3]byte{4, 30, 100}: "Apple, Inc.", + [3]byte{4, 30, 122}: "DSPWorks", + [3]byte{4, 32, 154}: "Panasonic AVC Networks Company", + [3]byte{4, 33, 76}: "Insight Energy Ventures LLC", + [3]byte{4, 34, 52}: "Wireless Standard Extensions", + [3]byte{4, 37, 197}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 38, 5}: "GFR Gesellschaft für Regelungstechnik und Energieeinsparung mbH", + [3]byte{4, 38, 101}: "Apple, Inc.", + [3]byte{4, 39, 88}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 42, 226}: "Cisco Systems, Inc", + [3]byte{4, 43, 187}: "PicoCELA, Inc.", + [3]byte{4, 45, 180}: "First Property (Beijing) Co., Ltd Modern MOMA Branch", + [3]byte{4, 47, 86}: "ATOCS (Shenzhen) LTD", + [3]byte{4, 49, 16}: "Inspur Group Co., Ltd.", + [3]byte{4, 50, 244}: "Partron", + [3]byte{4, 51, 137}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 54, 4}: "Gyeyoung I&T", + [3]byte{4, 61, 152}: "ChongQing QingJia Electronics CO.,LTD", + [3]byte{4, 65, 105}: "GoPro", + [3]byte{4, 68, 161}: "TELECON GALICIA,S.A.", + [3]byte{4, 70, 101}: "Murata Manufacturing Co., Ltd.", + [3]byte{4, 72, 154}: "Apple, Inc.", + [3]byte{4, 74, 80}: "Ramaxel Technology (Shenzhen) limited company", + [3]byte{4, 75, 237}: "Apple, Inc.", + [3]byte{4, 75, 255}: "GuangZhou Hedy Digital Technology Co., Ltd", + [3]byte{4, 76, 239}: "Fujian Sanao Technology Co.,Ltd", + [3]byte{4, 78, 6}: "Ericsson AB", + [3]byte{4, 78, 90}: "ARRIS Group, Inc.", + [3]byte{4, 79, 139}: "Adapteva, Inc.", + [3]byte{4, 79, 170}: "Ruckus Wireless", + [3]byte{4, 82, 199}: "Bose Corporation", + [3]byte{4, 82, 243}: "Apple, Inc.", + [3]byte{4, 83, 213}: "Sysorex Global Holdings", + [3]byte{4, 84, 83}: "Apple, Inc.", + [3]byte{4, 85, 202}: "BriView (Xiamen) Corp.", + [3]byte{4, 86, 4}: "Gionee Communication Equipment Co.,Ltd.", + [3]byte{4, 87, 47}: "Sertel Electronics UK Ltd", + [3]byte{4, 88, 111}: "Sichuan Whayer information industry Co.,LTD", + [3]byte{4, 90, 149}: "Nokia Corporation", + [3]byte{4, 92, 6}: "Zmodo Technology Corporation", + [3]byte{4, 92, 142}: "gosund GROUP CO.,LTD", + [3]byte{4, 93, 75}: "Sony Corporation", + [3]byte{4, 93, 86}: "camtron industrial inc.", + [3]byte{4, 95, 167}: "Shenzhen Yichen Technology Development Co.,LTD", + [3]byte{4, 97, 105}: "MEDIA GLOBAL LINKS CO., LTD.", + [3]byte{4, 98, 115}: "Cisco Systems, Inc", + [3]byte{4, 98, 215}: "ALSTOM HYDRO FRANCE", + [3]byte{4, 99, 224}: "Nome Oy", + [3]byte{4, 101, 101}: "Testop", + [3]byte{4, 103, 133}: "scemtec Hard- und Software fuer Mess- und Steuerungstechnik GmbH", + [3]byte{4, 105, 248}: "Apple, Inc.", + [3]byte{4, 108, 157}: "Cisco Systems, Inc", + [3]byte{4, 109, 66}: "Bryston Ltd.", + [3]byte{4, 110, 2}: "OpenRTLS Group", + [3]byte{4, 110, 73}: "TaiYear Electronic Technology (Suzhou) Co., Ltd", + [3]byte{4, 112, 188}: "Globalstar Inc.", + [3]byte{4, 116, 161}: "Aligera Equipamentos Digitais Ltda", + [3]byte{4, 117, 3}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 117, 245}: "CSST", + [3]byte{4, 118, 110}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{4, 120, 99}: "Shanghai MXCHIP Information Technology Co., Ltd.", + [3]byte{4, 125, 80}: "Shenzhen Kang Ying Technology Co.Ltd.", + [3]byte{4, 125, 123}: "QUANTA COMPUTER INC.", + [3]byte{4, 126, 74}: "moobox CO., Ltd.", + [3]byte{4, 129, 174}: "Clack Corporation", + [3]byte{4, 132, 138}: "7INOVA TECHNOLOGY LIMITED", + [3]byte{4, 136, 140}: "Eifelwerk Butler Systeme GmbH", + [3]byte{4, 136, 226}: "Beats Electronics LLC", + [3]byte{4, 138, 21}: "Avaya Inc", + [3]byte{4, 139, 66}: "Skspruce Technology Limited", + [3]byte{4, 140, 3}: "ThinPAD Technology (Shenzhen)CO.,LTD", + [3]byte{4, 141, 56}: "Netcore Technology Inc.", + [3]byte{4, 146, 238}: "iway AG", + [3]byte{4, 148, 107}: "TECNO MOBILE LIMITED", + [3]byte{4, 148, 161}: "CATCH THE WIND INC", + [3]byte{4, 149, 115}: "zte corporation", + [3]byte{4, 149, 230}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{4, 150, 69}: "WUXI SKY CHIP INTERCONNECTION TECHNOLOGY CO.,LTD.", + [3]byte{4, 151, 144}: "Lartech telecom LLC", + [3]byte{4, 152, 243}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{4, 153, 230}: "Shenzhen Yoostar Technology Co., Ltd", + [3]byte{4, 155, 156}: "Eadingcore Intelligent Technology Co., Ltd.", + [3]byte{4, 156, 98}: "BMT Medical Technology s.r.o.", + [3]byte{4, 159, 6}: "Smobile Co., Ltd.", + [3]byte{4, 159, 129}: "NetScout Systems, Inc.", + [3]byte{4, 159, 202}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 161, 81}: "NETGEAR", + [3]byte{4, 163, 22}: "Texas Instruments", + [3]byte{4, 163, 243}: "Emicon", + [3]byte{4, 168, 42}: "Nokia Corporation", + [3]byte{4, 176, 231}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 179, 182}: "Seamap (UK) Ltd", + [3]byte{4, 180, 102}: "BSP Co., Ltd.", + [3]byte{4, 182, 72}: "ZENNER", + [3]byte{4, 186, 54}: "Li Seng Technology Ltd", + [3]byte{4, 187, 249}: "Pavilion Data Systems Inc", + [3]byte{4, 189, 112}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 189, 136}: "Aruba Networks", + [3]byte{4, 191, 109}: "ZyXEL Communications Corporation", + [3]byte{4, 191, 168}: "ISB Corporation", + [3]byte{4, 192, 91}: "Tigo Energy", + [3]byte{4, 192, 111}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 192, 156}: "Tellabs Inc.", + [3]byte{4, 193, 3}: "Clover Network, Inc.", + [3]byte{4, 193, 185}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{4, 194, 62}: "HTC Corporation", + [3]byte{4, 197, 164}: "Cisco Systems, Inc", + [3]byte{4, 200, 128}: "Samtec Inc", + [3]byte{4, 201, 145}: "Phistek INC.", + [3]byte{4, 201, 217}: "Echostar Technologies Corp", + [3]byte{4, 203, 29}: "Traka plc", + [3]byte{4, 206, 20}: "Wilocity LTD.", + [3]byte{4, 207, 37}: "MANYCOLORS, INC.", + [3]byte{4, 211, 207}: "Apple, Inc.", + [3]byte{4, 212, 55}: "ZNV", + [3]byte{4, 215, 131}: "Y&H E&C Co.,LTD.", + [3]byte{4, 218, 210}: "Cisco Systems, Inc", + [3]byte{4, 219, 86}: "Apple, Inc.", + [3]byte{4, 219, 138}: "Suntech International Ltd.", + [3]byte{4, 221, 76}: "Velocytech", + [3]byte{4, 222, 219}: "Rockport Networks Inc", + [3]byte{4, 222, 242}: "Shenzhen ECOM Technology Co. Ltd", + [3]byte{4, 223, 105}: "Car Connectivity Consortium", + [3]byte{4, 224, 196}: "TRIUMPH-ADLER AG", + [3]byte{4, 225, 200}: "IMS Soluções em Energia Ltda.", + [3]byte{4, 226, 248}: "AEP Ticketing solutions srl", + [3]byte{4, 228, 81}: "Texas Instruments", + [3]byte{4, 229, 54}: "Apple, Inc.", + [3]byte{4, 229, 72}: "Cohda Wireless Pty Ltd", + [3]byte{4, 230, 98}: "Acroname Inc.", + [3]byte{4, 230, 118}: "AMPAK Technology, Inc.", + [3]byte{4, 233, 229}: "PJRC.COM, LLC", + [3]byte{4, 238, 145}: "x-fabric GmbH", + [3]byte{4, 240, 33}: "Compex Systems Pte Ltd", + [3]byte{4, 241, 62}: "Apple, Inc.", + [3]byte{4, 241, 125}: "Tarana Wireless", + [3]byte{4, 244, 188}: "Xena Networks", + [3]byte{4, 247, 228}: "Apple, Inc.", + [3]byte{4, 248, 194}: "Flaircomm Microelectronics, Inc.", + [3]byte{4, 249, 56}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 254, 49}: "Samsung Electronics Co.,Ltd", + [3]byte{4, 254, 127}: "Cisco Systems, Inc", + [3]byte{4, 254, 141}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{4, 254, 161}: "Fihonest communication co.,Ltd", + [3]byte{4, 255, 81}: "NOVAMEDIA INNOVISION SP. Z O.O.", + [3]byte{8, 0, 1}: "COMPUTERVISION CORPORATION", + [3]byte{8, 0, 2}: "BRIDGE COMMUNICATIONS INC.", + [3]byte{8, 0, 3}: "ADVANCED COMPUTER COMM.", + [3]byte{8, 0, 4}: "CROMEMCO INCORPORATED", + [3]byte{8, 0, 5}: "SYMBOLICS INC.", + [3]byte{8, 0, 6}: "SIEMENS AG", + [3]byte{8, 0, 7}: "Apple, Inc.", + [3]byte{8, 0, 8}: "BOLT BERANEK AND NEWMAN INC.", + [3]byte{8, 0, 9}: "Hewlett Packard", + [3]byte{8, 0, 10}: "NESTAR SYSTEMS INCORPORATED", + [3]byte{8, 0, 11}: "UNISYS CORPORATION", + [3]byte{8, 0, 12}: "MIKLYN DEVELOPMENT CO.", + [3]byte{8, 0, 13}: "International Computers, Ltd", + [3]byte{8, 0, 14}: "NCR CORPORATION", + [3]byte{8, 0, 15}: "MITEL CORPORATION", + [3]byte{8, 0, 17}: "TEKTRONIX INC.", + [3]byte{8, 0, 18}: "BELL ATLANTIC INTEGRATED SYST.", + [3]byte{8, 0, 19}: "Exxon", + [3]byte{8, 0, 20}: "EXCELAN", + [3]byte{8, 0, 21}: "STC BUSINESS SYSTEMS", + [3]byte{8, 0, 22}: "BARRISTER INFO SYS CORP", + [3]byte{8, 0, 23}: "NATIONAL SEMICONDUCTOR", + [3]byte{8, 0, 24}: "PIRELLI FOCOM NETWORKS", + [3]byte{8, 0, 25}: "GENERAL ELECTRIC CORPORATION", + [3]byte{8, 0, 26}: "TIARA/ 10NET", + [3]byte{8, 0, 27}: "EMC Corporation", + [3]byte{8, 0, 28}: "KDD-KOKUSAI DEBNSIN DENWA CO.", + [3]byte{8, 0, 29}: "ABLE COMMUNICATIONS INC.", + [3]byte{8, 0, 30}: "APOLLO COMPUTER INC.", + [3]byte{8, 0, 31}: "SHARP CORPORATION", + [3]byte{8, 0, 32}: "Oracle Corporation", + [3]byte{8, 0, 33}: "3M COMPANY", + [3]byte{8, 0, 34}: "NBI INC.", + [3]byte{8, 0, 35}: "Panasonic Communications Co., Ltd.", + [3]byte{8, 0, 36}: "10NET COMMUNICATIONS/DCA", + [3]byte{8, 0, 37}: "CONTROL DATA", + [3]byte{8, 0, 38}: "NORSK DATA A.S.", + [3]byte{8, 0, 39}: "PCS Systemtechnik GmbH", + [3]byte{8, 0, 40}: "Texas Instruments", + [3]byte{8, 0, 41}: "Megatek Corporation", + [3]byte{8, 0, 42}: "MOSAIC TECHNOLOGIES INC.", + [3]byte{8, 0, 43}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{8, 0, 44}: "BRITTON LEE INC.", + [3]byte{8, 0, 45}: "LAN-TEC INC.", + [3]byte{8, 0, 46}: "METAPHOR COMPUTER SYSTEMS", + [3]byte{8, 0, 47}: "PRIME COMPUTER INC.", + [3]byte{8, 0, 48}: "NETWORK RESEARCH CORPORATION", + [3]byte{8, 0, 48}: "ROYAL MELBOURNE INST OF TECH", + [3]byte{8, 0, 48}: "CERN", + [3]byte{8, 0, 49}: "LITTLE MACHINES INC.", + [3]byte{8, 0, 50}: "TIGAN INCORPORATED", + [3]byte{8, 0, 51}: "BAUSCH & LOMB", + [3]byte{8, 0, 52}: "FILENET CORPORATION", + [3]byte{8, 0, 53}: "MICROFIVE CORPORATION", + [3]byte{8, 0, 54}: "INTERGRAPH CORPORATION", + [3]byte{8, 0, 55}: "FUJI-XEROX CO. LTD.", + [3]byte{8, 0, 56}: "BULL S.A.S.", + [3]byte{8, 0, 57}: "SPIDER SYSTEMS LIMITED", + [3]byte{8, 0, 58}: "ORCATECH INC.", + [3]byte{8, 0, 59}: "TORUS SYSTEMS LIMITED", + [3]byte{8, 0, 60}: "SCHLUMBERGER WELL SERVICES", + [3]byte{8, 0, 61}: "CADNETIX CORPORATIONS", + [3]byte{8, 0, 62}: "CODEX CORPORATION", + [3]byte{8, 0, 63}: "FRED KOSCHARA ENTERPRISES", + [3]byte{8, 0, 64}: "FERRANTI COMPUTER SYS. LIMITED", + [3]byte{8, 0, 65}: "RACAL-MILGO INFORMATION SYS..", + [3]byte{8, 0, 66}: "JAPAN MACNICS CORP.", + [3]byte{8, 0, 67}: "PIXEL COMPUTER INC.", + [3]byte{8, 0, 68}: "DAVID SYSTEMS INC.", + [3]byte{8, 0, 69}: "CONCURRENT COMPUTER CORP.", + [3]byte{8, 0, 70}: "Sony Corporation", + [3]byte{8, 0, 71}: "SEQUENT COMPUTER SYSTEMS INC.", + [3]byte{8, 0, 72}: "EUROTHERM GAUGING SYSTEMS", + [3]byte{8, 0, 73}: "UNIVATION", + [3]byte{8, 0, 74}: "BANYAN SYSTEMS INC.", + [3]byte{8, 0, 75}: "Planning Research Corp.", + [3]byte{8, 0, 76}: "HYDRA COMPUTER SYSTEMS INC.", + [3]byte{8, 0, 77}: "CORVUS SYSTEMS INC.", + [3]byte{8, 0, 78}: "3COM EUROPE LTD.", + [3]byte{8, 0, 79}: "CYGNET SYSTEMS", + [3]byte{8, 0, 80}: "DAISY SYSTEMS CORP.", + [3]byte{8, 0, 81}: "ExperData", + [3]byte{8, 0, 82}: "INSYSTEC", + [3]byte{8, 0, 83}: "MIDDLE EAST TECH. UNIVERSITY", + [3]byte{8, 0, 85}: "STANFORD TELECOMM. INC.", + [3]byte{8, 0, 86}: "STANFORD LINEAR ACCEL. CENTER", + [3]byte{8, 0, 87}: "Evans & Sutherland", + [3]byte{8, 0, 88}: "SYSTEMS CONCEPTS", + [3]byte{8, 0, 89}: "A/S MYCRON", + [3]byte{8, 0, 90}: "IBM Corp", + [3]byte{8, 0, 91}: "VTA TECHNOLOGIES INC.", + [3]byte{8, 0, 92}: "FOUR PHASE SYSTEMS", + [3]byte{8, 0, 93}: "GOULD INC.", + [3]byte{8, 0, 94}: "COUNTERPOINT COMPUTER INC.", + [3]byte{8, 0, 95}: "SABER TECHNOLOGY CORP.", + [3]byte{8, 0, 96}: "INDUSTRIAL NETWORKING INC.", + [3]byte{8, 0, 97}: "JAROGATE LTD.", + [3]byte{8, 0, 98}: "General Dynamics", + [3]byte{8, 0, 99}: "PLESSEY", + [3]byte{8, 0, 100}: "Sitasys AG", + [3]byte{8, 0, 101}: "GENRAD INC.", + [3]byte{8, 0, 102}: "AGFA CORPORATION", + [3]byte{8, 0, 103}: "ComDesign", + [3]byte{8, 0, 104}: "RIDGE COMPUTERS", + [3]byte{8, 0, 105}: "SILICON GRAPHICS INC.", + [3]byte{8, 0, 106}: "AT&T", + [3]byte{8, 0, 107}: "ACCEL TECHNOLOGIES INC.", + [3]byte{8, 0, 108}: "SUNTEK TECHNOLOGY INT'L", + [3]byte{8, 0, 109}: "WHITECHAPEL COMPUTER WORKS", + [3]byte{8, 0, 110}: "MASSCOMP", + [3]byte{8, 0, 111}: "PHILIPS APELDOORN B.V.", + [3]byte{8, 0, 112}: "MITSUBISHI ELECTRIC CORP.", + [3]byte{8, 0, 113}: "MATRA (DSIE)", + [3]byte{8, 0, 114}: "XEROX CORP UNIV GRANT PROGRAM", + [3]byte{8, 0, 115}: "TECMAR INC.", + [3]byte{8, 0, 116}: "CASIO COMPUTER CO. LTD.", + [3]byte{8, 0, 117}: "DANSK DATA ELECTRONIK", + [3]byte{8, 0, 118}: "PC LAN TECHNOLOGIES", + [3]byte{8, 0, 119}: "TSL COMMUNICATIONS LTD.", + [3]byte{8, 0, 120}: "ACCELL CORPORATION", + [3]byte{8, 0, 121}: "THE DROID WORKS", + [3]byte{8, 0, 122}: "INDATA", + [3]byte{8, 0, 123}: "SANYO ELECTRIC CO. LTD.", + [3]byte{8, 0, 124}: "VITALINK COMMUNICATIONS CORP.", + [3]byte{8, 0, 126}: "AMALGAMATED WIRELESS(AUS) LTD", + [3]byte{8, 0, 127}: "CARNEGIE-MELLON UNIVERSITY", + [3]byte{8, 0, 128}: "AES DATA INC.", + [3]byte{8, 0, 129}: "ASTECH INC.", + [3]byte{8, 0, 130}: "VERITAS SOFTWARE", + [3]byte{8, 0, 131}: "Seiko Instruments Inc.", + [3]byte{8, 0, 132}: "TOMEN ELECTRONICS CORP.", + [3]byte{8, 0, 133}: "ELXSI", + [3]byte{8, 0, 134}: "KONICA MINOLTA HOLDINGS, INC.", + [3]byte{8, 0, 135}: "Xyplex, Inc.", + [3]byte{8, 0, 136}: "Brocade Communications Systems, Inc.", + [3]byte{8, 0, 137}: "Kinetics", + [3]byte{8, 0, 138}: "PerfTech, Inc.", + [3]byte{8, 0, 139}: "PYRAMID TECHNOLOGY CORP.", + [3]byte{8, 0, 140}: "NETWORK RESEARCH CORPORATION", + [3]byte{8, 0, 141}: "XYVISION INC.", + [3]byte{8, 0, 142}: "Tandem Computers", + [3]byte{8, 0, 143}: "CHIPCOM CORPORATION", + [3]byte{8, 0, 144}: "SONOMA SYSTEMS", + [3]byte{8, 1, 15}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{8, 2, 142}: "NETGEAR", + [3]byte{8, 3, 113}: "KRG CORPORATE", + [3]byte{8, 5, 129}: "Roku, Inc.", + [3]byte{8, 5, 205}: "DongGuang EnMai Electronic Product Co.Ltd.", + [3]byte{8, 8, 194}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 8, 234}: "AMSC", + [3]byte{8, 9, 182}: "Masimo Corp", + [3]byte{8, 10, 78}: "Planet Bingo® — 3rd Rock Gaming®", + [3]byte{8, 12, 11}: "SysMik GmbH Dresden", + [3]byte{8, 12, 201}: "Mission Technology Group, dba Magma", + [3]byte{8, 13, 132}: "GECO, Inc.", + [3]byte{8, 14, 168}: "Velex s.r.l.", + [3]byte{8, 15, 250}: "KSP INC.", + [3]byte{8, 17, 94}: "Bitel Co., Ltd.", + [3]byte{8, 17, 150}: "Intel Corporate", + [3]byte{8, 20, 67}: "UNIBRAIN S.A.", + [3]byte{8, 22, 81}: "SHENZHEN SEA STAR TECHNOLOGY CO.,LTD", + [3]byte{8, 23, 53}: "Cisco Systems, Inc", + [3]byte{8, 23, 244}: "IBM Corp", + [3]byte{8, 24, 26}: "zte corporation", + [3]byte{8, 24, 76}: "A. S. Thomas, Inc.", + [3]byte{8, 25, 166}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{8, 29, 251}: "Shanghai Mexon Communication Technology Co.,Ltd", + [3]byte{8, 31, 63}: "WondaLink Inc.", + [3]byte{8, 31, 113}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{8, 31, 235}: "BinCube", + [3]byte{8, 31, 243}: "Cisco Systems, Inc", + [3]byte{8, 33, 239}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 35, 178}: "vivo Mobile Communication Co., Ltd.", + [3]byte{8, 37, 34}: "ADVANSEE", + [3]byte{8, 39, 25}: "APS systems/electronic AG", + [3]byte{8, 39, 206}: "NAGANO KEIKI CO., LTD.", + [3]byte{8, 42, 208}: "SRD Innovations Inc.", + [3]byte{8, 44, 176}: "Network Instruments", + [3]byte{8, 46, 95}: "Hewlett Packard", + [3]byte{8, 53, 113}: "CASwell INC.", + [3]byte{8, 55, 61}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 55, 156}: "Topaz Co. LTD.", + [3]byte{8, 56, 165}: "Funkwerk plettac electronic GmbH", + [3]byte{8, 58, 92}: "Junilab, Inc.", + [3]byte{8, 58, 184}: "Shinoda Plasma Co., Ltd.", + [3]byte{8, 61, 136}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 62, 12}: "ARRIS Group, Inc.", + [3]byte{8, 62, 93}: "Sagemcom Broadband SAS", + [3]byte{8, 62, 142}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{8, 63, 62}: "WSH GmbH", + [3]byte{8, 63, 118}: "Intellian Technologies, Inc.", + [3]byte{8, 63, 188}: "zte corporation", + [3]byte{8, 64, 39}: "Gridstore Inc.", + [3]byte{8, 70, 86}: "VEO-LABS", + [3]byte{8, 72, 44}: "Raycore Taiwan Co., LTD.", + [3]byte{8, 78, 28}: "H2A Systems, LLC", + [3]byte{8, 78, 191}: "Broad Net Mux Corporation", + [3]byte{8, 81, 46}: "Orion Diagnostica Oy", + [3]byte{8, 82, 64}: "EbV Elektronikbau- und Vertriebs GmbH", + [3]byte{8, 87, 0}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{8, 90, 224}: "Recovision Technology Co., Ltd.", + [3]byte{8, 91, 14}: "Fortinet, Inc.", + [3]byte{8, 91, 218}: "CliniCare LTD", + [3]byte{8, 93, 221}: "MERCURY CORPORATION", + [3]byte{8, 96, 110}: "ASUSTek COMPUTER INC.", + [3]byte{8, 98, 102}: "ASUSTek COMPUTER INC.", + [3]byte{8, 99, 97}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{8, 102, 152}: "Apple, Inc.", + [3]byte{8, 104, 208}: "Japan System Design", + [3]byte{8, 104, 234}: "EITO ELECTRONICS CO., LTD.", + [3]byte{8, 106, 10}: "ASKEY COMPUTER CORP", + [3]byte{8, 109, 65}: "Apple, Inc.", + [3]byte{8, 109, 242}: "Shenzhen MIMOWAVE Technology Co.,Ltd", + [3]byte{8, 112, 69}: "Apple, Inc.", + [3]byte{8, 116, 2}: "Apple, Inc.", + [3]byte{8, 116, 246}: "Winterhalter Gastronom GmbH", + [3]byte{8, 117, 114}: "Obelux Oy", + [3]byte{8, 118, 24}: "ViE Technologies Sdn. Bhd.", + [3]byte{8, 118, 149}: "Auto Industrial Co., Ltd.", + [3]byte{8, 118, 255}: "Thomson Telecom Belgium", + [3]byte{8, 121, 153}: "AIM GmbH", + [3]byte{8, 122, 76}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{8, 123, 170}: "SVYAZKOMPLEKTSERVICE, LLC", + [3]byte{8, 124, 190}: "Quintic Corp.", + [3]byte{8, 125, 33}: "Altasec technology corporation", + [3]byte{8, 128, 57}: "Cisco SPVTG", + [3]byte{8, 129, 188}: "HongKong Ipro Technology Co., Limited", + [3]byte{8, 129, 244}: "Juniper Networks", + [3]byte{8, 134, 32}: "TECNO MOBILE LIMITED", + [3]byte{8, 134, 59}: "Belkin International Inc.", + [3]byte{8, 140, 44}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 141, 200}: "Ryowa Electronics Co.,Ltd", + [3]byte{8, 142, 79}: "SF Software Solutions", + [3]byte{8, 143, 44}: "Hills Sound Vision & Lighting", + [3]byte{8, 148, 239}: "Wistron Infocomm (Zhongshan) Corporation", + [3]byte{8, 149, 42}: "Technicolor CH USA Inc.", + [3]byte{8, 150, 173}: "Cisco Systems, Inc", + [3]byte{8, 150, 215}: "AVM GmbH", + [3]byte{8, 151, 88}: "Shenzhen Strong Rising Electronics Co.,Ltd DongGuan Subsidiary", + [3]byte{8, 155, 75}: "iKuai Networks", + [3]byte{8, 158, 1}: "QUANTA COMPUTER INC.", + [3]byte{8, 158, 8}: "Google, Inc.", + [3]byte{8, 159, 151}: "LEROY AUTOMATION", + [3]byte{8, 161, 43}: "ShenZhen EZL Technology Co., Ltd", + [3]byte{8, 165, 200}: "Sunnovo International Limited", + [3]byte{8, 169, 90}: "AzureWave Technology Inc.", + [3]byte{8, 172, 165}: "Benu Video, Inc.", + [3]byte{8, 175, 120}: "Totus Solutions, Inc.", + [3]byte{8, 178, 88}: "Juniper Networks", + [3]byte{8, 178, 163}: "Cynny Italia S.r.L.", + [3]byte{8, 180, 207}: "Abicom International", + [3]byte{8, 183, 56}: "Lite-On Technogy Corp.", + [3]byte{8, 183, 236}: "Wireless Seismic", + [3]byte{8, 187, 204}: "AK-NORD EDV VERTRIEBSGES. mbH", + [3]byte{8, 189, 67}: "NETGEAR", + [3]byte{8, 190, 9}: "Astrol Electronic AG", + [3]byte{8, 190, 119}: "Green Electronics", + [3]byte{8, 192, 33}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{8, 198, 179}: "QTECH LLC", + [3]byte{8, 202, 69}: "Toyou Feiji Electronics Co., Ltd.", + [3]byte{8, 204, 104}: "Cisco Systems, Inc", + [3]byte{8, 204, 167}: "Cisco Systems, Inc", + [3]byte{8, 205, 155}: "samtec automotive electronics & software GmbH", + [3]byte{8, 208, 159}: "Cisco Systems, Inc", + [3]byte{8, 208, 183}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{8, 210, 154}: "Proformatique", + [3]byte{8, 211, 75}: "Techman Electronics (Changshu) Co., Ltd.", + [3]byte{8, 212, 12}: "Intel Corporate", + [3]byte{8, 212, 43}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 213, 192}: "Seers Technology Co., Ltd", + [3]byte{8, 216, 51}: "Shenzhen RF Technology Co., Ltd", + [3]byte{8, 223, 31}: "Bose Corporation", + [3]byte{8, 229, 218}: "NANJING FUJITSU COMPUTER PRODUCTS CO.,LTD. ", + [3]byte{8, 230, 114}: "JEBSEE ELECTRONICS CO.,LTD.", + [3]byte{8, 232, 79}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{8, 234, 64}: "SHENZHEN BILIAN ELECTRONIC CO.,LTD", + [3]byte{8, 234, 68}: "Aerohive Networks Inc.", + [3]byte{8, 235, 41}: "Jiangsu Huitong Group Co.,Ltd.", + [3]byte{8, 235, 116}: "HUMAX Co., Ltd.", + [3]byte{8, 235, 237}: "World Elite Technology Co.,LTD", + [3]byte{8, 236, 169}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 237, 2}: "IEEE Registration Authority", + [3]byte{8, 237, 185}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{8, 238, 139}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 239, 59}: "MCS Logic Inc.", + [3]byte{8, 239, 171}: "SAYME WIRELESS SENSOR NETWORK", + [3]byte{8, 241, 183}: "Towerstream Corpration", + [3]byte{8, 242, 244}: "Net One Partners Co.,Ltd.", + [3]byte{8, 246, 248}: "GET Engineering", + [3]byte{8, 247, 40}: "GLOBO Multimedia Sp. z o.o. Sp.k.", + [3]byte{8, 250, 224}: "Fohhn Audio AG", + [3]byte{8, 252, 82}: "OpenXS BV", + [3]byte{8, 252, 136}: "Samsung Electronics Co.,Ltd", + [3]byte{8, 253, 14}: "Samsung Electronics Co.,Ltd", + [3]byte{12, 2, 39}: "Technicolor CH USA Inc.", + [3]byte{12, 4, 0}: "Jantar d.o.o.", + [3]byte{12, 5, 53}: "Juniper Systems", + [3]byte{12, 17, 5}: "Ringslink (Xiamen) Network Communication Technologies Co., Ltd", + [3]byte{12, 17, 103}: "Cisco Systems, Inc", + [3]byte{12, 18, 98}: "zte corporation", + [3]byte{12, 19, 11}: "Uniqoteq Ltd.", + [3]byte{12, 20, 32}: "Samsung Electronics Co.,Ltd", + [3]byte{12, 21, 57}: "Apple, Inc.", + [3]byte{12, 21, 197}: "SDTEC Co., Ltd.", + [3]byte{12, 23, 241}: "TELECSYS", + [3]byte{12, 25, 31}: "Inform Electronik", + [3]byte{12, 26, 16}: "Acoustic Stream", + [3]byte{12, 29, 175}: "Xiaomi Communications Co Ltd", + [3]byte{12, 29, 194}: "SeAH Networks", + [3]byte{12, 32, 38}: "noax Technologies AG", + [3]byte{12, 37, 118}: "LONGCHEER TELECOMMUNICATION LIMITED", + [3]byte{12, 39, 36}: "Cisco Systems, Inc", + [3]byte{12, 39, 85}: "Valuable Techologies Limited", + [3]byte{12, 42, 105}: "electric imp, incorporated", + [3]byte{12, 42, 231}: "Beijing General Research Institute of Mining and Metallurgy", + [3]byte{12, 45, 137}: "QiiQ Communications Inc.", + [3]byte{12, 48, 33}: "Apple, Inc.", + [3]byte{12, 55, 220}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{12, 56, 62}: "Fanvil Technology Co., Ltd.", + [3]byte{12, 57, 86}: "Observator instruments", + [3]byte{12, 60, 101}: "Dome Imaging Inc", + [3]byte{12, 60, 205}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{12, 62, 159}: "Apple, Inc.", + [3]byte{12, 65, 62}: "Microsoft Corporation", + [3]byte{12, 69, 186}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{12, 70, 157}: "MS Sedco", + [3]byte{12, 71, 61}: "Hitron Technologies. Inc", + [3]byte{12, 71, 201}: "Amazon Technologies Inc.", + [3]byte{12, 72, 133}: "LG Electronics (Mobile Communications)", + [3]byte{12, 73, 51}: "Sichuan Jiuzhou Electronic Technology Co., Ltd.", + [3]byte{12, 76, 57}: "MitraStar Technology Corp.", + [3]byte{12, 77, 233}: "Apple, Inc.", + [3]byte{12, 79, 90}: "ASA-RT s.r.l.", + [3]byte{12, 81, 1}: "Apple, Inc.", + [3]byte{12, 81, 247}: "CHAUVIN ARNOUX", + [3]byte{12, 84, 165}: "PEGATRON CORPORATION", + [3]byte{12, 84, 185}: "Nokia", + [3]byte{12, 85, 33}: "Axiros GmbH", + [3]byte{12, 86, 92}: "HyBroad Vision (Hong Kong) Technology Co Ltd", + [3]byte{12, 87, 235}: "Mueller Systems", + [3]byte{12, 90, 25}: "Axtion Sdn Bhd", + [3]byte{12, 90, 158}: "Wi-SUN Alliance", + [3]byte{12, 92, 216}: "DOLI Elektronik GmbH", + [3]byte{12, 95, 53}: "Niagara Video Corporation", + [3]byte{12, 96, 118}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{12, 97, 39}: "Actiontec Electronics, Inc", + [3]byte{12, 97, 207}: "Texas Instruments", + [3]byte{12, 99, 252}: "Nanjing Signway Technology Co., Ltd", + [3]byte{12, 104, 3}: "Cisco Systems, Inc", + [3]byte{12, 106, 230}: "Stanley Security Solutions", + [3]byte{12, 110, 79}: "PrimeVOLT Co., Ltd.", + [3]byte{12, 111, 156}: "Shaw Communications Inc.", + [3]byte{12, 113, 93}: "Samsung Electronics Co.,Ltd", + [3]byte{12, 114, 44}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{12, 115, 190}: "Dongguan Haimai Electronie Technology Co.,Ltd", + [3]byte{12, 116, 194}: "Apple, Inc.", + [3]byte{12, 117, 35}: "BEIJING GEHUA CATV NETWORK CO.,LTD", + [3]byte{12, 117, 108}: "Anaren Microwave, Inc.", + [3]byte{12, 117, 189}: "Cisco Systems, Inc", + [3]byte{12, 119, 26}: "Apple, Inc.", + [3]byte{12, 125, 124}: "Kexiang Information Technology Co, Ltd.", + [3]byte{12, 129, 18}: "Private", + [3]byte{12, 130, 48}: "SHENZHEN MAGNUS TECHNOLOGIES CO.,LTD", + [3]byte{12, 130, 104}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{12, 130, 106}: "Wuhan Huagong Genuine Optics Technology Co., Ltd", + [3]byte{12, 132, 17}: "A.O. Smith Water Products", + [3]byte{12, 132, 132}: "Zenovia Electronics Inc.", + [3]byte{12, 132, 220}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{12, 133, 37}: "Cisco Systems, Inc", + [3]byte{12, 134, 16}: "Juniper Networks", + [3]byte{12, 137, 16}: "Samsung Electronics Co.,Ltd", + [3]byte{12, 138, 135}: "AgLogica Holdings, Inc", + [3]byte{12, 139, 253}: "Intel Corporate", + [3]byte{12, 140, 143}: "Kamo Technology Limited", + [3]byte{12, 140, 220}: "Suunto Oy", + [3]byte{12, 141, 152}: "TOP EIGHT IND CORP", + [3]byte{12, 141, 219}: "Cisco Meraki", + [3]byte{12, 145, 96}: "Hui Zhou Gaoshengda Technology Co.,LTD", + [3]byte{12, 146, 78}: "Rice Lake Weighing Systems", + [3]byte{12, 147, 1}: "PT. Prasimax Inovasi Teknologi", + [3]byte{12, 147, 251}: "BNS Solutions", + [3]byte{12, 150, 191}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{12, 155, 19}: "Shanghai Magic Mobile Telecommunication Co.Ltd.", + [3]byte{12, 157, 86}: "Consort Controls Ltd", + [3]byte{12, 158, 145}: "Sankosha Corporation", + [3]byte{12, 161, 56}: "Blinq Wireless Inc.", + [3]byte{12, 162, 244}: "Chameleon Technology (UK) Limited", + [3]byte{12, 164, 2}: "Alcatel-Lucent IPD", + [3]byte{12, 164, 42}: "OB Telecom Electronic Technology Co., Ltd", + [3]byte{12, 166, 148}: "Sunitec Enterprise Co.,Ltd", + [3]byte{12, 172, 5}: "Unitend Technologies Inc.", + [3]byte{12, 175, 90}: "GENUS POWER INFRASTRUCTURES LIMITED", + [3]byte{12, 179, 25}: "Samsung Electronics Co.,Ltd", + [3]byte{12, 180, 239}: "Digience Co.,Ltd.", + [3]byte{12, 181, 222}: "Alcatel Lucent", + [3]byte{12, 188, 159}: "Apple, Inc.", + [3]byte{12, 189, 81}: "TCT mobile ltd", + [3]byte{12, 191, 21}: "Genetec Inc.", + [3]byte{12, 191, 63}: "Shenzhen Lencotion Technology Co.,Ltd", + [3]byte{12, 192, 192}: "MAGNETI MARELLI SISTEMAS ELECTRONICOS MEXICO", + [3]byte{12, 195, 167}: "Meritec", + [3]byte{12, 196, 122}: "Super Micro Computer, Inc.", + [3]byte{12, 196, 126}: "EUCAST Co., Ltd.", + [3]byte{12, 198, 85}: "Wuxi YSTen Technology Co.,Ltd.", + [3]byte{12, 198, 106}: "Nokia Corporation", + [3]byte{12, 198, 172}: "DAGS", + [3]byte{12, 199, 49}: "Currant, Inc.", + [3]byte{12, 200, 31}: "Summer Infant, Inc.", + [3]byte{12, 201, 198}: "Samwin Hong Kong Limited", + [3]byte{12, 203, 141}: "ASCO Numatics GmbH", + [3]byte{12, 204, 38}: "Airenetworks", + [3]byte{12, 205, 211}: "EASTRIVER TECHNOLOGY CO., LTD.", + [3]byte{12, 205, 251}: "EDIC Systems Inc.", + [3]byte{12, 207, 209}: "SPRINGWAVE Co., Ltd", + [3]byte{12, 210, 146}: "Intel Corporate", + [3]byte{12, 210, 181}: "Binatone Telecommunication Pvt. Ltd", + [3]byte{12, 213, 2}: "Westell Technologies Inc.", + [3]byte{12, 214, 150}: "Amimon Ltd", + [3]byte{12, 214, 189}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{12, 215, 70}: "Apple, Inc.", + [3]byte{12, 215, 194}: "Axium Technologies, Inc.", + [3]byte{12, 216, 108}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{12, 217, 150}: "Cisco Systems, Inc", + [3]byte{12, 217, 193}: "Visteon Corporation", + [3]byte{12, 218, 65}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{12, 220, 204}: "Inala Technologies", + [3]byte{12, 221, 239}: "Nokia Corporation", + [3]byte{12, 223, 164}: "Samsung Electronics Co.,Ltd", + [3]byte{12, 224, 228}: "PLANTRONICS, INC.", + [3]byte{12, 229, 211}: "DH electronics GmbH", + [3]byte{12, 231, 9}: "Fox Crypto B.V.", + [3]byte{12, 231, 37}: "Microsoft Corporation", + [3]byte{12, 232, 47}: "Bonfiglioli Vectron GmbH", + [3]byte{12, 233, 54}: "ELIMOS srl", + [3]byte{12, 238, 230}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{12, 239, 124}: "AnaCom Inc", + [3]byte{12, 239, 175}: "IEEE Registration Authority", + [3]byte{12, 240, 25}: "Malgn Technology Co., Ltd.", + [3]byte{12, 240, 180}: "Globalsat International Technology Ltd", + [3]byte{12, 243, 97}: "Java Information", + [3]byte{12, 243, 238}: "EM Microelectronic", + [3]byte{12, 244, 5}: "Beijing Signalway Technologies Co.,Ltd", + [3]byte{12, 244, 213}: "Ruckus Wireless", + [3]byte{12, 245, 164}: "Cisco Systems, Inc", + [3]byte{12, 248, 147}: "ARRIS Group, Inc.", + [3]byte{12, 249, 192}: "BSkyB Ltd", + [3]byte{12, 252, 131}: "Airoha Technology Corp.,", + [3]byte{12, 253, 55}: "SUSE Linux GmbH", + [3]byte{12, 254, 69}: "Sony Interactive Entertainment Inc.", + [3]byte{16, 0, 0}: "Private", + [3]byte{16, 0, 90}: "IBM Corp", + [3]byte{16, 0, 232}: "NATIONAL SEMICONDUCTOR", + [3]byte{16, 0, 253}: "LaonPeople", + [3]byte{16, 1, 202}: "Ashley Butterworth", + [3]byte{16, 2, 181}: "Intel Corporate", + [3]byte{16, 5, 1}: "PEGATRON CORPORATION", + [3]byte{16, 5, 177}: "ARRIS Group, Inc.", + [3]byte{16, 5, 202}: "Cisco Systems, Inc", + [3]byte{16, 7, 35}: "IEEE Registration Authority", + [3]byte{16, 8, 177}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{16, 9, 12}: "Janome Sewing Machine Co., Ltd.", + [3]byte{16, 11, 169}: "Intel Corporate", + [3]byte{16, 12, 36}: "pomdevices, LLC", + [3]byte{16, 13, 47}: "Online Security Pty. Ltd.", + [3]byte{16, 13, 50}: "Embedian, Inc.", + [3]byte{16, 13, 127}: "NETGEAR", + [3]byte{16, 14, 43}: "NEC CASIO Mobile Communications", + [3]byte{16, 14, 126}: "Juniper Networks", + [3]byte{16, 15, 24}: "Fu Gang Electronic(KunShan)CO.,LTD", + [3]byte{16, 16, 182}: "McCain Inc", + [3]byte{16, 18, 18}: "Vivo International Corporation Pty Ltd", + [3]byte{16, 18, 24}: "Korins Inc.", + [3]byte{16, 18, 72}: "ITG, Inc.", + [3]byte{16, 18, 80}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{16, 19, 49}: "Technicolor", + [3]byte{16, 19, 238}: "Justec International Technology INC.", + [3]byte{16, 24, 158}: "Elmo Motion Control", + [3]byte{16, 27, 84}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{16, 28, 12}: "Apple, Inc.", + [3]byte{16, 29, 81}: "ON-Q LLC dba ON-Q Mesh Networks", + [3]byte{16, 29, 192}: "Samsung Electronics Co.,Ltd", + [3]byte{16, 31, 116}: "Hewlett Packard", + [3]byte{16, 34, 121}: "ZeroDesktop, Inc.", + [3]byte{16, 39, 190}: "TVIP", + [3]byte{16, 40, 49}: "Morion Inc.", + [3]byte{16, 42, 179}: "Xiaomi Communications Co Ltd", + [3]byte{16, 44, 131}: "XIMEA", + [3]byte{16, 45, 150}: "Looxcie Inc.", + [3]byte{16, 46, 175}: "Texas Instruments", + [3]byte{16, 47, 107}: "Microsoft Corporation", + [3]byte{16, 48, 71}: "Samsung Electronics Co.,Ltd", + [3]byte{16, 51, 120}: "FLECTRON Co., LTD", + [3]byte{16, 55, 17}: "Simlink AS", + [3]byte{16, 59, 89}: "Samsung Electronics Co.,Ltd", + [3]byte{16, 61, 234}: "HFC Technology (Beijing) Ltd. Co.", + [3]byte{16, 64, 243}: "Apple, Inc.", + [3]byte{16, 65, 127}: "Apple, Inc.", + [3]byte{16, 67, 105}: "Soundmax Electronic Limited ", + [3]byte{16, 68, 90}: "Shaanxi Hitech Electronic Co., LTD", + [3]byte{16, 69, 190}: "Norphonic AS", + [3]byte{16, 69, 248}: "LNT-Automation GmbH", + [3]byte{16, 71, 128}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{16, 72, 177}: "Beijing Duokan Technology Limited", + [3]byte{16, 74, 125}: "Intel Corporate", + [3]byte{16, 75, 70}: "Mitsubishi Electric Corporation", + [3]byte{16, 77, 119}: "Innovative Computer Engineering", + [3]byte{16, 78, 7}: "Shanghai Genvision Industries Co.,Ltd", + [3]byte{16, 79, 168}: "Sony Corporation", + [3]byte{16, 81, 114}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{16, 86, 17}: "ARRIS Group, Inc.", + [3]byte{16, 86, 202}: "Peplink International Ltd.", + [3]byte{16, 88, 135}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{16, 90, 247}: "ADB Italia ", + [3]byte{16, 92, 59}: "Perma-Pipe, Inc.", + [3]byte{16, 92, 191}: "DuroByte Inc", + [3]byte{16, 95, 6}: "Actiontec Electronics, Inc", + [3]byte{16, 95, 73}: "Cisco SPVTG", + [3]byte{16, 96, 75}: "Hewlett Packard", + [3]byte{16, 98, 201}: "Adatis GmbH & Co. KG", + [3]byte{16, 98, 235}: "D-Link International", + [3]byte{16, 100, 226}: "ADFweb.com s.r.l.", + [3]byte{16, 101, 163}: "Core Brands LLC", + [3]byte{16, 101, 207}: "IQSIM", + [3]byte{16, 102, 130}: "NEC Platforms, Ltd.", + [3]byte{16, 104, 63}: "LG Electronics (Mobile Communications)", + [3]byte{16, 111, 63}: "BUFFALO.INC", + [3]byte{16, 111, 239}: "Ad-Sol Nissin Corp", + [3]byte{16, 113, 249}: "Cloud Telecomputers, LLC", + [3]byte{16, 114, 35}: "TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO ", + [3]byte{16, 118, 138}: "EoCell", + [3]byte{16, 119, 176}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{16, 119, 177}: "Samsung Electronics Co.,Ltd", + [3]byte{16, 120, 91}: "Actiontec Electronics, Inc", + [3]byte{16, 120, 115}: "Shenzhen Jinkeyi Communication Co., Ltd.", + [3]byte{16, 120, 206}: "Hanvit SI, Inc.", + [3]byte{16, 120, 210}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{16, 122, 134}: "U&U ENGINEERING INC.", + [3]byte{16, 123, 239}: "ZyXEL Communications Corporation", + [3]byte{16, 125, 26}: "Dell Inc.", + [3]byte{16, 131, 210}: "Microseven Systems, LLC", + [3]byte{16, 134, 140}: "ARRIS Group, Inc.", + [3]byte{16, 136, 15}: "Daruma Telecomunicações e Informática S.A.", + [3]byte{16, 136, 206}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{16, 138, 27}: "RAONIX Inc.", + [3]byte{16, 140, 207}: "Cisco Systems, Inc", + [3]byte{16, 146, 102}: "Samsung Electronics Co.,Ltd", + [3]byte{16, 147, 233}: "Apple, Inc.", + [3]byte{16, 149, 75}: "Megabyte Ltd.", + [3]byte{16, 152, 54}: "Dell Inc.", + [3]byte{16, 154, 185}: "Tosibox Oy", + [3]byte{16, 154, 221}: "Apple, Inc.", + [3]byte{16, 159, 169}: "Actiontec Electronics, Inc", + [3]byte{16, 161, 59}: "FUJIKURA RUBBER LTD.", + [3]byte{16, 165, 208}: "Murata Manufacturing Co., Ltd.", + [3]byte{16, 166, 89}: "Mobile Create Co.,Ltd.", + [3]byte{16, 167, 67}: "SK Mtek Limited", + [3]byte{16, 169, 50}: "Beijing Cyber Cloud Technology Co. ,Ltd.", + [3]byte{16, 174, 96}: "Private", + [3]byte{16, 175, 120}: "Shenzhen ATUE Technology Co., Ltd", + [3]byte{16, 177, 248}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{16, 178, 107}: "base Co.,Ltd.", + [3]byte{16, 183, 19}: "Private", + [3]byte{16, 183, 246}: "Plastoform Industries Ltd.", + [3]byte{16, 185, 254}: "Lika srl", + [3]byte{16, 186, 165}: "GANA I&C CO., LTD", + [3]byte{16, 189, 24}: "Cisco Systems, Inc", + [3]byte{16, 189, 85}: "Q-Lab Corporation", + [3]byte{16, 190, 245}: "D-Link International", + [3]byte{16, 191, 72}: "ASUSTek COMPUTER INC.", + [3]byte{16, 192, 124}: "Blu-ray Disc Association", + [3]byte{16, 194, 186}: "UTT Co., Ltd.", + [3]byte{16, 195, 123}: "ASUSTek COMPUTER INC.", + [3]byte{16, 197, 134}: "BIO SOUND LAB CO., LTD.", + [3]byte{16, 198, 12}: "Domino UK Ltd", + [3]byte{16, 198, 31}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{16, 198, 126}: "SHENZHEN JUCHIN TECHNOLOGY CO., LTD", + [3]byte{16, 198, 252}: "Garmin International", + [3]byte{16, 199, 63}: "Midas Klark Teknik Ltd", + [3]byte{16, 202, 129}: "PRECIA", + [3]byte{16, 204, 27}: "Liverock technologies,INC", + [3]byte{16, 204, 219}: "AXIMUM PRODUITS ELECTRONIQUES", + [3]byte{16, 205, 174}: "Avaya Inc", + [3]byte{16, 208, 171}: "zte corporation", + [3]byte{16, 209, 220}: "INSTAR Deutschland GmbH", + [3]byte{16, 211, 138}: "Samsung Electronics Co.,Ltd", + [3]byte{16, 213, 66}: "Samsung Electronics Co.,Ltd", + [3]byte{16, 218, 67}: "NETGEAR", + [3]byte{16, 221, 177}: "Apple, Inc.", + [3]byte{16, 221, 244}: "Maxway Electronics CO.,LTD", + [3]byte{16, 222, 228}: "automationNEXT GmbH", + [3]byte{16, 223, 139}: "Shenzhen CareDear Communication Technology Co.,Ltd", + [3]byte{16, 226, 213}: "Qi Hardware Inc.", + [3]byte{16, 227, 199}: "Seohwa Telecom", + [3]byte{16, 228, 175}: "APR, LLC", + [3]byte{16, 230, 143}: "KWANGSUNG ELECTRONICS KOREA CO.,LTD.", + [3]byte{16, 230, 174}: "Source Technologies, LLC", + [3]byte{16, 232, 120}: "Nokia", + [3]byte{16, 232, 238}: "PhaseSpace", + [3]byte{16, 234, 89}: "Cisco SPVTG", + [3]byte{16, 238, 217}: "Canoga Perkins Corporation", + [3]byte{16, 240, 5}: "Intel Corporate", + [3]byte{16, 243, 17}: "Cisco Systems, Inc", + [3]byte{16, 243, 219}: "Gridco Systems, Inc.", + [3]byte{16, 244, 154}: "T3 Innovation", + [3]byte{16, 246, 129}: "vivo Mobile Communication Co., Ltd.", + [3]byte{16, 249, 111}: "LG Electronics (Mobile Communications)", + [3]byte{16, 249, 238}: "Nokia Corporation", + [3]byte{16, 250, 206}: "Reacheng Communication Technology Co.,Ltd", + [3]byte{16, 251, 240}: "KangSheng LTD.", + [3]byte{16, 252, 84}: "Shany Electronic Co., Ltd. ", + [3]byte{16, 254, 237}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{17, 0, 170}: "Private", + [3]byte{17, 17, 17}: "Private", + [3]byte{20, 2, 236}: "Hewlett Packard Enterprise", + [3]byte{20, 4, 103}: "SNK Technologies Co.,Ltd.", + [3]byte{20, 7, 8}: "Private", + [3]byte{20, 7, 224}: "Abrantix AG", + [3]byte{20, 12, 91}: "PLNetworks", + [3]byte{20, 12, 118}: "FREEBOX SAS", + [3]byte{20, 13, 79}: "Flextronics International", + [3]byte{20, 16, 159}: "Apple, Inc.", + [3]byte{20, 19, 48}: "Anakreon UK LLP", + [3]byte{20, 19, 87}: "ATP Electronics, Inc.", + [3]byte{20, 20, 75}: "FUJIAN STAR-NET COMMUNICATION CO.,LTD", + [3]byte{20, 20, 230}: "Ningbo Sanhe Digital Co.,Ltd", + [3]byte{20, 21, 124}: "TOKYO COSMOS ELECTRIC CO.,LTD.", + [3]byte{20, 24, 119}: "Dell Inc.", + [3]byte{20, 26, 81}: "Treetech Sistemas Digitais", + [3]byte{20, 26, 163}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{20, 27, 189}: "Volex Inc.", + [3]byte{20, 27, 240}: "Intellimedia Systems Ltd", + [3]byte{20, 31, 120}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 31, 186}: "IEEE Registration Authority", + [3]byte{20, 34, 219}: "eero inc.", + [3]byte{20, 35, 215}: "EUTRONIX CO., LTD.", + [3]byte{20, 41, 113}: "NEMOA ELECTRONICS (HK) CO. LTD", + [3]byte{20, 43, 210}: "Armtel Ltd.", + [3]byte{20, 43, 214}: "Guangdong Appscomm Co.,Ltd", + [3]byte{20, 45, 39}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{20, 45, 139}: "Incipio Technologies, Inc", + [3]byte{20, 45, 245}: "Amphitech", + [3]byte{20, 47, 253}: "LT SECURITY INC", + [3]byte{20, 48, 4}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{20, 48, 122}: "Avermetrics", + [3]byte{20, 48, 198}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{20, 50, 209}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 51, 101}: "TEM Mobile Limited", + [3]byte{20, 53, 139}: "Mediabridge Products, LLC.", + [3]byte{20, 53, 179}: "Future Designs, Inc.", + [3]byte{20, 54, 5}: "Nokia Corporation", + [3]byte{20, 54, 198}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{20, 55, 59}: "PROCOM Systems", + [3]byte{20, 58, 234}: "Dynapower Company LLC", + [3]byte{20, 61, 242}: "Beijing Shidai Hongyuan Network Communication Co.,Ltd", + [3]byte{20, 62, 96}: "Nokia", + [3]byte{20, 62, 191}: "zte corporation", + [3]byte{20, 63, 39}: "Noccela Oy", + [3]byte{20, 65, 70}: "Honeywell (China) Co., LTD", + [3]byte{20, 65, 226}: "Monaco Enterprises, Inc.", + [3]byte{20, 67, 25}: "Creative&Link Technology Limited", + [3]byte{20, 70, 228}: "AVISTEL", + [3]byte{20, 72, 139}: "Shenzhen Doov Technology Co.,Ltd", + [3]byte{20, 73, 120}: "Digital Control Incorporated", + [3]byte{20, 73, 224}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{20, 76, 26}: "Max Communication GmbH", + [3]byte{20, 77, 103}: "Zioncom Electronics (Shenzhen) Ltd.", + [3]byte{20, 79, 215}: "IEEE Registration Authority", + [3]byte{20, 84, 18}: "Entis Co., Ltd.", + [3]byte{20, 86, 69}: "Savitech Corp.", + [3]byte{20, 86, 142}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 88, 208}: "Hewlett Packard", + [3]byte{20, 90, 5}: "Apple, Inc.", + [3]byte{20, 90, 131}: "Logi-D inc", + [3]byte{20, 91, 209}: "ARRIS Group, Inc.", + [3]byte{20, 95, 148}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{20, 96, 128}: "zte corporation", + [3]byte{20, 97, 2}: "Alpine Electronics, Inc.", + [3]byte{20, 97, 47}: "Avaya Inc", + [3]byte{20, 99, 8}: "JABIL CIRCUIT (SHANGHAI) LTD.", + [3]byte{20, 106, 11}: "Cypress Electronics Limited", + [3]byte{20, 107, 114}: "Shenzhen Fortune Ship Technology Co., Ltd.", + [3]byte{20, 110, 10}: "Private", + [3]byte{20, 115, 115}: "TUBITAK UEKAE", + [3]byte{20, 116, 17}: "RIM", + [3]byte{20, 117, 144}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{20, 125, 179}: "JOA TELECOM.CO.,LTD", + [3]byte{20, 125, 197}: "Murata Manufacturing Co., Ltd.", + [3]byte{20, 130, 91}: "Hefei Radio Communication Technology Co., Ltd ", + [3]byte{20, 134, 146}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{20, 137, 62}: "VIXTEL TECHNOLOGIES LIMTED", + [3]byte{20, 137, 81}: "LCFC(HeFei) Electronics Technology co., ltd", + [3]byte{20, 137, 253}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 138, 112}: "ADS GmbH", + [3]byte{20, 143, 33}: "Garmin International", + [3]byte{20, 143, 198}: "Apple, Inc.", + [3]byte{20, 144, 144}: "KongTop industrial(shen zhen)CO.,LTD", + [3]byte{20, 145, 130}: "Belkin International Inc.", + [3]byte{20, 148, 72}: "BLU CASTLE S.A.", + [3]byte{20, 152, 125}: "Technicolor CH USA Inc.", + [3]byte{20, 153, 226}: "Apple, Inc.", + [3]byte{20, 154, 16}: "Microsoft Corporation", + [3]byte{20, 157, 9}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{20, 158, 207}: "Dell Inc.", + [3]byte{20, 159, 232}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{20, 160, 248}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{20, 163, 100}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 165, 26}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{20, 166, 44}: "S.M. Dezac S.A.", + [3]byte{20, 167, 139}: "Zhejiang Dahua Technology Co., Ltd.", + [3]byte{20, 168, 107}: "ShenZhen Telacom Science&Technology Co., Ltd", + [3]byte{20, 169, 227}: "MST CORPORATION", + [3]byte{20, 171, 197}: "Intel Corporate", + [3]byte{20, 171, 240}: "ARRIS Group, Inc.", + [3]byte{20, 174, 219}: "VTech Telecommunications Ltd.", + [3]byte{20, 177, 38}: "Industrial Software Co", + [3]byte{20, 177, 200}: "InfiniWing, Inc.", + [3]byte{20, 179, 31}: "Dell Inc.", + [3]byte{20, 179, 112}: "Gigaset Digital Technology (Shenzhen) Co., Ltd.", + [3]byte{20, 180, 132}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 183, 61}: "ARCHEAN Technologies", + [3]byte{20, 183, 248}: "Technicolor CH USA Inc.", + [3]byte{20, 184, 55}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{20, 185, 104}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{20, 187, 110}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 189, 97}: "Apple, Inc.", + [3]byte{20, 192, 137}: "DUNE HD LTD", + [3]byte{20, 193, 38}: "Nokia Corporation", + [3]byte{20, 193, 255}: "ShenZhen QianHai Comlan communication Co.,LTD", + [3]byte{20, 194, 29}: "Sabtech Industries", + [3]byte{20, 195, 194}: "K.A. Schmersal GmbH & Co. KG", + [3]byte{20, 201, 19}: "LG Electronics", + [3]byte{20, 204, 32}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{20, 207, 141}: "OHSUNG ELECTRONICS CO., LTD.", + [3]byte{20, 207, 146}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{20, 207, 226}: "ARRIS Group, Inc.", + [3]byte{20, 209, 31}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{20, 212, 254}: "ARRIS Group, Inc.", + [3]byte{20, 214, 77}: "D-Link International", + [3]byte{20, 215, 110}: "CONCH ELECTRONIC Co.,Ltd", + [3]byte{20, 218, 233}: "ASUSTek COMPUTER INC.", + [3]byte{20, 219, 133}: "S NET MEDIA", + [3]byte{20, 221, 169}: "ASUSTek COMPUTER INC.", + [3]byte{20, 221, 229}: "MPMKVVCL", + [3]byte{20, 228, 236}: "mLogic LLC", + [3]byte{20, 230, 228}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{20, 231, 200}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{20, 235, 51}: "BSMediasoft Co., Ltd.", + [3]byte{20, 237, 165}: "Wächter GmbH Sicherheitssysteme", + [3]byte{20, 237, 187}: "2Wire Inc", + [3]byte{20, 237, 228}: "Kaiam Corporation", + [3]byte{20, 238, 157}: "AirNav Systems LLC", + [3]byte{20, 240, 197}: "Xtremio Ltd.", + [3]byte{20, 242, 142}: "ShenYang ZhongKe-Allwin Technology Co.LTD", + [3]byte{20, 244, 42}: "Samsung Electronics Co.,Ltd", + [3]byte{20, 246, 90}: "Xiaomi Communications Co Ltd", + [3]byte{20, 248, 147}: "Wuhan FiberHome Digital Technology Co.,Ltd.", + [3]byte{20, 254, 175}: "SAGITTAR LIMITED", + [3]byte{20, 254, 181}: "Dell Inc.", + [3]byte{24, 0, 45}: "Sony Mobile Communications AB", + [3]byte{24, 0, 219}: "Fitbit Inc.", + [3]byte{24, 1, 125}: "Harbin Arteor technology co., LTD", + [3]byte{24, 1, 227}: "Bittium Wireless Ltd", + [3]byte{24, 3, 115}: "Dell Inc.", + [3]byte{24, 3, 250}: "IBT Interfaces", + [3]byte{24, 6, 117}: "Dilax Intelcom GmbH", + [3]byte{24, 11, 82}: "Nanotron Technologies GmbH", + [3]byte{24, 12, 20}: "iSonea Limited", + [3]byte{24, 12, 119}: "Westinghouse Electric Company, LLC", + [3]byte{24, 12, 172}: "CANON INC.", + [3]byte{24, 16, 78}: "CEDINT-UPM", + [3]byte{24, 18, 18}: "Cepton Technologies", + [3]byte{24, 20, 32}: "TEB SAS", + [3]byte{24, 20, 86}: "Nokia Corporation", + [3]byte{24, 22, 201}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 23, 20}: "DAEWOOIS", + [3]byte{24, 23, 37}: "Cameo Communications, Inc.", + [3]byte{24, 25, 63}: "Tamtron Oy", + [3]byte{24, 27, 235}: "Actiontec Electronics, Inc", + [3]byte{24, 30, 120}: "Sagemcom Broadband SAS", + [3]byte{24, 30, 176}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 32, 18}: "Aztech Associates Inc.", + [3]byte{24, 32, 50}: "Apple, Inc.", + [3]byte{24, 32, 166}: "Sage Co., Ltd.", + [3]byte{24, 33, 149}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 34, 126}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 38, 102}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 40, 97}: "AirTies Wireless Networks", + [3]byte{24, 42, 123}: "Nintendo Co., Ltd.", + [3]byte{24, 43, 5}: "8D Technologies", + [3]byte{24, 44, 145}: "Concept Development, Inc.", + [3]byte{24, 48, 9}: "Woojin Industrial Systems Co., Ltd.", + [3]byte{24, 50, 162}: "LAON TECHNOLOGY CO., LTD.", + [3]byte{24, 51, 157}: "Cisco Systems, Inc", + [3]byte{24, 52, 81}: "Apple, Inc.", + [3]byte{24, 54, 252}: "Elecsys International Corporation", + [3]byte{24, 56, 37}: "Wuhan Lingjiu High-tech Co.,Ltd.", + [3]byte{24, 56, 100}: "CAP-TECH INTERNATIONAL CO., LTD.", + [3]byte{24, 57, 25}: "Unicoi Systems", + [3]byte{24, 58, 45}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 59, 210}: "BYD Precision Manufacture Company Ltd.", + [3]byte{24, 61, 162}: "Intel Corporate", + [3]byte{24, 63, 71}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 64, 164}: "Shenzhen Trylong Smart Science and Technology Co., Ltd.", + [3]byte{24, 66, 29}: "Private", + [3]byte{24, 66, 47}: "Alcatel Lucent", + [3]byte{24, 68, 98}: "Riava Networks, Inc.", + [3]byte{24, 68, 230}: "zte corporation", + [3]byte{24, 70, 23}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 72, 216}: "Fastback Networks", + [3]byte{24, 74, 111}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{24, 78, 148}: "MESSOA TECHNOLOGIES INC.", + [3]byte{24, 79, 50}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{24, 82, 7}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{24, 82, 83}: "Pixord Corporation", + [3]byte{24, 83, 224}: "Hanyang Digitech Co.Ltd", + [3]byte{24, 85, 15}: "Cisco SPVTG", + [3]byte{24, 89, 51}: "Cisco SPVTG", + [3]byte{24, 89, 54}: "Xiaomi Communications Co Ltd", + [3]byte{24, 90, 232}: "Zenotech.Co.,Ltd", + [3]byte{24, 93, 154}: "BobjGear LLC", + [3]byte{24, 94, 15}: "Intel Corporate", + [3]byte{24, 97, 199}: "lemonbeat GmbH", + [3]byte{24, 98, 44}: "Sagemcom Broadband SAS", + [3]byte{24, 100, 114}: "Aruba Networks", + [3]byte{24, 101, 113}: "Top Victory Electronics (Taiwan) Co., Ltd.", + [3]byte{24, 101, 144}: "Apple, Inc.", + [3]byte{24, 102, 218}: "Dell Inc.", + [3]byte{24, 102, 227}: "Veros Systems, Inc.", + [3]byte{24, 103, 63}: "Hanover Displays Limited", + [3]byte{24, 103, 81}: "KOMEG Industrielle Messtechnik GmbH", + [3]byte{24, 103, 176}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 104, 106}: "zte corporation", + [3]byte{24, 104, 130}: "Beward R&D Co., Ltd.", + [3]byte{24, 104, 203}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{24, 109, 153}: "Adanis Inc.", + [3]byte{24, 113, 23}: "eta plus electronic gmbh", + [3]byte{24, 117, 50}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{24, 121, 162}: "GMJ ELECTRIC LIMITED", + [3]byte{24, 122, 147}: "AMICCOM Electronics Corporation", + [3]byte{24, 124, 129}: "Valeo Vision Systems", + [3]byte{24, 126, 213}: "shenzhen kaism technology Co. Ltd", + [3]byte{24, 128, 206}: "Barberry Solutions Ltd", + [3]byte{24, 128, 245}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{24, 130, 25}: "Alibaba Cloud Computing Ltd.", + [3]byte{24, 131, 49}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 131, 191}: "Arcadyan Technology Corporation", + [3]byte{24, 132, 16}: "CoreTrust Inc.", + [3]byte{24, 134, 58}: "DIGITAL ART SYSTEM", + [3]byte{24, 134, 172}: "Nokia Danmark A/S", + [3]byte{24, 135, 150}: "HTC Corporation", + [3]byte{24, 136, 87}: "Beijing Jinhong Xi-Dian Information Technology Corp.", + [3]byte{24, 137, 91}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 137, 223}: "CerebrEX Inc.", + [3]byte{24, 139, 21}: "ShenZhen ZhongRuiJing Technology co.,LTD", + [3]byte{24, 139, 69}: "Cisco Systems, Inc", + [3]byte{24, 139, 157}: "Cisco Systems, Inc", + [3]byte{24, 142, 213}: "TP Vision Belgium N.V. - innovation site Brugge", + [3]byte{24, 142, 249}: "G2C Co. Ltd.", + [3]byte{24, 146, 44}: "Virtual Instruments", + [3]byte{24, 151, 255}: "TechFaith Wireless Technology Limited", + [3]byte{24, 153, 245}: "Sichuan Changhong Electric Ltd.", + [3]byte{24, 154, 103}: "CSE-Servelec Limited", + [3]byte{24, 156, 93}: "Cisco Systems, Inc", + [3]byte{24, 158, 252}: "Apple, Inc.", + [3]byte{24, 163, 232}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{24, 166, 247}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{24, 169, 5}: "Hewlett Packard", + [3]byte{24, 169, 88}: "PROVISION THAI CO., LTD.", + [3]byte{24, 169, 155}: "Dell Inc.", + [3]byte{24, 170, 69}: "Fon Technology", + [3]byte{24, 171, 245}: "Ultra Electronics Electrics", + [3]byte{24, 173, 77}: "Polostar Technology Corporation", + [3]byte{24, 174, 187}: "Siemens Convergence Creators GmbH&Co.KG", + [3]byte{24, 175, 97}: "Apple, Inc.", + [3]byte{24, 175, 143}: "Apple, Inc.", + [3]byte{24, 175, 159}: "DIGITRONIC Automationsanlagen GmbH", + [3]byte{24, 177, 105}: "Sonicwall", + [3]byte{24, 178, 9}: "Torrey Pines Logic, Inc", + [3]byte{24, 179, 186}: "Netlogic AB", + [3]byte{24, 180, 48}: "Nest Labs Inc.", + [3]byte{24, 181, 145}: "I-Storm", + [3]byte{24, 183, 158}: "Invoxia", + [3]byte{24, 189, 173}: "L-TECH CORPORATION", + [3]byte{24, 192, 134}: "Broadcom", + [3]byte{24, 196, 81}: "Tucson Embedded Systems", + [3]byte{24, 197, 1}: "SHENZHEN GONGJIN ELECTRONICS CO.,LT", + [3]byte{24, 197, 138}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{24, 200, 231}: "Shenzhen Hualistone Technology Co.,Ltd", + [3]byte{24, 204, 35}: "Philio Technology Corporation", + [3]byte{24, 207, 94}: "Liteon Technology Corporation", + [3]byte{24, 208, 113}: "DASAN CO., LTD.", + [3]byte{24, 210, 118}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{24, 213, 182}: "SMG Holdings LLC", + [3]byte{24, 214, 106}: "Inmarsat", + [3]byte{24, 214, 199}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{24, 214, 207}: "Kurth Electronic GmbH", + [3]byte{24, 217, 73}: "Qvis Labs, LLC", + [3]byte{24, 219, 242}: "Dell Inc.", + [3]byte{24, 220, 86}: "Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd", + [3]byte{24, 222, 215}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{24, 226, 136}: "STT Condigi", + [3]byte{24, 226, 159}: "vivo Mobile Communication Co., Ltd.", + [3]byte{24, 226, 194}: "Samsung Electronics Co.,Ltd", + [3]byte{24, 227, 188}: "TCT mobile ltd", + [3]byte{24, 231, 40}: "Cisco Systems, Inc", + [3]byte{24, 231, 244}: "Apple, Inc.", + [3]byte{24, 232, 15}: "Viking Electronics Inc.", + [3]byte{24, 232, 221}: "MODULETEK", + [3]byte{24, 238, 105}: "Apple, Inc.", + [3]byte{24, 239, 99}: "Cisco Systems, Inc", + [3]byte{24, 241, 69}: "NetComm Wireless Limited", + [3]byte{24, 242, 146}: "Shannon Systems", + [3]byte{24, 244, 106}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{24, 246, 67}: "Apple, Inc.", + [3]byte{24, 246, 80}: "Multimedia Pacific Limited", + [3]byte{24, 247, 107}: "Zhejiang Winsight Technology CO.,LTD", + [3]byte{24, 248, 122}: "i3 International Inc.", + [3]byte{24, 250, 111}: "ISC applied systems corp", + [3]byte{24, 251, 123}: "Dell Inc.", + [3]byte{24, 252, 159}: "Changhe Electronics Co., Ltd.", + [3]byte{24, 254, 52}: "Espressif Inc.", + [3]byte{24, 255, 15}: "Intel Corporate", + [3]byte{24, 255, 46}: "Shenzhen Rui Ying Da Technology Co., Ltd", + [3]byte{28, 6, 86}: "IDY Corporation", + [3]byte{28, 8, 193}: "Lg Innotek", + [3]byte{28, 11, 82}: "EPICOM S.A", + [3]byte{28, 15, 207}: "Sypro Optics GmbH", + [3]byte{28, 17, 225}: "Wartsila Finland Oy", + [3]byte{28, 18, 157}: "IEEE PES PSRC/SUB ", + [3]byte{28, 20, 72}: "ARRIS Group, Inc.", + [3]byte{28, 20, 179}: "Airwire Technologies", + [3]byte{28, 23, 211}: "Cisco Systems, Inc", + [3]byte{28, 24, 74}: "ShenZhen RicherLink Technologies Co.,LTD", + [3]byte{28, 25, 222}: "eyevis GmbH", + [3]byte{28, 26, 192}: "Apple, Inc.", + [3]byte{28, 27, 13}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{28, 27, 104}: "ARRIS Group, Inc.", + [3]byte{28, 28, 253}: "Dalian Hi-Think Computer Technology, Corp", + [3]byte{28, 29, 103}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{28, 29, 134}: "Cisco Systems, Inc", + [3]byte{28, 30, 227}: "Hui Zhou Gaoshengda Technology Co.,LTD", + [3]byte{28, 33, 209}: "IEEE Registration Authority", + [3]byte{28, 35, 44}: "Samsung Electronics Co.,Ltd", + [3]byte{28, 35, 79}: "EDMI Europe Ltd", + [3]byte{28, 37, 225}: "China Mobile IOT Company Limited", + [3]byte{28, 51, 14}: "PernixData", + [3]byte{28, 51, 77}: "ITS Telecom", + [3]byte{28, 52, 119}: "Innovation Wireless", + [3]byte{28, 53, 241}: "NEW Lift Neue Elektronische Wege Steuerungsbau GmbH", + [3]byte{28, 55, 191}: "Cloudium Systems Ltd.", + [3]byte{28, 57, 71}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{28, 57, 138}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{28, 58, 79}: "AccuSpec Electronics, LLC", + [3]byte{28, 58, 222}: "Samsung Electronics Co.,Ltd", + [3]byte{28, 61, 231}: "Sigma Koki Co.,Ltd.", + [3]byte{28, 62, 132}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{28, 64, 36}: "Dell Inc.", + [3]byte{28, 64, 232}: "SHENZHEN PROGRESS&WIN TECHNOLOGY CO.,LTD", + [3]byte{28, 65, 88}: "Gemalto M2M GmbH", + [3]byte{28, 67, 236}: "JAPAN CIRCUIT CO.,LTD", + [3]byte{28, 68, 25}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{28, 69, 147}: "Texas Instruments", + [3]byte{28, 72, 64}: "IMS Messsysteme GmbH", + [3]byte{28, 72, 206}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{28, 72, 249}: "GN Netcom A/S", + [3]byte{28, 73, 123}: "Gemtek Technology Co., Ltd.", + [3]byte{28, 74, 247}: "AMON INC", + [3]byte{28, 75, 185}: "SMG ENTERPRISE, LLC", + [3]byte{28, 75, 214}: "AzureWave Technology Inc.", + [3]byte{28, 81, 181}: "Techaya LTD", + [3]byte{28, 82, 22}: "DONGGUAN HELE ELECTRONICS CO., LTD", + [3]byte{28, 82, 214}: "FLAT DISPLAY TECHNOLOGY CORPORATION", + [3]byte{28, 85, 58}: "QianGua Corp.", + [3]byte{28, 86, 254}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{28, 87, 216}: "Kraftway Corporation PLC", + [3]byte{28, 90, 11}: "Tegile Systems", + [3]byte{28, 90, 62}: "Samsung Electronics Co.,Ltd", + [3]byte{28, 90, 107}: "Philips Electronics Nederland BV", + [3]byte{28, 92, 85}: "PRIMA Cinema, Inc", + [3]byte{28, 92, 96}: "Shenzhen Belzon Technology Co.,LTD.", + [3]byte{28, 92, 242}: "Apple, Inc.", + [3]byte{28, 95, 43}: "D-Link International", + [3]byte{28, 95, 255}: "Beijing Ereneben Information Technology Co.,Ltd Shenzhen Branch", + [3]byte{28, 96, 222}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{28, 98, 184}: "Samsung Electronics Co.,Ltd", + [3]byte{28, 99, 183}: "OpenProducts 237 AB", + [3]byte{28, 101, 157}: "Liteon Technology Corporation", + [3]byte{28, 102, 109}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{28, 102, 170}: "Samsung Electronics Co.,Ltd", + [3]byte{28, 103, 88}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{28, 105, 165}: "BlackBerry RTS", + [3]byte{28, 106, 122}: "Cisco Systems, Inc", + [3]byte{28, 107, 202}: "Mitsunami Co., Ltd.", + [3]byte{28, 110, 76}: "Logistic Service & Engineering Co.,Ltd", + [3]byte{28, 110, 118}: "Quarion Technology Inc", + [3]byte{28, 111, 101}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{28, 115, 112}: "Neotech", + [3]byte{28, 116, 13}: "ZyXEL Communications Corporation", + [3]byte{28, 117, 8}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{28, 118, 202}: "Terasic Technologies Inc.", + [3]byte{28, 119, 246}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{28, 120, 57}: "Shenzhen Tencent Computer System Co., Ltd.", + [3]byte{28, 123, 33}: "Sony Mobile Communications AB", + [3]byte{28, 123, 35}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{28, 124, 17}: "EID ", + [3]byte{28, 124, 69}: "Vitek Industrial Video Products, Inc.", + [3]byte{28, 124, 199}: "Coriant GmbH", + [3]byte{28, 125, 34}: "Fuji Xerox Co., Ltd.", + [3]byte{28, 126, 81}: "3bumen.com", + [3]byte{28, 126, 229}: "D-Link International", + [3]byte{28, 131, 65}: "Hefei Bitland Information Technology Co.Ltd", + [3]byte{28, 131, 176}: "Linked IP GmbH", + [3]byte{28, 132, 100}: "FORMOSA WIRELESS COMMUNICATION CORP.", + [3]byte{28, 134, 173}: "MCT CO., LTD.", + [3]byte{28, 135, 44}: "ASUSTek COMPUTER INC.", + [3]byte{28, 142, 92}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{28, 142, 142}: "DB Communication & Systems Co., ltd.", + [3]byte{28, 143, 138}: "Phase Motion Control SpA", + [3]byte{28, 145, 72}: "Apple, Inc.", + [3]byte{28, 145, 121}: "Integrated System Technologies Ltd", + [3]byte{28, 148, 146}: "RUAG Schweiz AG", + [3]byte{28, 149, 93}: "I-LAX ELECTRONICS INC.", + [3]byte{28, 149, 159}: "Veethree Electronics And Marine LLC", + [3]byte{28, 150, 90}: "Weifang goertek Electronics CO.,LTD", + [3]byte{28, 151, 61}: "PRICOM Design", + [3]byte{28, 152, 236}: "Hewlett Packard Enterprise", + [3]byte{28, 153, 76}: "Murata Manufacturing Co., Ltd.", + [3]byte{28, 156, 38}: "Zoovel Technologies", + [3]byte{28, 157, 62}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{28, 158, 70}: "Apple, Inc.", + [3]byte{28, 158, 203}: "Beijing Nari Smartchip Microelectronics Company Limited", + [3]byte{28, 160, 211}: "IEEE Registration Authority", + [3]byte{28, 162, 177}: "ruwido austria gmbh", + [3]byte{28, 165, 50}: "Shenzhen Gongjin Electronics Co.,Ltd", + [3]byte{28, 167, 112}: "SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD", + [3]byte{28, 170, 7}: "Cisco Systems, Inc", + [3]byte{28, 171, 1}: "Innovolt", + [3]byte{28, 171, 167}: "Apple, Inc.", + [3]byte{28, 171, 192}: "Hitron Technologies. Inc", + [3]byte{28, 173, 209}: "Bosung Electronics Co., Ltd.", + [3]byte{28, 175, 5}: "Samsung Electronics Co.,Ltd", + [3]byte{28, 175, 247}: "D-Link International", + [3]byte{28, 176, 148}: "HTC Corporation", + [3]byte{28, 177, 127}: "NEC Platforms, Ltd.", + [3]byte{28, 178, 67}: "TDC A/S", + [3]byte{28, 183, 44}: "ASUSTek COMPUTER INC.", + [3]byte{28, 184, 87}: "Becon Technologies Co,.Ltd.", + [3]byte{28, 185, 196}: "Ruckus Wireless", + [3]byte{28, 186, 140}: "Texas Instruments", + [3]byte{28, 187, 168}: "OJSC Ufimskiy Zavod Promsvyaz", + [3]byte{28, 189, 14}: "Amplified Engineering Pty Ltd", + [3]byte{28, 189, 185}: "D-Link International", + [3]byte{28, 192, 53}: "PLANEX COMMUNICATIONS INC.", + [3]byte{28, 192, 225}: "IEEE Registration Authority", + [3]byte{28, 193, 26}: "Wavetronix", + [3]byte{28, 193, 222}: "Hewlett Packard", + [3]byte{28, 195, 22}: "MileSight Technology Co., Ltd.", + [3]byte{28, 197, 134}: "Absolute Acoustics", + [3]byte{28, 198, 60}: "Arcadyan Technology Corporation", + [3]byte{28, 199, 45}: "Shenzhen Huapu Digital CO.,Ltd", + [3]byte{28, 202, 227}: "IEEE Registration Authority", + [3]byte{28, 203, 153}: "TCT mobile ltd", + [3]byte{28, 205, 229}: "Shanghai Wind Technologies Co.,Ltd", + [3]byte{28, 212, 12}: "Kriwan Industrie-Elektronik GmbH", + [3]byte{28, 214, 189}: "LEEDARSON LIGHTING CO., LTD.", + [3]byte{28, 218, 39}: "vivo Mobile Communication Co., Ltd.", + [3]byte{28, 222, 167}: "Cisco Systems, Inc", + [3]byte{28, 223, 15}: "Cisco Systems, Inc", + [3]byte{28, 225, 101}: "Marshal Corporation", + [3]byte{28, 225, 146}: "Qisda Corporation", + [3]byte{28, 226, 204}: "Texas Instruments", + [3]byte{28, 230, 43}: "Apple, Inc.", + [3]byte{28, 230, 199}: "Cisco Systems, Inc", + [3]byte{28, 232, 93}: "Cisco Systems, Inc", + [3]byte{28, 234, 27}: "Nokia", + [3]byte{28, 238, 201}: "Elo touch solutions", + [3]byte{28, 238, 232}: "Ilshin Elecom", + [3]byte{28, 239, 206}: "bebro electronic GmbH", + [3]byte{28, 240, 62}: "Wearhaus Inc.", + [3]byte{28, 240, 97}: "SCAPS GmbH", + [3]byte{28, 244, 202}: "Private", + [3]byte{28, 245, 231}: "Turtle Industry Co., Ltd.", + [3]byte{28, 250, 104}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{28, 252, 187}: "Realfiction ApS", + [3]byte{28, 254, 167}: "IDentytech Solutins Ltd.", + [3]byte{32, 1, 79}: "Linea Research Ltd", + [3]byte{32, 2, 175}: "Murata Manufacturing Co., Ltd.", + [3]byte{32, 5, 5}: "RADMAX COMMUNICATION PRIVATE LIMITED", + [3]byte{32, 5, 232}: "OOO InProMedia", + [3]byte{32, 8, 237}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{32, 10, 94}: "Xiangshan Giant Eagle Technology Developing Co., Ltd.", + [3]byte{32, 11, 199}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{32, 12, 200}: "NETGEAR", + [3]byte{32, 14, 149}: "IEC – TC9 WG43", + [3]byte{32, 16, 122}: "Gemtek Technology Co., Ltd.", + [3]byte{32, 18, 87}: "Most Lucky Trading Ltd", + [3]byte{32, 18, 213}: "Scientech Materials Corporation", + [3]byte{32, 19, 224}: "Samsung Electronics Co.,Ltd", + [3]byte{32, 22, 216}: "Liteon Technology Corporation", + [3]byte{32, 24, 14}: "Shenzhen Sunchip Technology Co., Ltd", + [3]byte{32, 26, 6}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{32, 29, 3}: "Elatec GmbH", + [3]byte{32, 33, 165}: "LG Electronics (Mobile Communications)", + [3]byte{32, 37, 100}: "PEGATRON CORPORATION", + [3]byte{32, 37, 152}: "Teleview", + [3]byte{32, 40, 188}: "Visionscape Co,. Ltd.", + [3]byte{32, 43, 193}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{32, 44, 183}: "Kong Yue Electronics & Information Industry (Xinhui) Ltd.", + [3]byte{32, 45, 7}: "Samsung Electronics Co.,Ltd", + [3]byte{32, 45, 248}: "Digital Media Cartridge Ltd.", + [3]byte{32, 49, 235}: "HDSN", + [3]byte{32, 55, 6}: "Cisco Systems, Inc", + [3]byte{32, 55, 188}: "Kuipers Electronic Engineering BV", + [3]byte{32, 58, 7}: "Cisco Systems, Inc", + [3]byte{32, 58, 239}: "Sivantos GmbH", + [3]byte{32, 60, 174}: "Apple, Inc.", + [3]byte{32, 61, 102}: "ARRIS Group, Inc.", + [3]byte{32, 61, 178}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{32, 64, 5}: "feno GmbH", + [3]byte{32, 65, 90}: "Smarteh d.o.o.", + [3]byte{32, 68, 58}: "Schneider Electric Asia Pacific Ltd", + [3]byte{32, 70, 161}: "VECOW Co., Ltd", + [3]byte{32, 70, 249}: "Advanced Network Devices (dba:AND)", + [3]byte{32, 71, 71}: "Dell Inc.", + [3]byte{32, 71, 237}: "BSkyB Ltd", + [3]byte{32, 74, 170}: "Hanscan Spain S.A.", + [3]byte{32, 76, 3}: "Aruba Networks", + [3]byte{32, 76, 109}: "Hugo Brennenstuhl Gmbh & Co. KG.", + [3]byte{32, 76, 158}: "Cisco Systems, Inc", + [3]byte{32, 78, 107}: "Axxana(israel) ltd", + [3]byte{32, 78, 113}: "Juniper Networks", + [3]byte{32, 78, 127}: "NETGEAR", + [3]byte{32, 83, 202}: "Risk Technology Ltd", + [3]byte{32, 84, 118}: "Sony Mobile Communications AB", + [3]byte{32, 85, 49}: "Samsung Electronics Co.,Ltd", + [3]byte{32, 85, 50}: "Gotech International Technology Limited", + [3]byte{32, 87, 33}: "Salix Technology CO., Ltd.", + [3]byte{32, 87, 175}: "Shenzhen FH-NET OPTOELECTRONICS CO.,LTD", + [3]byte{32, 89, 160}: "Paragon Technologies Inc.", + [3]byte{32, 90, 0}: "Coval", + [3]byte{32, 91, 42}: "Private", + [3]byte{32, 91, 94}: "Shenzhen Wonhe Technology Co., Ltd", + [3]byte{32, 92, 250}: "Yangzhou ChangLian Network Technology Co,ltd.", + [3]byte{32, 93, 71}: "vivo Mobile Communication Co., Ltd.", + [3]byte{32, 94, 247}: "Samsung Electronics Co.,Ltd", + [3]byte{32, 98, 116}: "Microsoft Corporation", + [3]byte{32, 99, 95}: "Abeeway", + [3]byte{32, 100, 50}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{32, 103, 177}: "Pluto inc.", + [3]byte{32, 104, 157}: "Liteon Technology Corporation", + [3]byte{32, 106, 138}: "Wistron Infocomm (Zhongshan) Corporation", + [3]byte{32, 106, 255}: "Atlas Elektronik UK Limited", + [3]byte{32, 108, 138}: "Aerohive Networks Inc.", + [3]byte{32, 110, 156}: "Samsung Electronics Co.,Ltd", + [3]byte{32, 111, 236}: "Braemac CA LLC", + [3]byte{32, 113, 158}: "SF Technology Co.,Ltd", + [3]byte{32, 115, 85}: "ARRIS Group, Inc.", + [3]byte{32, 116, 207}: "Shenzhen Voxtech Co.,Ltd", + [3]byte{32, 118, 0}: "Actiontec Electronics, Inc", + [3]byte{32, 118, 143}: "Apple, Inc.", + [3]byte{32, 118, 147}: "Lenovo (Beijing) Limited.", + [3]byte{32, 120, 11}: "Delta Faucet Company", + [3]byte{32, 120, 240}: "Apple, Inc.", + [3]byte{32, 124, 143}: "Quanta Microsystems,Inc.", + [3]byte{32, 125, 116}: "Apple, Inc.", + [3]byte{32, 130, 192}: "Xiaomi Communications Co Ltd", + [3]byte{32, 133, 140}: "Assa", + [3]byte{32, 135, 86}: "SIEMENS AG", + [3]byte{32, 135, 172}: "AES motomation", + [3]byte{32, 137, 111}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{32, 137, 132}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{32, 137, 134}: "zte corporation", + [3]byte{32, 139, 55}: "Skyworth Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{32, 144, 111}: "Shenzhen Tencent Computer System Co., Ltd.", + [3]byte{32, 145, 72}: "Texas Instruments", + [3]byte{32, 145, 138}: "PROFALUX", + [3]byte{32, 145, 217}: "I'M SPA", + [3]byte{32, 147, 77}: "FUJIAN STAR-NET COMMUNICATION CO.,LTD", + [3]byte{32, 154, 233}: "Volacomm Co., Ltd", + [3]byte{32, 155, 165}: "JIAXING GLEAD Electronics Co.,Ltd", + [3]byte{32, 155, 205}: "Apple, Inc.", + [3]byte{32, 162, 228}: "Apple, Inc.", + [3]byte{32, 162, 231}: "Lee-Dickens Ltd", + [3]byte{32, 166, 128}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{32, 167, 131}: "miControl GmbH", + [3]byte{32, 167, 135}: "Bointec Taiwan Corporation Limited", + [3]byte{32, 168, 185}: "Siemens", + [3]byte{32, 169, 14}: "TCT mobile ltd", + [3]byte{32, 169, 155}: "Microsoft Corporation", + [3]byte{32, 170, 37}: "IP-NET LLC", + [3]byte{32, 170, 75}: "Cisco-Linksys, LLC", + [3]byte{32, 171, 55}: "Apple, Inc.", + [3]byte{32, 176, 247}: "Enclustra GmbH", + [3]byte{32, 179, 153}: "Enterasys", + [3]byte{32, 181, 198}: "Mimosa Networks", + [3]byte{32, 183, 192}: "OMICRON electronics GmbH", + [3]byte{32, 187, 118}: "COL GIOVANNI PAOLO SpA", + [3]byte{32, 187, 192}: "Cisco Systems, Inc", + [3]byte{32, 187, 198}: "Jabil Circuit Hungary Ltd.", + [3]byte{32, 191, 219}: "DVL", + [3]byte{32, 192, 71}: "Verizon", + [3]byte{32, 192, 109}: "SHENZHEN SPACETEK TECHNOLOGY CO.,LTD", + [3]byte{32, 193, 175}: "i Wit Digital Co., Limited", + [3]byte{32, 195, 143}: "Texas Instruments", + [3]byte{32, 195, 164}: "RetailNext", + [3]byte{32, 198, 13}: "Shanghai annijie Information technology Co.,LTD", + [3]byte{32, 198, 235}: "Panasonic Corporation AVC Networks Company", + [3]byte{32, 200, 179}: "SHENZHEN BUL-TECH CO.,LTD.", + [3]byte{32, 201, 208}: "Apple, Inc.", + [3]byte{32, 205, 57}: "Texas Instruments", + [3]byte{32, 206, 196}: "Peraso Technologies", + [3]byte{32, 207, 48}: "ASUSTek COMPUTER INC.", + [3]byte{32, 209, 96}: "Private", + [3]byte{32, 210, 31}: "Wincal Technology Corp.", + [3]byte{32, 210, 95}: "SmartCap Technologies", + [3]byte{32, 211, 144}: "Samsung Electronics Co.,Ltd", + [3]byte{32, 213, 171}: "Korea Infocom Co.,Ltd.", + [3]byte{32, 213, 191}: "Samsung Electronics Co.,Ltd", + [3]byte{32, 214, 7}: "Nokia Corporation", + [3]byte{32, 215, 90}: "Posh Mobile Limited", + [3]byte{32, 217, 6}: "Iota, Inc.", + [3]byte{32, 219, 171}: "Samsung Electronics Co., Ltd.", + [3]byte{32, 220, 147}: "Cheetah Hi-Tech, Inc.", + [3]byte{32, 220, 230}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{32, 223, 63}: "Nanjing SAC Power Grid Automation Co., Ltd.", + [3]byte{32, 228, 7}: "Spark srl", + [3]byte{32, 229, 42}: "NETGEAR", + [3]byte{32, 229, 100}: "ARRIS Group, Inc.", + [3]byte{32, 231, 145}: "Siemens Healthcare Diagnostics, Inc", + [3]byte{32, 234, 199}: "SHENZHEN RIOPINE ELECTRONICS CO., LTD", + [3]byte{32, 237, 116}: "Ability enterprise co.,Ltd.", + [3]byte{32, 238, 198}: "Elefirst Science & Tech Co ., ltd", + [3]byte{32, 240, 2}: "MTData Developments Pty. Ltd.", + [3]byte{32, 241, 124}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{32, 243, 163}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{32, 244, 27}: "Shenzhen Bilian electronic CO.,LTD", + [3]byte{32, 245, 16}: "Codex Digital Limited", + [3]byte{32, 245, 67}: "Hui Zhou Gaoshengda Technology Co.,LTD", + [3]byte{32, 248, 94}: "Delta Electronics", + [3]byte{32, 250, 187}: "Cambridge Executive Limited", + [3]byte{32, 253, 241}: "3COM EUROPE LTD", + [3]byte{32, 254, 205}: "System In Frontier Inc.", + [3]byte{32, 254, 219}: "M2M Solution S.A.S.", + [3]byte{36, 0, 186}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 1, 199}: "Cisco Systems, Inc", + [3]byte{36, 5, 15}: "MTN Electronic Co. Ltd", + [3]byte{36, 5, 245}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{36, 9, 23}: "Devlin Electronics Limited", + [3]byte{36, 9, 149}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 10, 17}: "TCT mobile ltd", + [3]byte{36, 10, 100}: "AzureWave Technology Inc.", + [3]byte{36, 10, 196}: "Espressif Inc.", + [3]byte{36, 11, 10}: "Palo Alto Networks", + [3]byte{36, 11, 42}: "Viettel Group", + [3]byte{36, 11, 177}: "KOSTAL Industrie Elektrik GmbH ", + [3]byte{36, 13, 101}: "Shenzhen Vsun Communication Technology Co., Ltd.", + [3]byte{36, 13, 194}: "TCT mobile ltd", + [3]byte{36, 16, 100}: "Shenzhen Ecsino Tecnical Co. Ltd", + [3]byte{36, 17, 37}: "Hutek Co., Ltd.", + [3]byte{36, 17, 72}: "Entropix, LLC", + [3]byte{36, 17, 208}: "Chongqing Ehs Science and Technology Development Co.,Ltd.", + [3]byte{36, 26, 140}: "Squarehead Technology AS", + [3]byte{36, 27, 19}: "Shanghai Nutshell Electronic Co., Ltd.", + [3]byte{36, 27, 68}: "Hangzhou Tuners Electronics Co., Ltd", + [3]byte{36, 28, 4}: "SHENZHEN JEHE TECHNOLOGY DEVELOPMENT CO., LTD.", + [3]byte{36, 30, 235}: "Apple, Inc.", + [3]byte{36, 31, 44}: "Calsys, Inc.", + [3]byte{36, 31, 160}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 32, 199}: "Sagemcom Broadband SAS", + [3]byte{36, 33, 171}: "Sony Mobile Communications AB", + [3]byte{36, 36, 14}: "Apple, Inc.", + [3]byte{36, 38, 66}: "SHARP Corporation.", + [3]byte{36, 47, 250}: "Toshiba Global Commerce Solutions", + [3]byte{36, 49, 132}: "SHARP Corporation", + [3]byte{36, 51, 108}: "Private", + [3]byte{36, 53, 204}: "Zhongshan Scinan Internet of Things Co.,Ltd.", + [3]byte{36, 55, 76}: "Cisco SPVTG", + [3]byte{36, 55, 239}: "EMC Electronic Media Communication SA", + [3]byte{36, 60, 32}: "Dynamode Group", + [3]byte{36, 66, 188}: "Alinco,incorporated", + [3]byte{36, 68, 39}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 69, 151}: "GEMUE Gebr. Mueller Apparatebau", + [3]byte{36, 71, 14}: "PentronicAB", + [3]byte{36, 73, 123}: "Innovative Converged Devices Inc", + [3]byte{36, 75, 3}: "Samsung Electronics Co.,Ltd", + [3]byte{36, 75, 129}: "Samsung Electronics Co.,Ltd", + [3]byte{36, 76, 7}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 78, 123}: "IEEE Registration Authority", + [3]byte{36, 79, 29}: "iRule LLC", + [3]byte{36, 89, 11}: "White Sky Inc. Limited", + [3]byte{36, 91, 167}: "Apple, Inc.", + [3]byte{36, 91, 240}: "Liteon, Inc.", + [3]byte{36, 92, 191}: "NCSE", + [3]byte{36, 94, 190}: "QNAP Systems, Inc.", + [3]byte{36, 95, 223}: "KYOCERA Corporation", + [3]byte{36, 96, 129}: "razberi technologies", + [3]byte{36, 97, 90}: "China Mobile Group Device Co.,Ltd.", + [3]byte{36, 98, 120}: "sysmocom - systems for mobile communications GmbH", + [3]byte{36, 100, 239}: "CYG SUNRI CO.,LTD.", + [3]byte{36, 101, 17}: "AVM GmbH", + [3]byte{36, 105, 62}: "innodisk Corporation", + [3]byte{36, 105, 74}: "Jasmine Systems Inc.", + [3]byte{36, 105, 104}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{36, 105, 165}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 106, 171}: "IT-IS International", + [3]byte{36, 108, 138}: "YUKAI Engineering", + [3]byte{36, 110, 150}: "Dell Inc.", + [3]byte{36, 113, 137}: "Texas Instruments", + [3]byte{36, 114, 96}: "IOTTECH Corp", + [3]byte{36, 118, 86}: "Shanghai Net Miles Fiber Optics Technology Co., LTD.", + [3]byte{36, 118, 125}: "Cisco SPVTG", + [3]byte{36, 119, 3}: "Intel Corporate", + [3]byte{36, 124, 76}: "Herman Miller", + [3]byte{36, 127, 32}: "Sagemcom Broadband SAS", + [3]byte{36, 127, 60}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 128, 0}: "Westcontrol AS", + [3]byte{36, 129, 170}: "KSH International Co., Ltd.", + [3]byte{36, 130, 138}: "Prowave Technologies Ltd.", + [3]byte{36, 134, 244}: "Ctek, Inc.", + [3]byte{36, 135, 7}: "SEnergy Corporation", + [3]byte{36, 136, 148}: "shenzhen lensun Communication Technology LTD", + [3]byte{36, 138, 7}: "Mellanox Technologies, Inc.", + [3]byte{36, 146, 14}: "Samsung Electronics Co.,Ltd", + [3]byte{36, 147, 202}: "Voxtronic Technology Computer-Systeme GmbH", + [3]byte{36, 148, 66}: "OPEN ROAD SOLUTIONS , INC.", + [3]byte{36, 149, 4}: "SFR", + [3]byte{36, 151, 237}: "Techvision Intelligent Technology Limited", + [3]byte{36, 158, 171}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 160, 116}: "Apple, Inc.", + [3]byte{36, 162, 225}: "Apple, Inc.", + [3]byte{36, 164, 44}: "KOUKAAM a.s.", + [3]byte{36, 164, 60}: "Ubiquiti Networks Inc.", + [3]byte{36, 164, 149}: "Thales Canada Inc.", + [3]byte{36, 167, 220}: "BSkyB Ltd", + [3]byte{36, 168, 125}: "Panasonic Automotive Systems Asia Pacific(Thailand)Co.,Ltd.", + [3]byte{36, 169, 55}: "PURE Storage", + [3]byte{36, 171, 129}: "Apple, Inc.", + [3]byte{36, 175, 74}: "Alcatel-Lucent IPD", + [3]byte{36, 175, 84}: "NEXGEN Mediatech Inc.", + [3]byte{36, 176, 169}: "Shanghai Mobiletek Communication Ltd.", + [3]byte{36, 182, 87}: "Cisco Systems, Inc", + [3]byte{36, 182, 184}: "FRIEM SPA", + [3]byte{36, 182, 253}: "Dell Inc.", + [3]byte{36, 184, 140}: "Crenus Co.,Ltd.", + [3]byte{36, 184, 210}: "Opzoon Technology Co.,Ltd.", + [3]byte{36, 186, 19}: "RISO KAGAKU CORPORATION", + [3]byte{36, 186, 48}: "Technical Consumer Products, Inc.", + [3]byte{36, 187, 193}: "Absolute Analysis", + [3]byte{36, 188, 130}: "Dali Wireless, Inc.", + [3]byte{36, 188, 248}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 190, 5}: "Hewlett Packard", + [3]byte{36, 191, 116}: "Private", + [3]byte{36, 192, 179}: "RSF", + [3]byte{36, 193, 189}: "CRRC DALIAN R&D CO.,LTD.", + [3]byte{36, 195, 249}: "Securitas Direct AB", + [3]byte{36, 196, 74}: "zte corporation", + [3]byte{36, 198, 150}: "Samsung Electronics Co.,Ltd", + [3]byte{36, 200, 72}: "mywerk system GmbH", + [3]byte{36, 200, 110}: "Chaney Instrument Co.", + [3]byte{36, 201, 161}: "Ruckus Wireless", + [3]byte{36, 201, 222}: "Genoray", + [3]byte{36, 203, 231}: "MYK, Inc.", + [3]byte{36, 207, 33}: "Shenzhen State Micro Technology Co., Ltd", + [3]byte{36, 209, 63}: "MEXUS CO.,LTD", + [3]byte{36, 210, 204}: "SmartDrive Systems Inc.", + [3]byte{36, 213, 28}: "Zhongtian broadband technology co., LTD ", + [3]byte{36, 217, 33}: "Avaya Inc", + [3]byte{36, 218, 17}: "NO NDA Inc", + [3]byte{36, 218, 155}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{36, 218, 182}: "Sistemas de Gestión Energética S.A. de C.V", + [3]byte{36, 219, 172}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 219, 173}: "ShopperTrak RCT Corporation", + [3]byte{36, 219, 237}: "Samsung Electronics Co.,Ltd", + [3]byte{36, 222, 198}: "Aruba Networks", + [3]byte{36, 223, 106}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{36, 226, 113}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{36, 227, 20}: "Apple, Inc.", + [3]byte{36, 228, 63}: "Wenzhou Kunmei Communication Technology Co.,Ltd.", + [3]byte{36, 229, 170}: "Philips Oral Healthcare, Inc.", + [3]byte{36, 230, 186}: "JSC Zavod im. Kozitsky", + [3]byte{36, 233, 179}: "Cisco Systems, Inc", + [3]byte{36, 234, 64}: "Helmholz GmbH & Co. KG", + [3]byte{36, 235, 101}: "SAET I.S. S.r.l.", + [3]byte{36, 236, 153}: "ASKEY COMPUTER CORP", + [3]byte{36, 236, 214}: "CSG Science & Technology Co.,Ltd.Hefei", + [3]byte{36, 238, 58}: "Chengdu Yingji Electronic Hi-tech Co Ltd", + [3]byte{36, 240, 148}: "Apple, Inc.", + [3]byte{36, 240, 255}: "GHT Co., Ltd.", + [3]byte{36, 242, 221}: "Radiant Zemax LLC", + [3]byte{36, 245, 126}: "HWH CO., LTD.", + [3]byte{36, 245, 170}: "Samsung Electronics Co.,Ltd", + [3]byte{36, 253, 82}: "Liteon Technology Corporation", + [3]byte{36, 253, 91}: "SmartThings, Inc.", + [3]byte{40, 4, 224}: "FERMAX ELECTRONICA S.A.U.", + [3]byte{40, 6, 30}: "NINGBO GLOBAL USEFUL ELECTRIC CO.,LTD", + [3]byte{40, 6, 141}: "ITL, LLC", + [3]byte{40, 11, 92}: "Apple, Inc.", + [3]byte{40, 12, 40}: "Unigen DataStorage Corporation", + [3]byte{40, 12, 184}: "Mikrosay Yazilim ve Elektronik A.S.", + [3]byte{40, 13, 252}: "Sony Interactive Entertainment Inc.", + [3]byte{40, 14, 139}: "Beijing Spirit Technology Development Co., Ltd.", + [3]byte{40, 16, 27}: "MagnaCom", + [3]byte{40, 16, 123}: "D-Link International", + [3]byte{40, 20, 113}: "Lantis co., LTD.", + [3]byte{40, 22, 46}: "2Wire Inc", + [3]byte{40, 22, 173}: "Intel Corporate", + [3]byte{40, 23, 206}: "Omnisense Ltd", + [3]byte{40, 24, 120}: "Microsoft Corporation", + [3]byte{40, 24, 253}: "Aditya Infotech Ltd.", + [3]byte{40, 34, 70}: "Beijing Sinoix Communication Co., LTD", + [3]byte{40, 36, 255}: "Wistron Neweb Corporation", + [3]byte{40, 37, 54}: "SHENZHEN HOLATEK CO.,LTD", + [3]byte{40, 38, 166}: "PBR electronics GmbH", + [3]byte{40, 39, 191}: "Samsung Electronics Co.,Ltd", + [3]byte{40, 40, 93}: "ZyXEL Communications Corporation", + [3]byte{40, 41, 204}: "Corsa Technology Incorporated", + [3]byte{40, 41, 217}: "GlobalBeiMing technology (Beijing)Co. Ltd", + [3]byte{40, 44, 178}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{40, 49, 82}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{40, 50, 197}: "HUMAX Co., Ltd.", + [3]byte{40, 52, 16}: "Enigma Diagnostics Limited", + [3]byte{40, 52, 162}: "Cisco Systems, Inc", + [3]byte{40, 54, 56}: "IEEE Registration Authority", + [3]byte{40, 55, 19}: "Shenzhen 3Nod Digital Technology Co., Ltd.", + [3]byte{40, 55, 55}: "Apple, Inc.", + [3]byte{40, 56, 207}: "Gen2wave", + [3]byte{40, 57, 94}: "Samsung Electronics Co.,Ltd", + [3]byte{40, 57, 231}: "Preceno Technology Pte.Ltd.", + [3]byte{40, 59, 150}: "Cool Control LTD", + [3]byte{40, 60, 228}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{40, 63, 105}: "Sony Mobile Communications AB", + [3]byte{40, 64, 26}: "C8 MediSensors, Inc.", + [3]byte{40, 65, 33}: "OptiSense Network, LLC", + [3]byte{40, 68, 48}: "GenesisTechnical Systems (UK) Ltd", + [3]byte{40, 71, 170}: "Nokia Corporation", + [3]byte{40, 72, 70}: "GridCentric Inc.", + [3]byte{40, 76, 83}: "Intune Networks", + [3]byte{40, 77, 146}: "Luminator", + [3]byte{40, 78, 215}: "OutSmart Power Systems, Inc.", + [3]byte{40, 79, 206}: "Liaoning Wontel Science and Technology Development Co.,Ltd.", + [3]byte{40, 81, 50}: "Shenzhen Prayfly Technology Co.,Ltd", + [3]byte{40, 82, 97}: "Cisco Systems, Inc", + [3]byte{40, 82, 224}: "Layon international Electronic & Telecom Co.,Ltd", + [3]byte{40, 86, 90}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{40, 87, 103}: "Echostar Technologies Corp", + [3]byte{40, 87, 190}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{40, 90, 235}: "Apple, Inc.", + [3]byte{40, 95, 47}: "RNware Co.,Ltd.", + [3]byte{40, 95, 219}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{40, 96, 70}: "Lantech Communications Global, Inc.", + [3]byte{40, 96, 148}: "CAPELEC", + [3]byte{40, 99, 54}: "Siemens AG - Industrial Automation - EWA", + [3]byte{40, 101, 107}: "Keystone Microtech Corporation", + [3]byte{40, 106, 184}: "Apple, Inc.", + [3]byte{40, 106, 186}: "Apple, Inc.", + [3]byte{40, 108, 7}: "XIAOMI Electronics,CO.,LTD", + [3]byte{40, 109, 151}: "SAMJIN Co., Ltd.", + [3]byte{40, 110, 212}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{40, 111, 127}: "Cisco Systems, Inc", + [3]byte{40, 113, 132}: "Spire Payments", + [3]byte{40, 114, 197}: "Smartmatic Corp", + [3]byte{40, 114, 240}: "ATHENA", + [3]byte{40, 118, 16}: "IgniteNet", + [3]byte{40, 118, 205}: "Funshion Online Technologies Co.,Ltd", + [3]byte{40, 121, 148}: "Realplay Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{40, 122, 238}: "ARRIS Group, Inc.", + [3]byte{40, 124, 219}: "Hefei Toycloud Technology Co.,ltd", + [3]byte{40, 128, 35}: "Hewlett Packard", + [3]byte{40, 131, 53}: "Samsung Electronics Co.,Ltd", + [3]byte{40, 132, 250}: "SHARP Corporation", + [3]byte{40, 133, 45}: "Touch Networks", + [3]byte{40, 137, 21}: "CashGuard Sverige AB", + [3]byte{40, 138, 28}: "Juniper Networks", + [3]byte{40, 145, 208}: "Stage Tec Entwicklungsgesellschaft für professionelle Audiotechnik mbH", + [3]byte{40, 146, 74}: "Hewlett Packard", + [3]byte{40, 147, 254}: "Cisco Systems, Inc", + [3]byte{40, 148, 15}: "Cisco Systems, Inc", + [3]byte{40, 148, 175}: "Samhwa Telecom", + [3]byte{40, 152, 123}: "Samsung Electronics Co.,Ltd", + [3]byte{40, 153, 58}: "Arista Networks", + [3]byte{40, 154, 75}: "SteelSeries ApS", + [3]byte{40, 154, 250}: "TCT mobile ltd", + [3]byte{40, 158, 223}: "Danfoss Turbocor Compressors, Inc", + [3]byte{40, 160, 43}: "Apple, Inc.", + [3]byte{40, 161, 131}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{40, 161, 134}: "enblink", + [3]byte{40, 161, 146}: "GERP Solution", + [3]byte{40, 161, 235}: "ETEK TECHNOLOGY (SHENZHEN) CO.,LTD", + [3]byte{40, 162, 65}: "exlar corp", + [3]byte{40, 162, 75}: "Juniper Networks", + [3]byte{40, 165, 116}: "Miller Electric Mfg. Co.", + [3]byte{40, 165, 238}: "Shenzhen SDGI CATV Co., Ltd", + [3]byte{40, 166, 219}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{40, 172, 103}: "Mach Power, Rappresentanze Internazionali s.r.l.", + [3]byte{40, 175, 10}: "Sirius XM Radio Inc", + [3]byte{40, 176, 204}: "Xenya d.o.o.", + [3]byte{40, 178, 189}: "Intel Corporate", + [3]byte{40, 179, 171}: "Genmark Automation", + [3]byte{40, 180, 72}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{40, 185, 217}: "Radisys Corporation", + [3]byte{40, 186, 24}: "NextNav, LLC", + [3]byte{40, 186, 181}: "Samsung Electronics Co.,Ltd", + [3]byte{40, 187, 89}: "RNET Technologies, Inc.", + [3]byte{40, 188, 24}: "SourcingOverseas Co. Ltd", + [3]byte{40, 188, 86}: "EMAC, Inc.", + [3]byte{40, 190, 3}: "TCT mobile ltd", + [3]byte{40, 190, 155}: "Technicolor CH USA Inc.", + [3]byte{40, 192, 218}: "Juniper Networks", + [3]byte{40, 194, 221}: "AzureWave Technology Inc.", + [3]byte{40, 198, 63}: "Intel Corporate", + [3]byte{40, 198, 113}: "Yota Devices OY", + [3]byte{40, 198, 142}: "NETGEAR", + [3]byte{40, 199, 24}: "Altierre", + [3]byte{40, 199, 206}: "Cisco Systems, Inc", + [3]byte{40, 200, 37}: "DellKing Industrial Co., Ltd", + [3]byte{40, 200, 122}: "ARRIS Group, Inc.", + [3]byte{40, 201, 20}: "Taimag Corporation", + [3]byte{40, 202, 9}: "ThyssenKrupp Elevators (Shanghai) Co.,Ltd", + [3]byte{40, 203, 235}: "One", + [3]byte{40, 204, 1}: "Samsung Electronics Co.,Ltd", + [3]byte{40, 204, 255}: "Corporacion Empresarial Altra SL", + [3]byte{40, 205, 28}: "Espotel Oy", + [3]byte{40, 205, 76}: "Individual Computers GmbH", + [3]byte{40, 205, 156}: "Shenzhen Dynamax Software Development Co.,Ltd.", + [3]byte{40, 207, 218}: "Apple, Inc.", + [3]byte{40, 207, 233}: "Apple, Inc.", + [3]byte{40, 209, 175}: "Nokia Corporation", + [3]byte{40, 210, 68}: "LCFC(HeFei) Electronics Technology Co., Ltd.", + [3]byte{40, 213, 118}: "Premier Wireless, Inc.", + [3]byte{40, 217, 62}: "Telecor Inc.", + [3]byte{40, 217, 138}: "Hangzhou Konke Technology Co.,Ltd.", + [3]byte{40, 217, 151}: "Yuduan Mobile Co., Ltd.", + [3]byte{40, 219, 129}: "Shanghai Guao Electronic Technology Co., Ltd", + [3]byte{40, 222, 246}: "bioMerieux Inc.", + [3]byte{40, 224, 44}: "Apple, Inc.", + [3]byte{40, 225, 76}: "Apple, Inc.", + [3]byte{40, 226, 151}: "Shanghai InfoTM Microelectronics Co.,Ltd.", + [3]byte{40, 227, 31}: "Xiaomi Communications Co Ltd", + [3]byte{40, 227, 71}: "Liteon Technology Corporation", + [3]byte{40, 228, 118}: "Pi-Coral", + [3]byte{40, 230, 8}: "Tokheim", + [3]byte{40, 230, 233}: "SIS Sat Internet Services GmbH", + [3]byte{40, 231, 148}: "Microtime Computer Inc.", + [3]byte{40, 231, 207}: "Apple, Inc.", + [3]byte{40, 237, 88}: "JAG Jakob AG", + [3]byte{40, 237, 106}: "Apple, Inc.", + [3]byte{40, 238, 44}: "Frontline Test Equipment", + [3]byte{40, 238, 82}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{40, 238, 211}: "Shenzhen Super D Technology Co., Ltd", + [3]byte{40, 239, 1}: "Private", + [3]byte{40, 240, 118}: "Apple, Inc.", + [3]byte{40, 241, 14}: "Dell Inc.", + [3]byte{40, 243, 88}: "2C - Trifonov & Co", + [3]byte{40, 243, 102}: "Shenzhen Bilian electronic CO.,LTD", + [3]byte{40, 245, 50}: "ADD-Engineering BV", + [3]byte{40, 246, 6}: "Syes srl", + [3]byte{40, 250, 160}: "vivo Mobile Communication Co., Ltd.", + [3]byte{40, 251, 211}: "Ragentek Technology Group", + [3]byte{40, 252, 81}: "The Electric Controller and Manufacturing Co., LLC", + [3]byte{40, 252, 246}: "Shenzhen Xin KingBrand enterprises Co.,Ltd", + [3]byte{40, 253, 128}: "IEEE Registration Authority", + [3]byte{40, 254, 205}: "Lemobile Information Technology (Beijing) Co., Ltd.", + [3]byte{40, 255, 62}: "zte corporation", + [3]byte{44, 0, 44}: "UNOWHY", + [3]byte{44, 0, 51}: "EControls, LLC", + [3]byte{44, 0, 247}: "XOS", + [3]byte{44, 1, 11}: "NASCENT Technology, LLC - RemKon", + [3]byte{44, 2, 159}: "3ALogics", + [3]byte{44, 6, 35}: "Win Leader Inc.", + [3]byte{44, 7, 60}: "DEVLINE LIMITED", + [3]byte{44, 8, 28}: "OVH", + [3]byte{44, 8, 140}: "HUMAX Co., Ltd.", + [3]byte{44, 9, 77}: "Raptor Engineering, LLC", + [3]byte{44, 9, 203}: "COBS AB", + [3]byte{44, 11, 233}: "Cisco Systems, Inc", + [3]byte{44, 14, 61}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{44, 16, 193}: "Nintendo Co., Ltd.", + [3]byte{44, 24, 174}: "Trend Electronics Co., Ltd.", + [3]byte{44, 25, 132}: "IDN Telecom, Inc.", + [3]byte{44, 26, 49}: "Electronics Company Limited", + [3]byte{44, 27, 200}: "Hunan Topview Network System CO.,LTD", + [3]byte{44, 29, 184}: "ARRIS Group, Inc.", + [3]byte{44, 30, 234}: "AERODEV", + [3]byte{44, 31, 35}: "Apple, Inc.", + [3]byte{44, 32, 11}: "Apple, Inc.", + [3]byte{44, 33, 49}: "Juniper Networks", + [3]byte{44, 33, 114}: "Juniper Networks", + [3]byte{44, 33, 215}: "IMAX Corporation", + [3]byte{44, 34, 139}: "CTR SRL", + [3]byte{44, 35, 58}: "Hewlett Packard", + [3]byte{44, 36, 95}: "Babolat VS", + [3]byte{44, 38, 95}: "IEEE Registration Authority", + [3]byte{44, 38, 197}: "zte corporation", + [3]byte{44, 39, 215}: "Hewlett Packard", + [3]byte{44, 40, 45}: "BBK EDUCATIONAL ELECTRONICS CORP.,LTD.", + [3]byte{44, 41, 151}: "Microsoft Corporation", + [3]byte{44, 45, 72}: "bct electronic GesmbH", + [3]byte{44, 48, 51}: "NETGEAR", + [3]byte{44, 48, 104}: "Pantech Co.,Ltd", + [3]byte{44, 49, 36}: "Cisco Systems, Inc", + [3]byte{44, 51, 17}: "Cisco Systems, Inc", + [3]byte{44, 51, 97}: "Apple, Inc.", + [3]byte{44, 51, 122}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{44, 52, 39}: "ERCO & GENER", + [3]byte{44, 53, 87}: "ELLIY Power CO..Ltd", + [3]byte{44, 54, 160}: "Capisco Limited", + [3]byte{44, 54, 248}: "Cisco Systems, Inc", + [3]byte{44, 55, 49}: "SHENZHEN YIFANG DIGITAL TECHNOLOGY CO.,LTD.", + [3]byte{44, 55, 150}: "CYBO CO.,LTD.", + [3]byte{44, 57, 150}: "Sagemcom Broadband SAS", + [3]byte{44, 57, 193}: "Ciena Corporation", + [3]byte{44, 58, 40}: "Fagor Electrónica", + [3]byte{44, 59, 253}: "Netstor Technology Co., Ltd.", + [3]byte{44, 62, 207}: "Cisco Systems, Inc", + [3]byte{44, 63, 56}: "Cisco Systems, Inc", + [3]byte{44, 63, 62}: "Alge-Timing GmbH", + [3]byte{44, 64, 43}: "Smart iBlue Technology Limited", + [3]byte{44, 65, 56}: "Hewlett Packard", + [3]byte{44, 68, 1}: "Samsung Electronics Co.,Ltd", + [3]byte{44, 68, 27}: "Spectrum Medical Limited", + [3]byte{44, 68, 253}: "Hewlett Packard", + [3]byte{44, 77, 84}: "ASUSTek COMPUTER INC.", + [3]byte{44, 77, 121}: "GoerTek Inc.", + [3]byte{44, 80, 137}: "Shenzhen Kaixuan Visual Technology Co.,Limited", + [3]byte{44, 83, 74}: "Shenzhen Winyao Electronic Limited", + [3]byte{44, 84, 45}: "Cisco Systems, Inc", + [3]byte{44, 84, 207}: "LG Electronics (Mobile Communications)", + [3]byte{44, 85, 60}: "Gainspeed, Inc.", + [3]byte{44, 85, 211}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{44, 86, 220}: "ASUSTek COMPUTER INC.", + [3]byte{44, 89, 138}: "LG Electronics (Mobile Communications)", + [3]byte{44, 89, 229}: "Hewlett Packard", + [3]byte{44, 90, 5}: "Nokia Corporation", + [3]byte{44, 90, 15}: "Cisco Systems, Inc", + [3]byte{44, 90, 141}: "SYSTRONIK Elektronik u. Systemtechnik GmbH", + [3]byte{44, 90, 163}: "PROMATE ELECTRONIC CO.LTD", + [3]byte{44, 91, 184}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{44, 91, 225}: "Centripetal Networks, Inc", + [3]byte{44, 93, 147}: "Ruckus Wireless", + [3]byte{44, 95, 243}: "Pertronic Industries", + [3]byte{44, 96, 12}: "QUANTA COMPUTER INC.", + [3]byte{44, 98, 90}: "Finest Security Systems Co., Ltd", + [3]byte{44, 98, 137}: "Regenersis (Glenrothes) Ltd", + [3]byte{44, 99, 115}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{44, 103, 152}: "InTalTech Ltd.", + [3]byte{44, 103, 251}: "ShenZhen Zhengjili Electronics Co., LTD", + [3]byte{44, 105, 186}: "RF Controls, LLC", + [3]byte{44, 106, 111}: "IEEE Registration Authority", + [3]byte{44, 107, 245}: "Juniper Networks", + [3]byte{44, 110, 133}: "Intel Corporate", + [3]byte{44, 111, 201}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{44, 113, 85}: "HiveMotion", + [3]byte{44, 114, 195}: "Soundmatters", + [3]byte{44, 117, 15}: "Shanghai Dongzhou-Lawton Communication Technology Co. Ltd.", + [3]byte{44, 118, 138}: "Hewlett Packard", + [3]byte{44, 123, 90}: "Milper Ltd", + [3]byte{44, 123, 132}: "OOO Petr Telegin", + [3]byte{44, 126, 129}: "ARRIS Group, Inc.", + [3]byte{44, 126, 207}: "Onzo Ltd", + [3]byte{44, 128, 101}: "HARTING Inc. of North America", + [3]byte{44, 129, 88}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{44, 134, 210}: "Cisco Systems, Inc", + [3]byte{44, 138, 114}: "HTC Corporation", + [3]byte{44, 139, 242}: "Hitachi Metals America Ltd", + [3]byte{44, 145, 39}: "Eintechno Corporation", + [3]byte{44, 146, 44}: "Kishu Giken Kogyou Company Ltd,.", + [3]byte{44, 148, 100}: "Cincoze Co., Ltd.", + [3]byte{44, 149, 127}: "zte corporation", + [3]byte{44, 150, 98}: "Invenit BV", + [3]byte{44, 151, 23}: "I.C.Y. B.V.", + [3]byte{44, 153, 36}: "ARRIS Group, Inc.", + [3]byte{44, 154, 164}: "Eolo SpA", + [3]byte{44, 157, 30}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{44, 158, 95}: "ARRIS Group, Inc.", + [3]byte{44, 158, 252}: "CANON INC.", + [3]byte{44, 161, 87}: "acromate, Inc.", + [3]byte{44, 161, 125}: "ARRIS Group, Inc.", + [3]byte{44, 162, 180}: "Fortify Technologies, LLC", + [3]byte{44, 163, 14}: "POWER DRAGON DEVELOPMENT LIMITED", + [3]byte{44, 165, 57}: "Parallel Wireless, Inc", + [3]byte{44, 167, 128}: "True Technologies Inc.", + [3]byte{44, 168, 53}: "RIM", + [3]byte{44, 171, 0}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{44, 171, 37}: "Shenzhen Gongjin Electronics Co.,Ltd", + [3]byte{44, 171, 164}: "Cisco SPVTG", + [3]byte{44, 171, 235}: "Cisco Systems, Inc", + [3]byte{44, 172, 68}: "CONEXTOP", + [3]byte{44, 173, 19}: "SHENZHEN ZHILU TECHNOLOGY CO.,LTD", + [3]byte{44, 174, 43}: "Samsung Electronics Co.,Ltd", + [3]byte{44, 176, 93}: "NETGEAR", + [3]byte{44, 176, 223}: "Soliton Technologies Pvt Ltd", + [3]byte{44, 180, 58}: "Apple, Inc.", + [3]byte{44, 182, 147}: "Radware", + [3]byte{44, 182, 157}: "RED Digital Cinema", + [3]byte{44, 186, 186}: "Samsung Electronics Co.,Ltd", + [3]byte{44, 190, 8}: "Apple, Inc.", + [3]byte{44, 190, 151}: "Ingenieurbuero Bickele und Buehler GmbH", + [3]byte{44, 194, 96}: "Oracle Corporation ", + [3]byte{44, 197, 72}: "IAdea Corporation", + [3]byte{44, 197, 211}: "Ruckus Wireless", + [3]byte{44, 204, 21}: "Nokia Corporation", + [3]byte{44, 205, 39}: "Precor Inc", + [3]byte{44, 205, 67}: "Summit Technology Group", + [3]byte{44, 205, 105}: "Aqavi.com", + [3]byte{44, 207, 88}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{44, 208, 45}: "Cisco Systems, Inc", + [3]byte{44, 208, 90}: "Liteon Technology Corporation", + [3]byte{44, 209, 65}: "IEEE Registration Authority", + [3]byte{44, 209, 218}: "Sanjole, Inc.", + [3]byte{44, 210, 231}: "Nokia Corporation", + [3]byte{44, 212, 68}: "FUJITSU LIMITED", + [3]byte{44, 220, 173}: "Wistron Neweb Corporation", + [3]byte{44, 221, 12}: "Discovergy GmbH", + [3]byte{44, 221, 149}: "Taicang T&W Electronics", + [3]byte{44, 221, 163}: "Point Grey Research Inc.", + [3]byte{44, 226, 168}: "DeviceDesign", + [3]byte{44, 228, 18}: "Sagemcom Broadband SAS", + [3]byte{44, 230, 204}: "Ruckus Wireless", + [3]byte{44, 232, 113}: "Alert Metalguard ApS", + [3]byte{44, 237, 235}: "Alpheus Digital Company Limited", + [3]byte{44, 238, 38}: "Petroleum Geo-Services", + [3]byte{44, 240, 162}: "Apple, Inc.", + [3]byte{44, 240, 238}: "Apple, Inc.", + [3]byte{44, 242, 3}: "EMKO ELEKTRONIK SAN VE TIC AS", + [3]byte{44, 244, 197}: "Avaya Inc", + [3]byte{44, 247, 241}: "Seeed Technology Inc.", + [3]byte{44, 250, 162}: "Alcatel-Lucent Enterprise", + [3]byte{44, 252, 228}: "CTEK Sweden AB", + [3]byte{44, 253, 55}: "Blue Calypso, Inc.", + [3]byte{44, 255, 101}: "Oki Electric Industry Co., Ltd.", + [3]byte{48, 5, 92}: "Brother industries, LTD.", + [3]byte{48, 11, 156}: "Delta Mobile Systems, Inc.", + [3]byte{48, 12, 35}: "zte corporation", + [3]byte{48, 13, 42}: "Zhejiang Wellcom Technology Co.,Ltd.", + [3]byte{48, 13, 67}: "Microsoft Mobile Oy", + [3]byte{48, 14, 213}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{48, 14, 227}: "Aquantia Corporation", + [3]byte{48, 16, 179}: "Liteon Technology Corporation", + [3]byte{48, 16, 228}: "Apple, Inc.", + [3]byte{48, 20, 45}: "Piciorgros GmbH", + [3]byte{48, 20, 74}: "Wistron Neweb Corporation", + [3]byte{48, 21, 24}: "Ubiquitous Communication Co. ltd.", + [3]byte{48, 22, 141}: "ProLon", + [3]byte{48, 23, 200}: "Sony Mobile Communications AB", + [3]byte{48, 24, 207}: "DEOS control systems GmbH", + [3]byte{48, 25, 102}: "Samsung Electronics Co.,Ltd", + [3]byte{48, 26, 40}: "Mako Networks Ltd", + [3]byte{48, 33, 91}: "Shenzhen Ostar Display Electronic Co.,Ltd", + [3]byte{48, 41, 190}: "Shanghai MRDcom Co.,Ltd", + [3]byte{48, 45, 232}: "JDA, LLC (JDA Systems)", + [3]byte{48, 50, 148}: "W-IE-NE-R Plein & Baus GmbH", + [3]byte{48, 50, 212}: "Hanilstm Co., Ltd.", + [3]byte{48, 51, 53}: "Boosty", + [3]byte{48, 52, 210}: "Availink, Inc.", + [3]byte{48, 55, 166}: "Cisco Systems, Inc", + [3]byte{48, 56, 85}: "Nokia Corporation", + [3]byte{48, 57, 38}: "Sony Mobile Communications AB", + [3]byte{48, 57, 85}: "Shenzhen Jinhengjia Electronic Co., Ltd.", + [3]byte{48, 57, 242}: "ADB Broadband Italia", + [3]byte{48, 58, 100}: "Intel Corporate", + [3]byte{48, 61, 8}: "GLINTT TES S.A.", + [3]byte{48, 62, 173}: "Sonavox Canada Inc", + [3]byte{48, 65, 116}: "ALTEC LANSING LLC", + [3]byte{48, 66, 37}: "BURG-WÄCHTER KG", + [3]byte{48, 68, 73}: "PLATH GmbH", + [3]byte{48, 68, 135}: "Hefei Radio Communication Technology Co., Ltd ", + [3]byte{48, 68, 161}: "Shanghai Nanchao Information Technology", + [3]byte{48, 70, 154}: "NETGEAR", + [3]byte{48, 73, 59}: "Nanjing Z-Com Wireless Co.,Ltd", + [3]byte{48, 76, 126}: "Panasonic Electric Works Automation Controls Techno Co.,Ltd.", + [3]byte{48, 78, 195}: "Tianjin Techua Technology Co., Ltd.", + [3]byte{48, 81, 248}: "BYK-Gardner GmbH", + [3]byte{48, 82, 90}: "NST Co., LTD", + [3]byte{48, 82, 203}: "Liteon Technology Corporation", + [3]byte{48, 85, 237}: "Trex Network LLC", + [3]byte{48, 87, 172}: "IRLAB LTD.", + [3]byte{48, 88, 144}: "Frontier Silicon Ltd", + [3]byte{48, 89, 91}: "streamnow AG", + [3]byte{48, 89, 183}: "Microsoft", + [3]byte{48, 90, 58}: "ASUSTek COMPUTER INC.", + [3]byte{48, 93, 56}: "Beissbarth ", + [3]byte{48, 96, 35}: "ARRIS Group, Inc.", + [3]byte{48, 97, 18}: "PAV GmbH", + [3]byte{48, 97, 24}: "Paradom Inc.", + [3]byte{48, 99, 107}: "Apple, Inc.", + [3]byte{48, 101, 236}: "Wistron (ChongQing)", + [3]byte{48, 104, 140}: "Reach Technology Inc.", + [3]byte{48, 105, 75}: "RIM", + [3]byte{48, 108, 190}: "Skymotion Technology (HK) Limited", + [3]byte{48, 110, 92}: "Validus Technologies", + [3]byte{48, 113, 178}: "Hangzhou Prevail Optoelectronic Equipment Co.,LTD.", + [3]byte{48, 115, 80}: "Inpeco SA", + [3]byte{48, 116, 150}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{48, 117, 18}: "Sony Mobile Communications AB", + [3]byte{48, 118, 111}: "LG Electronics (Mobile Communications)", + [3]byte{48, 119, 203}: "Maike Industry(Shenzhen)CO.,LTD", + [3]byte{48, 120, 92}: "Partow Tamas Novin (Parman)", + [3]byte{48, 120, 107}: "TIANJIN Golden Pentagon Electronics Co., Ltd.", + [3]byte{48, 120, 194}: "Innowireless, Co. Ltd.", + [3]byte{48, 124, 48}: "RIM", + [3]byte{48, 124, 94}: "Juniper Networks", + [3]byte{48, 124, 178}: "ANOV FRANCE", + [3]byte{48, 126, 203}: "SFR", + [3]byte{48, 133, 169}: "ASUSTek COMPUTER INC.", + [3]byte{48, 135, 48}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{48, 135, 217}: "Ruckus Wireless", + [3]byte{48, 137, 153}: "Guangdong East Power Co.,", + [3]byte{48, 137, 211}: "HONGKONG UCLOUDLINK NETWORK TECHNOLOGY LIMITED", + [3]byte{48, 140, 251}: "Dropcam", + [3]byte{48, 141, 153}: "Hewlett Packard", + [3]byte{48, 144, 171}: "Apple, Inc.", + [3]byte{48, 145, 143}: "Technicolor", + [3]byte{48, 146, 246}: "SHANGHAI SUNMON COMMUNICATION TECHNOGY CO.,LTD", + [3]byte{48, 149, 227}: "SHANGHAI SIMCOM LIMITED", + [3]byte{48, 150, 251}: "Samsung Electronics Co.,Ltd", + [3]byte{48, 155, 173}: "BBK EDUCATIONAL ELECTRONICS CORP.,LTD.", + [3]byte{48, 162, 32}: "ARG Telecom", + [3]byte{48, 162, 67}: "Shenzhen Prifox Innovation Technology Co., Ltd.", + [3]byte{48, 168, 219}: "Sony Mobile Communications AB", + [3]byte{48, 169, 222}: "LG Innotek", + [3]byte{48, 170, 189}: "Shanghai Reallytek Information Technology Co.,Ltd", + [3]byte{48, 174, 123}: "Deqing Dusun Electron CO., LTD", + [3]byte{48, 174, 164}: "Espressif Inc.", + [3]byte{48, 174, 246}: "Radio Mobile Access", + [3]byte{48, 178, 22}: "Hytec Geraetebau GmbH", + [3]byte{48, 179, 162}: "Shenzhen Heguang Measurement & Control Technology Co.,Ltd", + [3]byte{48, 180, 158}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{48, 181, 194}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{48, 181, 241}: "Aitexin Technology Co., Ltd", + [3]byte{48, 182, 79}: "Juniper Networks", + [3]byte{48, 199, 80}: "MIC Technology Group", + [3]byte{48, 199, 174}: "Samsung Electronics Co.,Ltd", + [3]byte{48, 200, 42}: "WI-BIZ srl", + [3]byte{48, 203, 248}: "Samsung Electronics Co.,Ltd", + [3]byte{48, 205, 167}: "Samsung Electronics Co.,Ltd", + [3]byte{48, 209, 126}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{48, 211, 45}: "devolo AG", + [3]byte{48, 211, 87}: "Logosol, Inc.", + [3]byte{48, 211, 134}: "zte corporation", + [3]byte{48, 212, 106}: "Autosales Incorporated", + [3]byte{48, 213, 135}: "Samsung Electronics Co.,Ltd", + [3]byte{48, 214, 201}: "Samsung Electronics Co.,Ltd", + [3]byte{48, 222, 134}: "Cedac Software S.r.l.", + [3]byte{48, 224, 144}: "Linctronix Ltd,", + [3]byte{48, 225, 113}: "Hewlett Packard", + [3]byte{48, 227, 122}: "Intel Corporate", + [3]byte{48, 228, 142}: "Vodafone UK", + [3]byte{48, 228, 219}: "Cisco Systems, Inc", + [3]byte{48, 235, 37}: "INTEK DIGITAL", + [3]byte{48, 239, 209}: "Alstom Strongwish (Shenzhen) Co., Ltd.", + [3]byte{48, 243, 29}: "zte corporation", + [3]byte{48, 243, 53}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{48, 243, 58}: "+plugg srl", + [3]byte{48, 244, 47}: "ESP", + [3]byte{48, 246, 185}: "Ecocentric Energy", + [3]byte{48, 247, 13}: "Cisco Systems, Inc", + [3]byte{48, 247, 114}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{48, 247, 197}: "Apple, Inc.", + [3]byte{48, 247, 215}: "Thread Technology Co., Ltd", + [3]byte{48, 249, 237}: "Sony Corporation", + [3]byte{48, 250, 183}: "Tunai Creative", + [3]byte{48, 252, 104}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{48, 253, 17}: "MACROTECH (USA) INC.", + [3]byte{48, 255, 246}: "HangZhou KuoHeng Technology Co.,ltd", + [3]byte{52, 0, 163}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{52, 2, 134}: "Intel Corporate", + [3]byte{52, 2, 155}: "CloudBerry Technologies Private Limited", + [3]byte{52, 4, 158}: "IEEE Registration Authority", + [3]byte{52, 7, 79}: "AccelStor, Inc.", + [3]byte{52, 7, 251}: "Ericsson AB", + [3]byte{52, 8, 4}: "D-Link Corporation", + [3]byte{52, 10, 34}: "TOP-ACCESS ELECTRONICS CO LTD", + [3]byte{52, 10, 255}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{52, 11, 64}: "MIOS ELETTRONICA SRL", + [3]byte{52, 12, 237}: "Moduel AB", + [3]byte{52, 18, 144}: "Treeview Co.,Ltd.", + [3]byte{52, 18, 152}: "Apple, Inc.", + [3]byte{52, 19, 168}: "Mediplan Limited", + [3]byte{52, 19, 232}: "Intel Corporate", + [3]byte{52, 20, 95}: "Samsung Electronics Co.,Ltd", + [3]byte{52, 21, 158}: "Apple, Inc.", + [3]byte{52, 23, 235}: "Dell Inc.", + [3]byte{52, 26, 53}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{52, 26, 76}: "SHENZHEN WEIBU ELECTRONICS CO.,LTD.", + [3]byte{52, 27, 34}: "Grandbeing Technology Co., Ltd", + [3]byte{52, 30, 107}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{52, 31, 228}: "ARRIS Group, Inc.", + [3]byte{52, 33, 9}: "Jensen Scandinavia AS", + [3]byte{52, 35, 135}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{52, 35, 186}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{52, 37, 93}: "Shenzhen Loadcom Technology Co.,Ltd", + [3]byte{52, 38, 6}: "CarePredict, Inc.", + [3]byte{52, 40, 240}: "ATN International Limited", + [3]byte{52, 41, 234}: "MCD ELECTRONICS SP. Z O.O.", + [3]byte{52, 47, 110}: "Anywire corporation", + [3]byte{52, 49, 17}: "Samsung Electronics Co.,Ltd", + [3]byte{52, 49, 196}: "AVM GmbH", + [3]byte{52, 54, 59}: "Apple, Inc.", + [3]byte{52, 55, 89}: "zte corporation", + [3]byte{52, 56, 175}: "Inlab Software GmbH", + [3]byte{52, 61, 152}: "JinQianMao Technology Co.,Ltd.", + [3]byte{52, 61, 196}: "BUFFALO.INC", + [3]byte{52, 64, 181}: "IBM", + [3]byte{52, 70, 111}: "HiTEM Engineering", + [3]byte{52, 75, 61}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{52, 75, 80}: "zte corporation", + [3]byte{52, 76, 164}: "amazipoint technology Ltd.", + [3]byte{52, 76, 200}: "Echodyne Corp", + [3]byte{52, 77, 234}: "zte corporation", + [3]byte{52, 77, 247}: "LG Electronics (Mobile Communications)", + [3]byte{52, 79, 63}: "IO-Power Technology Co., Ltd.", + [3]byte{52, 79, 92}: "R&M AG", + [3]byte{52, 79, 105}: "EKINOPS SAS", + [3]byte{52, 81, 170}: "JID GLOBAL", + [3]byte{52, 81, 201}: "Apple, Inc.", + [3]byte{52, 84, 60}: "TAKAOKA TOKO CO.,LTD.", + [3]byte{52, 87, 96}: "MitraStar Technology Corp.", + [3]byte{52, 91, 17}: "EVI HEAT AB", + [3]byte{52, 91, 187}: "GD Midea Air-Conditioning Equipment Co.,Ltd.", + [3]byte{52, 92, 64}: "Cargt Holdings LLC", + [3]byte{52, 93, 16}: "Wytek", + [3]byte{52, 97, 120}: "The Boeing Company", + [3]byte{52, 98, 136}: "Cisco Systems, Inc", + [3]byte{52, 100, 169}: "Hewlett Packard", + [3]byte{52, 104, 74}: "Teraworks Co., Ltd.", + [3]byte{52, 104, 149}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{52, 105, 135}: "zte corporation", + [3]byte{52, 106, 194}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{52, 107, 211}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{52, 108, 15}: "Pramod Telecom Pvt. Ltd", + [3]byte{52, 110, 138}: "Ecosense", + [3]byte{52, 110, 157}: "Ericsson AB", + [3]byte{52, 111, 144}: "Cisco Systems, Inc", + [3]byte{52, 111, 146}: "White Rodgers Division", + [3]byte{52, 117, 199}: "Avaya Inc", + [3]byte{52, 118, 197}: "I-O DATA DEVICE, INC.", + [3]byte{52, 120, 119}: "O-Net Communications (Shenzhen) Limited", + [3]byte{52, 120, 215}: "Gionee Communication Equipment Co.,Ltd.", + [3]byte{52, 122, 96}: "ARRIS Group, Inc.", + [3]byte{52, 126, 57}: "Nokia Danmark A/S", + [3]byte{52, 128, 179}: "Xiaomi Communications Co Ltd", + [3]byte{52, 129, 55}: "UNICARD SA", + [3]byte{52, 129, 196}: "AVM GmbH", + [3]byte{52, 129, 244}: "SST Taiwan Ltd.", + [3]byte{52, 130, 222}: "Kiio Inc", + [3]byte{52, 131, 2}: "iFORCOM Co., Ltd", + [3]byte{52, 132, 70}: "Ericsson AB", + [3]byte{52, 134, 42}: "Heinz Lackmann GmbH & Co KG", + [3]byte{52, 135, 61}: "Quectel Wireless Solution Co.,Ltd.", + [3]byte{52, 136, 93}: "Logitech Far East", + [3]byte{52, 138, 123}: "Samsung Electronics Co.,Ltd", + [3]byte{52, 138, 174}: "Sagemcom Broadband SAS", + [3]byte{52, 149, 219}: "Logitec Corporation", + [3]byte{52, 150, 114}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{52, 151, 246}: "ASUSTek COMPUTER INC.", + [3]byte{52, 151, 251}: "ADVANCED RF TECHNOLOGIES INC", + [3]byte{52, 153, 111}: "VPI Engineering", + [3]byte{52, 153, 113}: "Quanta Storage Inc.", + [3]byte{52, 153, 215}: "Universal Flow Monitors, Inc.", + [3]byte{52, 154, 13}: "ZBD Displays Ltd", + [3]byte{52, 155, 91}: "Maquet GmbH", + [3]byte{52, 157, 144}: "Heinzmann GmbH & CO. KG", + [3]byte{52, 158, 52}: "Evervictory Electronic Co.Ltd", + [3]byte{52, 161, 131}: "AWare, Inc", + [3]byte{52, 162, 162}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{52, 163, 149}: "Apple, Inc.", + [3]byte{52, 163, 191}: "Terewave. Inc.", + [3]byte{52, 165, 93}: "TECHNOSOFT INTERNATIONAL SRL", + [3]byte{52, 165, 225}: "Sensorist ApS", + [3]byte{52, 166, 140}: "Shine Profit Development Limited", + [3]byte{52, 167, 9}: "Trevil srl", + [3]byte{52, 167, 186}: "Fischer International Systems Corporation", + [3]byte{52, 168, 67}: "KYOCERA Display Corporation", + [3]byte{52, 168, 78}: "Cisco Systems, Inc", + [3]byte{52, 170, 139}: "Samsung Electronics Co.,Ltd", + [3]byte{52, 170, 153}: "Nokia", + [3]byte{52, 170, 238}: "Mikrovisatos Servisas UAB", + [3]byte{52, 171, 55}: "Apple, Inc.", + [3]byte{52, 173, 228}: "Shanghai Chint Power Systems Co., Ltd.", + [3]byte{52, 175, 44}: "Nintendo Co., Ltd.", + [3]byte{52, 177, 247}: "Texas Instruments", + [3]byte{52, 179, 84}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{52, 181, 113}: "PLDS", + [3]byte{52, 183, 253}: "Guangzhou Younghead Electronic Technology Co.,Ltd", + [3]byte{52, 186, 81}: "Se-Kure Controls, Inc.", + [3]byte{52, 186, 117}: "Tembo Systems, Inc.", + [3]byte{52, 186, 154}: "Asiatelco Technologies Co.", + [3]byte{52, 187, 31}: "BlackBerry RTS", + [3]byte{52, 187, 38}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{52, 188, 166}: "Beijing Ding Qing Technology, Ltd.", + [3]byte{52, 189, 200}: "Cisco Systems, Inc", + [3]byte{52, 189, 249}: "Shanghai WDK Industrial Co.,Ltd.", + [3]byte{52, 189, 250}: "Cisco SPVTG", + [3]byte{52, 190, 0}: "Samsung Electronics Co.,Ltd", + [3]byte{52, 191, 144}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{52, 192, 89}: "Apple, Inc.", + [3]byte{52, 192, 249}: "Rockwell Automation", + [3]byte{52, 195, 172}: "Samsung Electronics Co.,Ltd", + [3]byte{52, 195, 210}: "FN-LINK TECHNOLOGY LIMITED", + [3]byte{52, 197, 208}: "Hagleitner Hygiene International GmbH", + [3]byte{52, 198, 154}: "Enecsys Ltd", + [3]byte{52, 199, 49}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{52, 200, 3}: "Nokia Corporation", + [3]byte{52, 201, 157}: "EIDOLON COMMUNICATIONS TECHNOLOGY CO. LTD.", + [3]byte{52, 201, 240}: "LM Technologies Ltd", + [3]byte{52, 204, 40}: "Nexpring Co. LTD.,", + [3]byte{52, 205, 109}: "CommSky Technologies", + [3]byte{52, 205, 190}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{52, 206, 0}: "XIAOMI Electronics,CO.,LTD", + [3]byte{52, 206, 148}: "Parsec (Pty) Ltd", + [3]byte{52, 208, 155}: "MobilMAX Technology Inc.", + [3]byte{52, 210, 112}: "Amazon Technologies Inc.", + [3]byte{52, 210, 196}: "RENA GmbH Print Systeme", + [3]byte{52, 215, 180}: "Tributary Systems, Inc.", + [3]byte{52, 219, 253}: "Cisco Systems, Inc", + [3]byte{52, 222, 26}: "Intel Corporate", + [3]byte{52, 222, 52}: "zte corporation", + [3]byte{52, 223, 42}: "Fujikon Industrial Co.,Limited", + [3]byte{52, 224, 207}: "zte corporation", + [3]byte{52, 224, 215}: "DONGGUAN QISHENG ELECTRONICS INDUSTRIAL CO., LTD", + [3]byte{52, 226, 253}: "Apple, Inc.", + [3]byte{52, 228, 42}: "Automatic Bar Controls Inc.", + [3]byte{52, 230, 173}: "Intel Corporate", + [3]byte{52, 230, 215}: "Dell Inc.", + [3]byte{52, 231, 11}: "HAN Networks Co., Ltd", + [3]byte{52, 231, 28}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{52, 234, 52}: "HangZhou Gubei Electronics Technology Co.,Ltd", + [3]byte{52, 237, 11}: "Shanghai XZ-COM.CO.,Ltd.", + [3]byte{52, 239, 68}: "2Wire Inc", + [3]byte{52, 239, 139}: "NTT Communications Corporation", + [3]byte{52, 240, 202}: "Shenzhen Linghangyuan Digital Technology Co.,Ltd.", + [3]byte{52, 243, 154}: "Intel Corporate", + [3]byte{52, 243, 155}: "WizLAN Ltd.", + [3]byte{52, 246, 45}: "SHARP Corporation", + [3]byte{52, 246, 210}: "Panasonic Taiwan Co.,Ltd.", + [3]byte{52, 249, 104}: "ATEK Products, LLC", + [3]byte{52, 250, 64}: "Guangzhou Robustel Technologies Co., Limited", + [3]byte{52, 252, 111}: "ALCEA", + [3]byte{52, 252, 185}: "Hewlett Packard Enterprise", + [3]byte{52, 252, 239}: "LG Electronics (Mobile Communications)", + [3]byte{56, 1, 149}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 1, 151}: "TSST Global,Inc", + [3]byte{56, 5, 70}: "Foctek Photonics, Inc.", + [3]byte{56, 5, 172}: "Piller Group GmbH", + [3]byte{56, 6, 180}: "A.D.C. GmbH", + [3]byte{56, 8, 253}: "Silca Spa", + [3]byte{56, 9, 164}: "Firefly Integrations", + [3]byte{56, 10, 10}: "Sky-City Communication and Electronics Limited Company", + [3]byte{56, 10, 148}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 10, 171}: "Formlabs", + [3]byte{56, 11, 64}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 13, 212}: "Primax Electronics Ltd.", + [3]byte{56, 14, 123}: "V.P.S. Thai Co., Ltd", + [3]byte{56, 15, 74}: "Apple, Inc.", + [3]byte{56, 15, 228}: "Dedicated Network Partners Oy", + [3]byte{56, 16, 213}: "AVM Audiovisuelles Marketing und Computersysteme GmbH", + [3]byte{56, 22, 209}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 23, 102}: "PROMZAKAZ LTD.", + [3]byte{56, 25, 47}: "Nokia Corporation", + [3]byte{56, 28, 26}: "Cisco Systems, Inc", + [3]byte{56, 28, 35}: "Hilan Technology CO.,LTD", + [3]byte{56, 28, 74}: "SIMCom Wireless Solutions Co.,Ltd.", + [3]byte{56, 29, 217}: "FN-LINK TECHNOLOGY LIMITED", + [3]byte{56, 32, 86}: "Cisco Systems, Inc", + [3]byte{56, 33, 135}: "Midea Group Co., Ltd.", + [3]byte{56, 34, 157}: "ADB Broadband Italia", + [3]byte{56, 34, 214}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{56, 37, 107}: "Microsoft Mobile Oy", + [3]byte{56, 38, 43}: "UTran Technology", + [3]byte{56, 38, 205}: "ANDTEK", + [3]byte{56, 40, 234}: "Fujian Netcom Technology Co., LTD", + [3]byte{56, 41, 90}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{56, 41, 221}: "ONvocal Inc", + [3]byte{56, 43, 120}: "ECO PLUGS ENTERPRISE CO., LTD", + [3]byte{56, 44, 74}: "ASUSTek COMPUTER INC.", + [3]byte{56, 45, 209}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 45, 232}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 49, 172}: "WEG", + [3]byte{56, 58, 33}: "IEEE Registration Authority", + [3]byte{56, 59, 200}: "2Wire Inc", + [3]byte{56, 63, 16}: "DBL Technology Ltd.", + [3]byte{56, 66, 51}: "Wildeboer Bauteile GmbH", + [3]byte{56, 66, 166}: "Ingenieurbuero Stahlkopf", + [3]byte{56, 67, 105}: "Patrol Products Consortium LLC", + [3]byte{56, 69, 76}: "Light Labs, Inc.", + [3]byte{56, 69, 140}: "MyCloud Technology corporation", + [3]byte{56, 70, 8}: "zte corporation", + [3]byte{56, 72, 76}: "Apple, Inc.", + [3]byte{56, 75, 118}: "AIRTAME ApS", + [3]byte{56, 76, 79}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{56, 76, 144}: "ARRIS Group, Inc.", + [3]byte{56, 79, 240}: "AzureWave Technology Inc.", + [3]byte{56, 82, 26}: "Nokia", + [3]byte{56, 86, 16}: "CANDY HOUSE, Inc.", + [3]byte{56, 88, 12}: "Panaccess Systems GmbH", + [3]byte{56, 89, 248}: "MindMade Sp. z o.o.", + [3]byte{56, 89, 249}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{56, 90, 168}: "Beijing Zhongdun Security Technology Development Co.", + [3]byte{56, 95, 102}: "Cisco SPVTG", + [3]byte{56, 95, 195}: "Yu Jeong System, Co.Ltd", + [3]byte{56, 96, 119}: "PEGATRON CORPORATION", + [3]byte{56, 99, 187}: "Hewlett Packard", + [3]byte{56, 99, 246}: "3NOD MULTIMEDIA(SHENZHEN)CO.,LTD", + [3]byte{56, 102, 69}: "OOSIC Technology CO.,Ltd", + [3]byte{56, 103, 147}: "Asia Optical Co., Inc.", + [3]byte{56, 107, 187}: "ARRIS Group, Inc.", + [3]byte{56, 108, 155}: "Ivy Biomedical", + [3]byte{56, 110, 33}: "Wasion Group Ltd.", + [3]byte{56, 112, 12}: "ARRIS Group, Inc.", + [3]byte{56, 113, 222}: "Apple, Inc.", + [3]byte{56, 114, 192}: "Comtrend Corporation", + [3]byte{56, 118, 202}: "Shenzhen Smart Intelligent Technology Co.Ltd", + [3]byte{56, 118, 209}: "Euronda SpA", + [3]byte{56, 123, 71}: "AKELA, Inc.", + [3]byte{56, 131, 69}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{56, 134, 2}: "Flexoptix GmbH", + [3]byte{56, 137, 220}: "Opticon Sensors Europe B.V.", + [3]byte{56, 138, 183}: "ITC Networks", + [3]byte{56, 140, 80}: "LG Electronics", + [3]byte{56, 142, 231}: "Fanhattan LLC", + [3]byte{56, 145, 213}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{56, 145, 251}: "Xenox Holding BV", + [3]byte{56, 148, 150}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 149, 146}: "Beijing Tendyron Corporation", + [3]byte{56, 151, 214}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{56, 152, 216}: "MERITECH CO.,LTD", + [3]byte{56, 159, 131}: "OTN Systems N.V.", + [3]byte{56, 162, 140}: "SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.", + [3]byte{56, 164, 237}: "Xiaomi Communications Co Ltd", + [3]byte{56, 165, 60}: "COMECER Netherlands", + [3]byte{56, 165, 182}: "SHENZHEN MEGMEET ELECTRICAL CO.,LTD", + [3]byte{56, 168, 81}: "Moog, Ing", + [3]byte{56, 168, 107}: "Orga BV", + [3]byte{56, 169, 95}: "Actifio Inc", + [3]byte{56, 170, 60}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{56, 172, 61}: "Nephos Inc", + [3]byte{56, 175, 215}: "FUJITSU LIMITED", + [3]byte{56, 177, 45}: "Sonotronic Nagel GmbH", + [3]byte{56, 177, 219}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{56, 181, 77}: "Apple, Inc.", + [3]byte{56, 181, 189}: "E.G.O. Elektro-Ger", + [3]byte{56, 183, 37}: "Wistron Infocomm (Zhongshan) Corporation", + [3]byte{56, 183, 77}: "Fijowave Limited", + [3]byte{56, 184, 235}: "IEEE Registration Authority", + [3]byte{56, 187, 35}: "OzVision America LLC", + [3]byte{56, 187, 60}: "Avaya Inc", + [3]byte{56, 188, 1}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{56, 188, 26}: "MEIZU Technology Co., Ltd.", + [3]byte{56, 191, 47}: "Espec Corp.", + [3]byte{56, 191, 51}: "NEC CASIO Mobile Communications", + [3]byte{56, 192, 150}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{56, 199, 10}: "WiFiSong", + [3]byte{56, 199, 186}: "CS Services Co.,Ltd.", + [3]byte{56, 200, 92}: "Cisco SPVTG", + [3]byte{56, 201, 134}: "Apple, Inc.", + [3]byte{56, 201, 169}: "SMART High Reliability Solutions, Inc.", + [3]byte{56, 202, 151}: "Contour Design LLC", + [3]byte{56, 202, 218}: "Apple, Inc.", + [3]byte{56, 209, 53}: "EasyIO Corporation Sdn. Bhd.", + [3]byte{56, 210, 105}: "Texas Instruments", + [3]byte{56, 212, 11}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 213, 71}: "ASUSTek COMPUTER INC.", + [3]byte{56, 216, 47}: "zte corporation", + [3]byte{56, 219, 187}: "Sunbow Telecom Co., Ltd.", + [3]byte{56, 222, 96}: "Mohlenhoff GmbH", + [3]byte{56, 224, 142}: "Mitsubishi Electric Corporation", + [3]byte{56, 227, 197}: "Taicang T&W Electronics", + [3]byte{56, 229, 149}: "Shenzhen Gongjin Electronics Co.,Ltd", + [3]byte{56, 231, 216}: "HTC Corporation", + [3]byte{56, 232, 223}: "b gmbh medien + datenbanken", + [3]byte{56, 233, 140}: "Reco S.p.A.", + [3]byte{56, 234, 167}: "Hewlett Packard", + [3]byte{56, 236, 17}: "Novatek Microelectronics Corp.", + [3]byte{56, 236, 228}: "Samsung Electronics Co.,Ltd", + [3]byte{56, 237, 24}: "Cisco Systems, Inc", + [3]byte{56, 238, 157}: "Anedo Ltd.", + [3]byte{56, 240, 152}: "Vapor Stone Rail Systems", + [3]byte{56, 240, 200}: "Livestream", + [3]byte{56, 241, 53}: "SensorTec-Canada", + [3]byte{56, 242, 62}: "Microsoft Mobile Oy", + [3]byte{56, 243, 63}: "TATSUNO CORPORATION", + [3]byte{56, 245, 87}: "JOLATA, INC.", + [3]byte{56, 245, 151}: "home2net GmbH", + [3]byte{56, 247, 8}: "National Resource Management, Inc.", + [3]byte{56, 247, 178}: "SEOJUN ELECTRIC", + [3]byte{56, 248, 137}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{56, 248, 183}: "V2COM PARTICIPACOES S.A.", + [3]byte{56, 248, 202}: "OWIN Inc.", + [3]byte{56, 250, 202}: "Skyworth Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{56, 253, 254}: "IEEE Registration Authority", + [3]byte{56, 254, 197}: "Ellips B.V.", + [3]byte{56, 255, 54}: "Ruckus Wireless", + [3]byte{60, 2, 177}: "Creation Technologies LP", + [3]byte{60, 4, 191}: "PRAVIS SYSTEMS Co.Ltd.,", + [3]byte{60, 5, 24}: "Samsung Electronics Co.,Ltd", + [3]byte{60, 5, 171}: "Product Creation Studio", + [3]byte{60, 7, 84}: "Apple, Inc.", + [3]byte{60, 7, 113}: "Sony Corporation", + [3]byte{60, 8, 30}: "Beijing Yupont Electric Power Technology Co.,Ltd", + [3]byte{60, 8, 246}: "Cisco Systems, Inc", + [3]byte{60, 9, 109}: "Powerhouse Dynamics", + [3]byte{60, 12, 72}: "Servergy, Inc.", + [3]byte{60, 14, 35}: "Cisco Systems, Inc", + [3]byte{60, 15, 193}: "KBC Networks", + [3]byte{60, 16, 64}: "daesung network", + [3]byte{60, 16, 111}: "ALBAHITH TECHNOLOGIES ", + [3]byte{60, 21, 194}: "Apple, Inc.", + [3]byte{60, 21, 234}: "TESCOM CO., LTD.", + [3]byte{60, 24, 159}: "Nokia Corporation", + [3]byte{60, 24, 160}: "Luxshare Precision Industry Co.,Ltd.", + [3]byte{60, 25, 21}: "GFI Chrono Time", + [3]byte{60, 25, 125}: "Ericsson AB", + [3]byte{60, 26, 15}: "ClearSky Data", + [3]byte{60, 26, 87}: "Cardiopulmonary Corp", + [3]byte{60, 26, 121}: "Huayuan Technology CO.,LTD", + [3]byte{60, 28, 190}: "JADAK LLC", + [3]byte{60, 30, 4}: "D-Link International", + [3]byte{60, 30, 19}: "HANGZHOU SUNRISE TECHNOLOGY CO., LTD", + [3]byte{60, 37, 215}: "Nokia Corporation", + [3]byte{60, 38, 213}: "Sotera Wireless", + [3]byte{60, 39, 99}: "SLE quality engineering GmbH & Co. KG", + [3]byte{60, 42, 244}: "Brother Industries, LTD.", + [3]byte{60, 44, 148}: "æ­å·žå¾·æ¾œç§‘技有é™å…¬å¸ï¼ˆHangZhou Delan Technology Co.,Ltd)", + [3]byte{60, 45, 183}: "Texas Instruments", + [3]byte{60, 47, 58}: "SFORZATO Corp.", + [3]byte{60, 48, 12}: "Dewar Electronics Pty Ltd", + [3]byte{60, 49, 120}: "Qolsys Inc.", + [3]byte{60, 51, 0}: "Shenzhen Bilian electronic CO.,LTD", + [3]byte{60, 53, 86}: "Cognitec Systems GmbH", + [3]byte{60, 54, 61}: "Nokia Corporation", + [3]byte{60, 54, 228}: "ARRIS Group, Inc.", + [3]byte{60, 56, 136}: "ConnectQuest, llc", + [3]byte{60, 57, 195}: "JW Electronics Co., Ltd.", + [3]byte{60, 57, 231}: "IEEE Registration Authority", + [3]byte{60, 58, 115}: "Avaya Inc", + [3]byte{60, 63, 81}: "2CRSI", + [3]byte{60, 64, 79}: "GUANGDONG PISEN ELECTRONICS CO.,LTD", + [3]byte{60, 67, 142}: "ARRIS Group, Inc.", + [3]byte{60, 70, 216}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{60, 71, 17}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{60, 73, 55}: "ASSMANN Electronic GmbH", + [3]byte{60, 74, 146}: "Hewlett Packard", + [3]byte{60, 76, 105}: "Infinity System S.L.", + [3]byte{60, 78, 71}: "Etronic A/S", + [3]byte{60, 82, 130}: "Hewlett Packard", + [3]byte{60, 87, 189}: "Kessler Crane Inc.", + [3]byte{60, 87, 213}: "FiveCo", + [3]byte{60, 89, 30}: "TCL King Electrical Appliances (Huizhou) Co., Ltd", + [3]byte{60, 90, 55}: "Samsung Electronics Co.,Ltd", + [3]byte{60, 90, 180}: "Google, Inc.", + [3]byte{60, 92, 195}: "Shenzhen First Blue Chip Technology Ltd", + [3]byte{60, 94, 195}: "Cisco Systems, Inc", + [3]byte{60, 95, 1}: "Synerchip Co., Ltd.", + [3]byte{60, 97, 4}: "Juniper Networks", + [3]byte{60, 98, 0}: "Samsung Electronics Co.,Ltd", + [3]byte{60, 98, 120}: "SHENZHEN JETNET TECHNOLOGY CO.,LTD.", + [3]byte{60, 103, 22}: "Lily Robotics", + [3]byte{60, 103, 44}: "Sciovid Inc.", + [3]byte{60, 103, 140}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{60, 104, 22}: "VXi Corporation", + [3]byte{60, 106, 125}: "Niigata Power Systems Co., Ltd.", + [3]byte{60, 106, 157}: "Dexatek Technology LTD.", + [3]byte{60, 110, 99}: "Mitron OY", + [3]byte{60, 111, 69}: "Fiberpro Inc.", + [3]byte{60, 111, 234}: "Panasonic India Pvt. Ltd.", + [3]byte{60, 111, 247}: "EnTek Systems, Inc.", + [3]byte{60, 112, 89}: "MakerBot Industries", + [3]byte{60, 116, 55}: "RIM", + [3]byte{60, 117, 74}: "ARRIS Group, Inc.", + [3]byte{60, 119, 230}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{60, 120, 115}: "Airsonics", + [3]byte{60, 122, 138}: "ARRIS Group, Inc.", + [3]byte{60, 125, 177}: "Texas Instruments", + [3]byte{60, 127, 111}: "Telechips, Inc.", + [3]byte{60, 128, 170}: "Ransnet Singapore Pte Ltd", + [3]byte{60, 129, 216}: "Sagemcom Broadband SAS", + [3]byte{60, 131, 30}: "CKD Corporation", + [3]byte{60, 131, 117}: "Microsoft Corporation", + [3]byte{60, 131, 181}: "Advance Vision Electronics Co. Ltd.", + [3]byte{60, 134, 168}: "Sangshin elecom .co,, LTD", + [3]byte{60, 137, 112}: "Neosfar", + [3]byte{60, 137, 166}: "KAPELSE", + [3]byte{60, 138, 176}: "Juniper Networks", + [3]byte{60, 138, 229}: "Tensun Information Technology(Hangzhou) Co.,LTD", + [3]byte{60, 139, 205}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{60, 139, 254}: "Samsung Electronics Co.,Ltd", + [3]byte{60, 140, 64}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{60, 140, 248}: "TRENDnet, Inc.", + [3]byte{60, 144, 102}: "SmartRG, Inc.", + [3]byte{60, 145, 43}: "Vexata Inc", + [3]byte{60, 145, 87}: "Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd", + [3]byte{60, 145, 116}: "ALONG COMMUNICATION TECHNOLOGY", + [3]byte{60, 146, 220}: "Octopod Technology Co. Ltd.", + [3]byte{60, 148, 213}: "Juniper Networks", + [3]byte{60, 149, 9}: "Liteon Technology Corporation", + [3]byte{60, 151, 14}: "Wistron InfoComm(Kunshan)Co.,Ltd.", + [3]byte{60, 151, 126}: "IPS Technology Limited", + [3]byte{60, 152, 191}: "Quest Controls, Inc.", + [3]byte{60, 153, 247}: "Lansentechnology AB", + [3]byte{60, 159, 129}: "Shenzhen CATIC Bit Communications Technology Co.,Ltd", + [3]byte{60, 160, 103}: "Liteon Technology Corporation", + [3]byte{60, 161, 13}: "Samsung Electronics Co.,Ltd", + [3]byte{60, 163, 21}: "Bless Information & Communications Co., Ltd", + [3]byte{60, 163, 26}: "Oilfind International LLC", + [3]byte{60, 163, 72}: "vivo Mobile Communication Co., Ltd.", + [3]byte{60, 167, 43}: "MRV Communications (Networks) LTD", + [3]byte{60, 168, 42}: "Hewlett Packard", + [3]byte{60, 169, 244}: "Intel Corporate", + [3]byte{60, 170, 63}: "iKey, Ltd.", + [3]byte{60, 171, 142}: "Apple, Inc.", + [3]byte{60, 174, 105}: "ESA Elektroschaltanlagen Grimma GmbH", + [3]byte{60, 177, 91}: "Avaya Inc", + [3]byte{60, 177, 127}: "Wattwatchers Pty Ld", + [3]byte{60, 182, 183}: "vivo Mobile Communication Co., Ltd.", + [3]byte{60, 183, 43}: "PLUMgrid Inc", + [3]byte{60, 183, 146}: "Hitachi Maxell, Ltd., Optronics Division", + [3]byte{60, 184, 122}: "Private", + [3]byte{60, 185, 166}: "Belden Deutschland GmbH", + [3]byte{60, 187, 115}: "Shenzhen Xinguodu Technology Co., Ltd.", + [3]byte{60, 187, 253}: "Samsung Electronics Co.,Ltd", + [3]byte{60, 189, 62}: "Beijing Xiaomi Electronics Co., Ltd.", + [3]byte{60, 189, 216}: "LG ELECTRONICS INC", + [3]byte{60, 190, 225}: "NIKON CORPORATION", + [3]byte{60, 192, 198}: "d&b audiotechnik GmbH", + [3]byte{60, 193, 44}: "AES Corporation", + [3]byte{60, 193, 246}: "Melange Systems Pvt. Ltd.", + [3]byte{60, 194, 67}: "Nokia Corporation", + [3]byte{60, 194, 225}: "XINHUA CONTROL ENGINEERING CO.,LTD", + [3]byte{60, 201, 158}: "Huiyang Technology Co., Ltd", + [3]byte{60, 202, 135}: "Iders Incorporated", + [3]byte{60, 203, 124}: "TCT mobile ltd", + [3]byte{60, 205, 90}: "Technische Alternative GmbH", + [3]byte{60, 205, 147}: "LG ELECTRONICS INC", + [3]byte{60, 206, 21}: "Mercedes-Benz USA, LLC", + [3]byte{60, 206, 115}: "Cisco Systems, Inc", + [3]byte{60, 207, 91}: "ICOMM HK LIMITED", + [3]byte{60, 208, 248}: "Apple, Inc.", + [3]byte{60, 209, 110}: "Telepower Communication Co., Ltd", + [3]byte{60, 212, 214}: "WirelessWERX, Inc", + [3]byte{60, 215, 218}: "SK Mtek microelectronics(shenzhen)limited", + [3]byte{60, 217, 43}: "Hewlett Packard", + [3]byte{60, 217, 206}: "Eclipse WiFi", + [3]byte{60, 218, 42}: "zte corporation", + [3]byte{60, 221, 137}: "SOMO HOLDINGS & TECH. CO.,LTD.", + [3]byte{60, 223, 30}: "Cisco Systems, Inc", + [3]byte{60, 223, 169}: "ARRIS Group, Inc.", + [3]byte{60, 223, 189}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{60, 224, 114}: "Apple, Inc.", + [3]byte{60, 229, 166}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{60, 229, 180}: "KIDASEN INDUSTRIA E COMERCIO DE ANTENAS LTDA", + [3]byte{60, 230, 36}: "LG Display ", + [3]byte{60, 234, 79}: "2Wire Inc", + [3]byte{60, 234, 251}: "NSE AG", + [3]byte{60, 239, 140}: "Zhejiang Dahua Technology Co., Ltd.", + [3]byte{60, 243, 146}: "Virtualtek. Co. Ltd", + [3]byte{60, 245, 44}: "DSPECIALISTS GmbH", + [3]byte{60, 247, 42}: "Nokia Corporation", + [3]byte{60, 247, 72}: "Shenzhen Linsn Technology Development Co.,Ltd", + [3]byte{60, 248, 8}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{60, 248, 98}: "Intel Corporate", + [3]byte{60, 250, 67}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{60, 251, 150}: "Emcraft Systems LLC", + [3]byte{60, 253, 254}: "Intel Corporate", + [3]byte{64, 0, 224}: "Derek(Shaoguan)Limited", + [3]byte{64, 1, 7}: "Arista Corp", + [3]byte{64, 1, 198}: "3COM EUROPE LTD", + [3]byte{64, 4, 12}: "A&T", + [3]byte{64, 7, 192}: "Railtec Systems GmbH", + [3]byte{64, 13, 16}: "ARRIS Group, Inc.", + [3]byte{64, 14, 103}: "Tremol Ltd.", + [3]byte{64, 14, 133}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{64, 17, 220}: "Sonance", + [3]byte{64, 18, 228}: "Compass-EOS", + [3]byte{64, 19, 217}: "Global ES", + [3]byte{64, 21, 151}: "Protect America, Inc.", + [3]byte{64, 22, 59}: "Samsung Electronics Co.,Ltd", + [3]byte{64, 22, 126}: "ASUSTek COMPUTER INC.", + [3]byte{64, 22, 159}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{64, 22, 250}: "EKM Metering", + [3]byte{64, 24, 177}: "Aerohive Networks Inc.", + [3]byte{64, 24, 215}: "Smartronix, Inc.", + [3]byte{64, 27, 95}: "Weifang GoerTek Electronics Co., Ltd.", + [3]byte{64, 29, 89}: "Biometric Associates, LP", + [3]byte{64, 34, 237}: "Digital Projection Ltd", + [3]byte{64, 37, 194}: "Intel Corporate", + [3]byte{64, 39, 11}: "Mobileeco Co., Ltd", + [3]byte{64, 40, 20}: "RFI Engineering", + [3]byte{64, 43, 161}: "Sony Mobile Communications AB", + [3]byte{64, 44, 244}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{64, 46, 40}: "MiXTelematics", + [3]byte{64, 48, 4}: "Apple, Inc.", + [3]byte{64, 48, 103}: "Conlog (Pty) Ltd", + [3]byte{64, 51, 26}: "Apple, Inc.", + [3]byte{64, 51, 108}: "Godrej & Boyce Mfg. co. ltd", + [3]byte{64, 55, 173}: "Macro Image Technology, Inc.", + [3]byte{64, 60, 252}: "Apple, Inc.", + [3]byte{64, 61, 236}: "HUMAX Co., Ltd.", + [3]byte{64, 63, 140}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{64, 64, 34}: "ZIV", + [3]byte{64, 64, 107}: "Icomera", + [3]byte{64, 64, 167}: "Sony Mobile Communications AB", + [3]byte{64, 69, 218}: "Spreadtrum Communications (Shanghai) Co., Ltd.", + [3]byte{64, 71, 106}: "AG Acquisition Corp. d.b.a. ASTRO Gaming", + [3]byte{64, 73, 15}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{64, 74, 3}: "ZyXEL Communications Corporation", + [3]byte{64, 74, 24}: "Addrek Smart Solutions", + [3]byte{64, 74, 212}: "Widex A/S", + [3]byte{64, 77, 127}: "Apple, Inc.", + [3]byte{64, 77, 142}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{64, 78, 54}: "HTC Corporation", + [3]byte{64, 78, 235}: "Higher Way Electronic Co., Ltd.", + [3]byte{64, 80, 224}: "Milton Security Group LLC", + [3]byte{64, 81, 108}: "Grandex International Corporation", + [3]byte{64, 82, 13}: "Pico Technology", + [3]byte{64, 84, 228}: "Wearsafe Labs Inc", + [3]byte{64, 85, 57}: "Cisco Systems, Inc", + [3]byte{64, 86, 12}: "In Home Displays Ltd", + [3]byte{64, 86, 45}: "Smartron India Pvt ltd", + [3]byte{64, 90, 155}: "ANOVO", + [3]byte{64, 93, 130}: "NETGEAR", + [3]byte{64, 94, 225}: "Shenzhen H&T Intelligent Control Co.,Ltd.", + [3]byte{64, 95, 190}: "RIM", + [3]byte{64, 95, 194}: "Texas Instruments", + [3]byte{64, 96, 90}: "Hawkeye Tech Co. Ltd", + [3]byte{64, 97, 134}: "MICRO-STAR INT'L CO.,LTD", + [3]byte{64, 97, 142}: "Stella-Green Co", + [3]byte{64, 98, 182}: "Tele system communication", + [3]byte{64, 101, 163}: "Sagemcom Broadband SAS", + [3]byte{64, 102, 122}: "mediola - connected living AG", + [3]byte{64, 104, 38}: "Thales UK Limited", + [3]byte{64, 106, 171}: "RIM", + [3]byte{64, 108, 143}: "Apple, Inc.", + [3]byte{64, 111, 42}: "BlackBerry RTS", + [3]byte{64, 112, 9}: "ARRIS Group, Inc.", + [3]byte{64, 112, 74}: "Power Idea Technology Limited", + [3]byte{64, 112, 116}: "Life Technology (China) Co., Ltd", + [3]byte{64, 113, 131}: "Juniper Networks", + [3]byte{64, 116, 150}: "aFUN TECHNOLOGY INC.", + [3]byte{64, 120, 106}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{64, 120, 117}: "IMBEL - Industria de Material Belico do Brasil", + [3]byte{64, 122, 128}: "Nokia Corporation", + [3]byte{64, 123, 27}: "Mettle Networks Inc.", + [3]byte{64, 124, 125}: "Nokia", + [3]byte{64, 125, 15}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{64, 127, 224}: "Glory Star Technics (ShenZhen) Limited", + [3]byte{64, 130, 86}: "Continental Automotive GmbH", + [3]byte{64, 131, 222}: "Zebra Technologies Inc", + [3]byte{64, 132, 147}: "Clavister AB", + [3]byte{64, 134, 46}: "JDM MOBILE INTERNET SOLUTION CO., LTD.", + [3]byte{64, 136, 5}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{64, 136, 224}: "Beijing Ereneben Information Technology Limited Shenzhen Branch", + [3]byte{64, 138, 154}: "TITENG CO., Ltd.", + [3]byte{64, 139, 7}: "Actiontec Electronics, Inc", + [3]byte{64, 139, 246}: "Shenzhen TCL New Technology Co; Ltd.", + [3]byte{64, 141, 92}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{64, 149, 88}: "Aisino Corporation", + [3]byte{64, 149, 189}: "NTmore.Co.,Ltd", + [3]byte{64, 151, 209}: "BK Electronics cc", + [3]byte{64, 152, 76}: "Casacom Solutions AG", + [3]byte{64, 152, 78}: "Texas Instruments", + [3]byte{64, 152, 123}: "Aisino Corporation", + [3]byte{64, 155, 13}: "Shenzhen Yourf Kwan Industrial Co., Ltd", + [3]byte{64, 159, 56}: "AzureWave Technology Inc.", + [3]byte{64, 159, 135}: "Jide Technology (Hong Kong) Limited", + [3]byte{64, 159, 199}: "BAEKCHUN I&C Co., Ltd.", + [3]byte{64, 165, 239}: "Shenzhen Four Seas Global Link Network Technology Co., Ltd.", + [3]byte{64, 166, 119}: "Juniper Networks", + [3]byte{64, 166, 164}: "PassivSystems Ltd", + [3]byte{64, 166, 217}: "Apple, Inc.", + [3]byte{64, 166, 232}: "Cisco Systems, Inc", + [3]byte{64, 168, 240}: "Hewlett Packard", + [3]byte{64, 172, 141}: "Data Management, Inc.", + [3]byte{64, 176, 52}: "Hewlett Packard", + [3]byte{64, 176, 250}: "LG Electronics (Mobile Communications)", + [3]byte{64, 178, 200}: "Nortel Networks", + [3]byte{64, 179, 149}: "Apple, Inc.", + [3]byte{64, 179, 205}: "Chiyoda Electronics Co.,Ltd.", + [3]byte{64, 179, 252}: "Logital Co. Limited ", + [3]byte{64, 180, 205}: "Amazon Technologies Inc.", + [3]byte{64, 180, 240}: "Juniper Networks", + [3]byte{64, 182, 136}: "LEGIC Identsystems AG", + [3]byte{64, 182, 177}: "SUNGSAM CO,.Ltd", + [3]byte{64, 183, 243}: "ARRIS Group, Inc.", + [3]byte{64, 184, 55}: "Sony Mobile Communications AB", + [3]byte{64, 184, 154}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{64, 185, 60}: "Hewlett Packard Enterprise", + [3]byte{64, 186, 97}: "ARIMA Communications Corp.", + [3]byte{64, 188, 115}: "Cronoplast S.L.", + [3]byte{64, 188, 139}: "itelio GmbH", + [3]byte{64, 189, 158}: "Physio-Control, Inc", + [3]byte{64, 191, 23}: "Digistar Telecom. SA", + [3]byte{64, 194, 69}: "Shenzhen Hexicom Technology Co., Ltd.", + [3]byte{64, 196, 214}: "ChongQing Camyu Technology Development Co.,Ltd.", + [3]byte{64, 198, 42}: "Shanghai Jing Ren Electronic Technology Co., Ltd.", + [3]byte{64, 199, 41}: "Sagemcom Broadband SAS", + [3]byte{64, 199, 201}: "Naviit Inc.", + [3]byte{64, 200, 203}: "AM Telecom co., Ltd.", + [3]byte{64, 203, 168}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{64, 205, 58}: "Z3 Technology", + [3]byte{64, 210, 138}: "Nintendo Co., Ltd.", + [3]byte{64, 211, 45}: "Apple, Inc.", + [3]byte{64, 211, 87}: "Ison Technology Co., Ltd.", + [3]byte{64, 211, 174}: "Samsung Electronics Co.,Ltd", + [3]byte{64, 212, 14}: "Biodata Ltd", + [3]byte{64, 213, 89}: "MICRO S.E.R.I.", + [3]byte{64, 216, 85}: "IEEE Registration Authority", + [3]byte{64, 226, 48}: "AzureWave Technology Inc.", + [3]byte{64, 227, 214}: "Aruba Networks", + [3]byte{64, 231, 48}: "DEY Storage Systems, Inc.", + [3]byte{64, 231, 147}: "Shenzhen Siviton Technology Co.,Ltd", + [3]byte{64, 234, 206}: "FOUNDER BROADBAND NETWORK SERVICE CO.,LTD", + [3]byte{64, 236, 248}: "Siemens AG", + [3]byte{64, 237, 152}: "IEEE Registration Authority", + [3]byte{64, 239, 76}: "Fihonest communication co.,Ltd", + [3]byte{64, 240, 47}: "Liteon Technology Corporation", + [3]byte{64, 241, 76}: "ISE Europe SPRL", + [3]byte{64, 242, 1}: "Sagemcom Broadband SAS", + [3]byte{64, 242, 233}: "IBM", + [3]byte{64, 243, 8}: "Murata Manufacturing Co., Ltd.", + [3]byte{64, 243, 133}: "IEEE Registration Authority", + [3]byte{64, 244, 7}: "Nintendo Co., Ltd.", + [3]byte{64, 244, 19}: "Rubezh", + [3]byte{64, 244, 32}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{64, 244, 236}: "Cisco Systems, Inc", + [3]byte{64, 245, 46}: "Leica Microsystems (Schweiz) AG", + [3]byte{64, 250, 127}: "Preh Car Connect GmbH", + [3]byte{64, 252, 137}: "ARRIS Group, Inc.", + [3]byte{64, 254, 13}: "MAXIO", + [3]byte{68, 0, 16}: "Apple, Inc.", + [3]byte{68, 3, 44}: "Intel Corporate", + [3]byte{68, 3, 167}: "Cisco Systems, Inc", + [3]byte{68, 4, 68}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{68, 9, 184}: "Salcomp (Shenzhen) CO., LTD.", + [3]byte{68, 12, 253}: "NetMan Co., Ltd.", + [3]byte{68, 17, 2}: "EDMI Europe Ltd", + [3]byte{68, 17, 194}: "Telegartner Karl Gartner GmbH", + [3]byte{68, 19, 25}: "WKK TECHNOLOGY LTD.", + [3]byte{68, 20, 65}: "AudioControl Inc.", + [3]byte{68, 24, 79}: "Fitview", + [3]byte{68, 25, 182}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{68, 28, 168}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{68, 30, 145}: "ARVIDA Intelligent Electronics Technology Co.,Ltd.", + [3]byte{68, 30, 161}: "Hewlett Packard", + [3]byte{68, 35, 170}: "Farmage Co., Ltd.", + [3]byte{68, 37, 187}: "Bamboo Entertainment Corporation", + [3]byte{68, 41, 56}: "NietZsche enterprise Co.Ltd.", + [3]byte{68, 42, 96}: "Apple, Inc.", + [3]byte{68, 42, 255}: "E3 Technology, Inc.", + [3]byte{68, 43, 3}: "Cisco Systems, Inc", + [3]byte{68, 44, 5}: "AMPAK Technology, Inc.", + [3]byte{68, 49, 146}: "Hewlett Packard", + [3]byte{68, 50, 42}: "Avaya Inc", + [3]byte{68, 50, 200}: "Technicolor CH USA Inc.", + [3]byte{68, 51, 76}: "Shenzhen Bilian electronic CO.,LTD", + [3]byte{68, 52, 143}: "MXT INDUSTRIAL LTDA", + [3]byte{68, 53, 111}: "Neterix", + [3]byte{68, 55, 8}: "MRV Comunications", + [3]byte{68, 55, 25}: "2 Save Energy Ltd", + [3]byte{68, 55, 111}: "Young Electric Sign Co", + [3]byte{68, 55, 230}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{68, 56, 57}: "Cumulus Networks, inc", + [3]byte{68, 57, 196}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{68, 60, 156}: "Pintsch Tiefenbach GmbH", + [3]byte{68, 61, 33}: "Nuvolt", + [3]byte{68, 62, 178}: "DEOTRON Co., LTD.", + [3]byte{68, 68, 80}: "OttoQ", + [3]byte{68, 72, 145}: "HDMI Licensing, LLC", + [3]byte{68, 72, 193}: "Hewlett Packard Enterprise", + [3]byte{68, 74, 101}: "Silverflare Ltd.", + [3]byte{68, 76, 12}: "Apple, Inc.", + [3]byte{68, 76, 168}: "Arista Networks", + [3]byte{68, 78, 26}: "Samsung Electronics Co.,Ltd", + [3]byte{68, 79, 94}: "Pan Studios Co.,Ltd.", + [3]byte{68, 81, 219}: "Raytheon BBN Technologies", + [3]byte{68, 84, 192}: "Thompson Aerospace", + [3]byte{68, 85, 177}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{68, 86, 141}: "PNC Technologies Co., Ltd.", + [3]byte{68, 86, 183}: "Spawn Labs, Inc", + [3]byte{68, 88, 41}: "Cisco SPVTG", + [3]byte{68, 89, 159}: "Criticare Systems, Inc", + [3]byte{68, 94, 205}: "Razer Inc", + [3]byte{68, 94, 243}: "Tonalite Holding B.V.", + [3]byte{68, 95, 122}: "Shihlin Electric & Engineering Corp.", + [3]byte{68, 95, 140}: "Intercel Group Limited", + [3]byte{68, 97, 50}: "ecobee inc", + [3]byte{68, 97, 156}: "FONsystem co. ltd.", + [3]byte{68, 98, 70}: "Comat AG", + [3]byte{68, 101, 13}: "Amazon Technologies Inc.", + [3]byte{68, 101, 106}: "Mega Video Electronic(HK) Industry Co., Ltd", + [3]byte{68, 102, 110}: "IP-LINE", + [3]byte{68, 103, 85}: "Orbit Irrigation", + [3]byte{68, 104, 171}: "JUIN COMPANY, LIMITED", + [3]byte{68, 106, 46}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{68, 106, 183}: "ARRIS Group, Inc.", + [3]byte{68, 108, 36}: "Reallin Electronic Co.,Ltd", + [3]byte{68, 109, 87}: "Liteon Technology Corporation", + [3]byte{68, 109, 108}: "Samsung Electronics Co.,Ltd", + [3]byte{68, 110, 229}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{68, 112, 11}: "IFFU", + [3]byte{68, 112, 152}: "MING HONG TECHNOLOGY (SHEN ZHEN) LIMITED", + [3]byte{68, 115, 214}: "Logitech", + [3]byte{68, 116, 108}: "Sony Mobile Communications AB", + [3]byte{68, 120, 62}: "Samsung Electronics Co.,Ltd", + [3]byte{68, 123, 196}: "DualShine Technology(SZ)Co.,Ltd", + [3]byte{68, 124, 127}: "Innolight Technology Corporation", + [3]byte{68, 125, 165}: "VTION INFORMATION TECHNOLOGY (FUJIAN) CO.,LTD", + [3]byte{68, 126, 118}: "Trek Technology (S) Pte Ltd", + [3]byte{68, 126, 149}: "Alpha and Omega, Inc", + [3]byte{68, 128, 235}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{68, 130, 229}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{68, 131, 18}: "Star-Net", + [3]byte{68, 133, 0}: "Intel Corporate", + [3]byte{68, 134, 193}: "Siemens Low Voltage & Products", + [3]byte{68, 135, 35}: "HOYA SERVICE CORPORATION", + [3]byte{68, 135, 252}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{68, 136, 203}: "Camco Technologies NV", + [3]byte{68, 138, 91}: "Micro-Star INT'L CO., LTD.", + [3]byte{68, 140, 82}: "KTIS CO., Ltd", + [3]byte{68, 142, 18}: "DT Research, Inc.", + [3]byte{68, 142, 129}: "VIG", + [3]byte{68, 145, 219}: "Shanghai Huaqin Telecom Technology Co.,Ltd", + [3]byte{68, 148, 252}: "NETGEAR", + [3]byte{68, 149, 250}: "Qingdao Santong Digital Technology Co.Ltd", + [3]byte{68, 150, 43}: "Aidon Oy", + [3]byte{68, 151, 90}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{68, 155, 120}: "The Now Factory", + [3]byte{68, 156, 181}: "Alcomp, Inc", + [3]byte{68, 159, 127}: "DataCore Software Corporation", + [3]byte{68, 164, 45}: "TCT mobile ltd", + [3]byte{68, 166, 137}: "PROMAX ELECTRONICA SA", + [3]byte{68, 166, 229}: "THINKING TECHNOLOGY CO.,LTD", + [3]byte{68, 167, 207}: "Murata Manufacturing Co., Ltd.", + [3]byte{68, 168, 66}: "Dell Inc.", + [3]byte{68, 168, 194}: "SEWOO TECH CO., LTD", + [3]byte{68, 170, 39}: "udworks Co., Ltd.", + [3]byte{68, 170, 232}: "Nanotec Electronic GmbH & Co. KG", + [3]byte{68, 170, 245}: "ARRIS Group, Inc.", + [3]byte{68, 173, 217}: "Cisco Systems, Inc", + [3]byte{68, 179, 45}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{68, 179, 130}: "Kuang-chi Institute of Advanced Technology", + [3]byte{68, 186, 70}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{68, 191, 227}: "Shenzhen Longtech Electronics Co.,Ltd", + [3]byte{68, 193, 92}: "Texas Instruments", + [3]byte{68, 194, 51}: "Guangzhou Comet Technology Development Co.Ltd", + [3]byte{68, 195, 6}: "SIFROM Inc.", + [3]byte{68, 195, 70}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{68, 195, 155}: "OOO RUBEZH NPO", + [3]byte{68, 196, 169}: "Opticom Communication, LLC", + [3]byte{68, 197, 111}: "NGN Easy Satfinder (Tianjin) Electronic Co., Ltd", + [3]byte{68, 198, 155}: "Wuhan Feng Tian Information Network CO.,LTD", + [3]byte{68, 201, 162}: "Greenwald Industries", + [3]byte{68, 206, 125}: "SFR", + [3]byte{68, 209, 94}: "Shanghai Kingto Information Technology Ltd", + [3]byte{68, 209, 250}: "Shenzhen Yunlink Technology Co., Ltd", + [3]byte{68, 210, 68}: "Seiko Epson Corporation", + [3]byte{68, 210, 202}: "Anvia TV Oy", + [3]byte{68, 211, 202}: "Cisco Systems, Inc", + [3]byte{68, 212, 55}: "Inteno Broadband Technology AB", + [3]byte{68, 212, 224}: "Sony Mobile Communications AB", + [3]byte{68, 214, 61}: "Talari Networks", + [3]byte{68, 214, 225}: "Snuza International Pty. Ltd.", + [3]byte{68, 216, 50}: "AzureWave Technology Inc.", + [3]byte{68, 216, 132}: "Apple, Inc.", + [3]byte{68, 217, 231}: "Ubiquiti Networks Inc.", + [3]byte{68, 220, 145}: "PLANEX COMMUNICATIONS INC.", + [3]byte{68, 220, 203}: "SEMINDIA SYSTEMS PVT LTD", + [3]byte{68, 224, 142}: "Cisco SPVTG", + [3]byte{68, 225, 55}: "ARRIS Group, Inc.", + [3]byte{68, 228, 154}: "OMNITRONICS PTY LTD", + [3]byte{68, 228, 217}: "Cisco Systems, Inc", + [3]byte{68, 232, 165}: "Myreka Technologies Sdn. Bhd.", + [3]byte{68, 233, 221}: "Sagemcom Broadband SAS", + [3]byte{68, 237, 87}: "Longicorn, inc.", + [3]byte{68, 238, 2}: "MTI Ltd.", + [3]byte{68, 238, 48}: "Budelmann Elektronik GmbH", + [3]byte{68, 244, 54}: "zte corporation", + [3]byte{68, 244, 89}: "Samsung Electronics Co.,Ltd", + [3]byte{68, 244, 119}: "Juniper Networks", + [3]byte{68, 248, 73}: "Union Pacific Railroad", + [3]byte{68, 251, 66}: "Apple, Inc.", + [3]byte{68, 253, 163}: "Everysight LTD.", + [3]byte{72, 0, 49}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 0, 51}: "Technicolor CH USA Inc.", + [3]byte{72, 2, 42}: "B-Link Electronic Limited", + [3]byte{72, 3, 98}: "DESAY ELECTRONICS(HUIZHOU)CO.,LTD", + [3]byte{72, 6, 106}: "Tempered Networks, Inc.", + [3]byte{72, 12, 73}: "NAKAYO Inc", + [3]byte{72, 15, 207}: "Hewlett Packard", + [3]byte{72, 18, 73}: "Luxcom Technologies Inc.", + [3]byte{72, 19, 126}: "Samsung Electronics Co.,Ltd", + [3]byte{72, 19, 243}: "BBK EDUCATIONAL ELECTRONICS CORP.,LTD.", + [3]byte{72, 23, 76}: "MicroPower technologies", + [3]byte{72, 24, 66}: "Shanghai Winaas Co. Equipment Co. Ltd.", + [3]byte{72, 26, 132}: "Pointer Telocation Ltd", + [3]byte{72, 27, 210}: "Intron Scientific co., ltd.", + [3]byte{72, 29, 112}: "Cisco SPVTG", + [3]byte{72, 38, 232}: "Tek-Air Systems, Inc.", + [3]byte{72, 39, 234}: "Samsung Electronics Co.,Ltd", + [3]byte{72, 40, 47}: "zte corporation", + [3]byte{72, 44, 234}: "Motorola Inc Business Light Radios", + [3]byte{72, 51, 221}: "ZENNIO AVANCE Y TECNOLOGIA, S.L.", + [3]byte{72, 52, 61}: "IEP GmbH", + [3]byte{72, 54, 95}: "Wintecronics Ltd.", + [3]byte{72, 57, 116}: "Proware Technologies Co., Ltd.", + [3]byte{72, 59, 56}: "Apple, Inc.", + [3]byte{72, 60, 12}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 61, 50}: "Syscor Controls & Automation", + [3]byte{72, 67, 90}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 67, 124}: "Apple, Inc.", + [3]byte{72, 68, 135}: "Cisco SPVTG", + [3]byte{72, 68, 247}: "Samsung Electronics Co.,Ltd", + [3]byte{72, 69, 32}: "Intel Corporate", + [3]byte{72, 70, 241}: "Uros Oy", + [3]byte{72, 70, 251}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 73, 199}: "Samsung Electronics Co.,Ltd", + [3]byte{72, 75, 170}: "Apple, Inc.", + [3]byte{72, 77, 126}: "Dell Inc.", + [3]byte{72, 80, 115}: "Microsoft Corporation", + [3]byte{72, 81, 183}: "Intel Corporate", + [3]byte{72, 82, 97}: "SOREEL", + [3]byte{72, 84, 21}: "NET RULES TECNOLOGIA EIRELI", + [3]byte{72, 85, 95}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{72, 87, 221}: "Facebook", + [3]byte{72, 89, 41}: "LG Electronics (Mobile Communications)", + [3]byte{72, 90, 63}: "WISOL", + [3]byte{72, 90, 182}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{72, 91, 57}: "ASUSTek COMPUTER INC.", + [3]byte{72, 93, 54}: "Verizon", + [3]byte{72, 93, 96}: "AzureWave Technology Inc.", + [3]byte{72, 96, 188}: "Apple, Inc.", + [3]byte{72, 97, 163}: "Concern Axion JSC", + [3]byte{72, 98, 118}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 101, 238}: "IEEE Registration Authority", + [3]byte{72, 107, 44}: "BBK EDUCATIONAL ELECTRONICS CORP.,LTD.", + [3]byte{72, 107, 145}: "Fleetwood Group Inc.", + [3]byte{72, 109, 187}: "Vestel Elektronik San ve Tic. A.Åž.", + [3]byte{72, 110, 115}: "Pica8, Inc.", + [3]byte{72, 110, 251}: "Davit System Technology Co., Ltd.", + [3]byte{72, 111, 210}: "StorSimple Inc", + [3]byte{72, 113, 25}: "SGB GROUP LTD.", + [3]byte{72, 116, 110}: "Apple, Inc.", + [3]byte{72, 118, 4}: "Private", + [3]byte{72, 122, 85}: "ALE International", + [3]byte{72, 122, 218}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{72, 123, 107}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 130, 68}: "Life Fitness / Div. of Brunswick", + [3]byte{72, 130, 242}: "Appel Elektronik GmbH", + [3]byte{72, 131, 199}: "Sagemcom Broadband SAS", + [3]byte{72, 134, 232}: "Microsoft Corporation", + [3]byte{72, 136, 3}: "ManTechnology Inc.", + [3]byte{72, 136, 202}: "Motorola (Wuhan) Mobility Technologies Communication Co., Ltd.", + [3]byte{72, 138, 210}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{72, 142, 66}: "DIGALOG GmbH", + [3]byte{72, 145, 83}: "Weinmann Geräte für Medizin GmbH + Co. KG", + [3]byte{72, 145, 246}: "Shenzhen Reach software technology CO.,LTD", + [3]byte{72, 154, 66}: "Technomate Ltd", + [3]byte{72, 155, 226}: "SCI Innovations Ltd", + [3]byte{72, 157, 24}: "Flashbay Limited", + [3]byte{72, 157, 36}: "BlackBerry RTS", + [3]byte{72, 161, 149}: "Apple, Inc.", + [3]byte{72, 162, 45}: "Shenzhen Huaxuchang Telecom Technology Co.,Ltd", + [3]byte{72, 162, 183}: "Kodofon JSC", + [3]byte{72, 163, 128}: "Gionee Communication Equipment Co.,Ltd.", + [3]byte{72, 166, 210}: "GJsun Optical Science and Tech Co.,Ltd.", + [3]byte{72, 167, 78}: "zte corporation", + [3]byte{72, 169, 210}: "Wistron Neweb Corporation", + [3]byte{72, 170, 93}: "Store Electronic Systems", + [3]byte{72, 173, 8}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 178, 83}: "Marketaxess Corporation", + [3]byte{72, 181, 167}: "Glory Horse Industries Ltd.", + [3]byte{72, 182, 32}: "ROLI Ltd.", + [3]byte{72, 184, 222}: "HOMEWINS TECHNOLOGY CO.,LTD.", + [3]byte{72, 185, 119}: "PulseOn Oy", + [3]byte{72, 185, 194}: "Teletics Inc.", + [3]byte{72, 190, 45}: "Symanitron", + [3]byte{72, 191, 107}: "Apple, Inc.", + [3]byte{72, 191, 116}: "Baicells Technologies Co.,LTD", + [3]byte{72, 192, 73}: "Broad Telecom SA", + [3]byte{72, 192, 147}: "Xirrus, Inc.", + [3]byte{72, 193, 172}: "PLANTRONICS, INC.", + [3]byte{72, 198, 99}: "GTO Access Systems LLC", + [3]byte{72, 200, 98}: "Simo Wireless,Inc.", + [3]byte{72, 200, 182}: "SysTec GmbH", + [3]byte{72, 203, 110}: "Cello Electronics (UK) Ltd", + [3]byte{72, 208, 207}: "Universal Electronics, Inc.", + [3]byte{72, 209, 142}: "Metis Communication Co.,Ltd", + [3]byte{72, 210, 36}: "Liteon Technology Corporation", + [3]byte{72, 211, 67}: "ARRIS Group, Inc.", + [3]byte{72, 213, 57}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 213, 76}: "Jeda Networks", + [3]byte{72, 215, 5}: "Apple, Inc.", + [3]byte{72, 215, 255}: "BLANKOM Antennentechnik GmbH", + [3]byte{72, 216, 85}: "Telvent", + [3]byte{72, 216, 254}: "ClarIDy Solutions, Inc.", + [3]byte{72, 218, 150}: "Eddy Smart Home Solutions Inc.", + [3]byte{72, 219, 80}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 220, 251}: "Nokia Corporation", + [3]byte{72, 223, 28}: "Wuhan NEC Fibre Optic Communications industry Co. Ltd", + [3]byte{72, 223, 55}: "Hewlett Packard Enterprise", + [3]byte{72, 225, 175}: "Vity", + [3]byte{72, 226, 68}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{72, 233, 241}: "Apple, Inc.", + [3]byte{72, 234, 99}: "Zhejiang Uniview Technologies Co., Ltd.", + [3]byte{72, 235, 48}: "ETERNA TECHNOLOGY, INC.", + [3]byte{72, 237, 128}: "daesung eltec", + [3]byte{72, 238, 7}: "Silver Palm Technologies LLC", + [3]byte{72, 238, 12}: "D-Link International", + [3]byte{72, 238, 134}: "UTStarcom (China) Co.,Ltd", + [3]byte{72, 240, 123}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{72, 242, 48}: "Ubizcore Co.,LTD", + [3]byte{72, 243, 23}: "Private", + [3]byte{72, 244, 125}: "TechVision Holding Internation Limited", + [3]byte{72, 247, 192}: "Technicolor CH USA Inc.", + [3]byte{72, 247, 241}: "Nokia", + [3]byte{72, 248, 179}: "Cisco-Linksys, LLC", + [3]byte{72, 248, 225}: "Nokia", + [3]byte{72, 249, 37}: "Maestronic", + [3]byte{72, 249, 124}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{72, 252, 182}: "LAVA INTERNATIONAL(H.K) LIMITED", + [3]byte{72, 252, 184}: "Woodstream Corporation", + [3]byte{72, 253, 142}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{72, 254, 234}: "HOMA B.V.", + [3]byte{76, 0, 130}: "Cisco Systems, Inc", + [3]byte{76, 2, 46}: "CMR KOREA CO., LTD", + [3]byte{76, 2, 137}: "LEX COMPUTECH CO., LTD", + [3]byte{76, 6, 138}: "Basler Electric Company", + [3]byte{76, 7, 201}: "COMPUTER OFFICE Co.,Ltd.", + [3]byte{76, 9, 180}: "zte corporation", + [3]byte{76, 9, 212}: "Arcadyan Technology Corporation", + [3]byte{76, 11, 58}: "TCT mobile ltd", + [3]byte{76, 11, 190}: "Microsoft", + [3]byte{76, 13, 238}: "JABIL CIRCUIT (SHANGHAI) LTD.", + [3]byte{76, 15, 110}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{76, 15, 199}: "Earda Electronics Co.,Ltd ", + [3]byte{76, 17, 191}: "Zhejiang Dahua Technology Co., Ltd.", + [3]byte{76, 20, 128}: "NOREGON SYSTEMS, INC", + [3]byte{76, 20, 163}: "TCL Technoly Electronics (Huizhou) Co., Ltd.", + [3]byte{76, 22, 148}: "shenzhen sibituo Technology Co., Ltd", + [3]byte{76, 22, 241}: "zte corporation", + [3]byte{76, 23, 235}: "Sagemcom Broadband SAS", + [3]byte{76, 26, 58}: "PRIMA Research And Production Enterprise Ltd.", + [3]byte{76, 26, 61}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{76, 26, 149}: "Novakon Co., Ltd.", + [3]byte{76, 31, 204}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{76, 33, 208}: "Sony Mobile Communications AB", + [3]byte{76, 34, 88}: "cozybit, Inc.", + [3]byte{76, 37, 120}: "Nokia Corporation", + [3]byte{76, 38, 231}: "Welgate Co., Ltd.", + [3]byte{76, 44, 128}: "Beijing Skyway Technologies Co.,Ltd ", + [3]byte{76, 44, 131}: "Zhejiang KaNong Network Technology Co.,Ltd.", + [3]byte{76, 47, 157}: "ICM Controls", + [3]byte{76, 48, 137}: "Thales Transportation Systems GmbH", + [3]byte{76, 50, 45}: "TELEDATA NETWORKS", + [3]byte{76, 50, 117}: "Apple, Inc.", + [3]byte{76, 50, 217}: "M Rutty Holdings Pty. Ltd.", + [3]byte{76, 51, 78}: "HIGHTECH", + [3]byte{76, 52, 136}: "Intel Corporate", + [3]byte{76, 56, 213}: "MITAC COMPUTING TECHNOLOGY CORPORATION", + [3]byte{76, 57, 9}: "HPL Electric & Power Private Limited", + [3]byte{76, 57, 16}: "Newtek Electronics co., Ltd.", + [3]byte{76, 59, 116}: "VOGTEC(H.K.) Co., Ltd", + [3]byte{76, 60, 22}: "Samsung Electronics Co.,Ltd", + [3]byte{76, 72, 218}: "Beijing Autelan Technology Co.,Ltd", + [3]byte{76, 75, 104}: "Mobile Device, Inc. ", + [3]byte{76, 78, 3}: "TCT mobile ltd", + [3]byte{76, 78, 53}: "Cisco Systems, Inc", + [3]byte{76, 84, 39}: "Linepro Sp. z o.o.", + [3]byte{76, 84, 153}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{76, 85, 133}: "Hamilton Systems", + [3]byte{76, 85, 184}: "Turkcell Teknoloji", + [3]byte{76, 85, 204}: "Zentri Pty Ltd", + [3]byte{76, 87, 202}: "Apple, Inc.", + [3]byte{76, 93, 205}: "Oy Finnish Electric Vehicle Technologies Ltd", + [3]byte{76, 94, 12}: "Routerboard.com", + [3]byte{76, 95, 210}: "Alcatel-Lucent", + [3]byte{76, 96, 213}: "airPointe of New Hampshire", + [3]byte{76, 96, 222}: "NETGEAR", + [3]byte{76, 98, 85}: "SANMINA-SCI SYSTEM DE MEXICO S.A. DE C.V.", + [3]byte{76, 99, 235}: "Application Solutions (Electronics and Vision) Ltd", + [3]byte{76, 100, 217}: "Guangdong Leawin Group Co., Ltd", + [3]byte{76, 102, 65}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{76, 110, 110}: "Comnect Technology CO.,LTD", + [3]byte{76, 114, 185}: "PEGATRON CORPORATION", + [3]byte{76, 115, 103}: "Genius Bytes Software Solutions GmbH", + [3]byte{76, 115, 165}: "KOVE", + [3]byte{76, 116, 3}: "BQ", + [3]byte{76, 116, 135}: "Leader Phone Communication Technology Co., Ltd.", + [3]byte{76, 116, 191}: "Apple, Inc.", + [3]byte{76, 118, 37}: "Dell Inc.", + [3]byte{76, 119, 79}: "Embedded Wireless Labs ", + [3]byte{76, 120, 114}: "Cav. Uff. Giacomo Cimberio S.p.A. ", + [3]byte{76, 120, 151}: "Arrowhead Alarm Products Ltd", + [3]byte{76, 121, 186}: "Intel Corporate", + [3]byte{76, 124, 95}: "Apple, Inc.", + [3]byte{76, 127, 98}: "Nokia Corporation", + [3]byte{76, 128, 79}: "Armstrong Monitoring Corp", + [3]byte{76, 128, 147}: "Intel Corporate", + [3]byte{76, 129, 32}: "Taicang T&W Electronics", + [3]byte{76, 130, 207}: "Echostar Technologies Corp", + [3]byte{76, 131, 222}: "Cisco SPVTG", + [3]byte{76, 139, 48}: "Actiontec Electronics, Inc", + [3]byte{76, 139, 85}: "Grupo Digicon", + [3]byte{76, 139, 239}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{76, 141, 121}: "Apple, Inc.", + [3]byte{76, 142, 204}: "SILKAN SA", + [3]byte{76, 143, 165}: "Jastec", + [3]byte{76, 150, 20}: "Juniper Networks", + [3]byte{76, 152, 239}: "Zeo", + [3]byte{76, 158, 128}: "KYOKKO ELECTRIC Co., Ltd.", + [3]byte{76, 158, 228}: "Hanyang Navicom Co.,Ltd.", + [3]byte{76, 158, 255}: "ZyXEL Communications Corporation", + [3]byte{76, 160, 3}: "T-21 Technologies LLC", + [3]byte{76, 161, 97}: "Rain Bird Corporation", + [3]byte{76, 165, 21}: "Baikal Electronics JSC", + [3]byte{76, 165, 109}: "Samsung Electronics Co.,Ltd", + [3]byte{76, 167, 75}: "Alcatel Lucent", + [3]byte{76, 169, 40}: "Insensi", + [3]byte{76, 170, 22}: "AzureWave Technologies (Shanghai) Inc.", + [3]byte{76, 171, 51}: "KST technology", + [3]byte{76, 172, 10}: "zte corporation", + [3]byte{76, 174, 49}: "ShengHai Electronics (Shenzhen) Ltd", + [3]byte{76, 176, 232}: "Beijing RongZhi xinghua technology co., LTD", + [3]byte{76, 177, 108}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{76, 177, 153}: "Apple, Inc.", + [3]byte{76, 178, 28}: "Maxphotonics Co.,Ltd", + [3]byte{76, 180, 74}: "NANOWAVE Technologies Inc.", + [3]byte{76, 180, 234}: "HRD (S) PTE., LTD.", + [3]byte{76, 183, 109}: "Novi Security", + [3]byte{76, 184, 28}: "SAM Electronics GmbH", + [3]byte{76, 184, 44}: "Cambridge Mobile Telematics, Inc.", + [3]byte{76, 184, 181}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{76, 185, 200}: "CONET CO., LTD.", + [3]byte{76, 186, 163}: "Bison Electronics Inc.", + [3]byte{76, 187, 88}: "Chicony Electronics Co., Ltd.", + [3]byte{76, 188, 66}: "Shenzhen Hangsheng Electronics Co.,Ltd.", + [3]byte{76, 188, 165}: "Samsung Electronics Co.,Ltd", + [3]byte{76, 196, 82}: "Shang Hai Tyd. Electon Technology Ltd.", + [3]byte{76, 198, 2}: "Radios, Inc.", + [3]byte{76, 198, 129}: "Shenzhen Aisat Electronic Co., Ltd.", + [3]byte{76, 201, 79}: "Nokia", + [3]byte{76, 202, 83}: "Skyera, Inc.", + [3]byte{76, 203, 245}: "zte corporation", + [3]byte{76, 204, 52}: "Motorola Solutions Inc.", + [3]byte{76, 204, 106}: "Micro-Star INTL CO., LTD.", + [3]byte{76, 208, 138}: "HUMAX Co., Ltd.", + [3]byte{76, 214, 55}: "Qsono Electronics Co., Ltd", + [3]byte{76, 215, 182}: "Helmer Scientific", + [3]byte{76, 217, 196}: "Magneti Marelli Automotive Electronics (Guangzhou) Co. Ltd", + [3]byte{76, 223, 61}: "TEAM ENGINEERS ADVANCE TECHNOLOGIES INDIA PVT LTD", + [3]byte{76, 225, 115}: "IEEE Registration Authority", + [3]byte{76, 225, 187}: "Zhuhai HiFocus Technology Co., Ltd.", + [3]byte{76, 226, 241}: "sclak srl", + [3]byte{76, 230, 118}: "BUFFALO.INC", + [3]byte{76, 233, 51}: "RailComm, LLC", + [3]byte{76, 235, 66}: "Intel Corporate", + [3]byte{76, 236, 239}: "Soraa, Inc.", + [3]byte{76, 237, 222}: "ASKEY COMPUTER CORP", + [3]byte{76, 238, 176}: "SHC Netzwerktechnik GmbH", + [3]byte{76, 240, 46}: "Vifa Denmark A/S", + [3]byte{76, 242, 191}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{76, 244, 91}: "Blue Clover Devices", + [3]byte{76, 245, 160}: "Scalable Network Technologies Inc", + [3]byte{76, 247, 55}: "SamJi Electronics Co., Ltd", + [3]byte{76, 249, 93}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{76, 250, 202}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{76, 251, 69}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{76, 255, 18}: "Fuze Entertainment Co., ltd", + [3]byte{80, 0, 140}: "Hong Kong Telecommunications (HKT) Limited", + [3]byte{80, 1, 107}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{80, 1, 187}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 1, 217}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{80, 4, 184}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{80, 5, 61}: "CyWee Group Ltd", + [3]byte{80, 6, 4}: "Cisco Systems, Inc", + [3]byte{80, 6, 171}: "Cisco Systems, Inc", + [3]byte{80, 9, 89}: "Technicolor CH USA Inc.", + [3]byte{80, 11, 50}: "Foxda Technology Industrial(ShenZhen)Co.,LTD", + [3]byte{80, 11, 145}: "IEEE Registration Authority", + [3]byte{80, 14, 109}: "TrafficCast International", + [3]byte{80, 15, 245}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{80, 17, 235}: "SilverNet Ltd", + [3]byte{80, 20, 181}: "Richfit Information Technology Co., Ltd", + [3]byte{80, 23, 255}: "Cisco Systems, Inc", + [3]byte{80, 26, 165}: "GN Netcom A/S", + [3]byte{80, 26, 197}: "Microsoft", + [3]byte{80, 28, 191}: "Cisco Systems, Inc", + [3]byte{80, 30, 45}: "StreamUnlimited Engineering GmbH", + [3]byte{80, 32, 107}: "Emerson Climate Technologies Transportation Solutions", + [3]byte{80, 34, 103}: "PixeLINK", + [3]byte{80, 37, 43}: "Nethra Imaging Incorporated", + [3]byte{80, 38, 144}: "FUJITSU LIMITED", + [3]byte{80, 39, 199}: "TECHNART Co.,Ltd", + [3]byte{80, 41, 77}: "NANJING IOT SENSOR TECHNOLOGY CO,LTD", + [3]byte{80, 42, 126}: "Smart electronic GmbH", + [3]byte{80, 42, 139}: "Telekom Research and Development Sdn Bhd", + [3]byte{80, 43, 115}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{80, 45, 29}: "Nokia Corporation", + [3]byte{80, 45, 162}: "Intel Corporate", + [3]byte{80, 45, 244}: "Phytec Messtechnik GmbH", + [3]byte{80, 46, 92}: "HTC Corporation", + [3]byte{80, 46, 206}: "Asahi Electronics Co.,Ltd", + [3]byte{80, 49, 173}: "ABB Global Industries and Services Private Limited", + [3]byte{80, 50, 55}: "Apple, Inc.", + [3]byte{80, 50, 117}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 57, 85}: "Cisco SPVTG", + [3]byte{80, 58, 125}: "AlphaTech PLC Int’l Co., Ltd.", + [3]byte{80, 58, 160}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{80, 60, 196}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{80, 61, 161}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 61, 229}: "Cisco Systems, Inc", + [3]byte{80, 63, 86}: "Syncmold Enterprise Corp", + [3]byte{80, 63, 152}: "CMITECH", + [3]byte{80, 64, 97}: "Nokia", + [3]byte{80, 69, 247}: "Liuhe Intelligence Technology Ltd.", + [3]byte{80, 70, 93}: "ASUSTek COMPUTER INC.", + [3]byte{80, 72, 235}: "BEIJING HAIHEJINSHENG NETWORK TECHNOLOGY CO. LTD.", + [3]byte{80, 74, 94}: "Masimo Corporation", + [3]byte{80, 74, 110}: "NETGEAR", + [3]byte{80, 75, 91}: "CONTROLtronic GmbH", + [3]byte{80, 79, 148}: "Loxone Electronics GmbH", + [3]byte{80, 80, 42}: "Egardia", + [3]byte{80, 80, 101}: "TAKT Corporation", + [3]byte{80, 82, 210}: "Hangzhou Telin Technologies Co., Limited", + [3]byte{80, 85, 39}: "LG Electronics (Mobile Communications)", + [3]byte{80, 86, 99}: "Texas Instruments", + [3]byte{80, 86, 168}: "Jolla Ltd", + [3]byte{80, 86, 191}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 87, 168}: "Cisco Systems, Inc", + [3]byte{80, 88, 0}: "WyTec International, Inc.", + [3]byte{80, 88, 79}: "waytotec,Inc.", + [3]byte{80, 90, 198}: "GUANGDONG SUPER TELECOM CO.,LTD.", + [3]byte{80, 96, 40}: "Xirrus Inc.", + [3]byte{80, 97, 132}: "Avaya Inc", + [3]byte{80, 97, 214}: "Indu-Sol GmbH", + [3]byte{80, 99, 19}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{80, 100, 65}: "Greenlee", + [3]byte{80, 101, 131}: "Texas Instruments", + [3]byte{80, 101, 243}: "Hewlett Packard", + [3]byte{80, 103, 135}: "Planet Networks", + [3]byte{80, 103, 174}: "Cisco Systems, Inc", + [3]byte{80, 103, 240}: "ZyXEL Communications Corporation", + [3]byte{80, 104, 10}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{80, 106, 3}: "NETGEAR", + [3]byte{80, 107, 141}: "Nutanix", + [3]byte{80, 111, 154}: "Wi-Fi Alliance", + [3]byte{80, 112, 229}: "He Shan World Fair Electronics Technology Limited", + [3]byte{80, 114, 36}: "Texas Instruments", + [3]byte{80, 114, 77}: "BEG Brueck Electronic GmbH", + [3]byte{80, 118, 145}: "Tekpea, Inc.", + [3]byte{80, 118, 166}: "Ecil Informatica Ind. Com. Ltda", + [3]byte{80, 121, 91}: "Interexport Telecomunicaciones S.A.", + [3]byte{80, 122, 85}: "Apple, Inc.", + [3]byte{80, 123, 157}: "LCFC(HeFei) Electronics Technology co., ltd", + [3]byte{80, 125, 2}: "BIODIT", + [3]byte{80, 126, 93}: "Arcadyan Technology Corporation", + [3]byte{80, 130, 213}: "Apple, Inc.", + [3]byte{80, 133, 105}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 135, 137}: "Cisco Systems, Inc", + [3]byte{80, 135, 184}: "Nuvyyo Inc", + [3]byte{80, 137, 101}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{80, 138, 15}: "SHENZHEN FISE TECHNOLOGY HOLDING CO.,LTD.", + [3]byte{80, 138, 66}: "Uptmate Technology Co., LTD", + [3]byte{80, 138, 203}: "SHENZHEN MAXMADE TECHNOLOGY CO., LTD.", + [3]byte{80, 140, 119}: "DIRMEIER Schanktechnik GmbH &Co KG", + [3]byte{80, 140, 177}: "Texas Instruments", + [3]byte{80, 141, 111}: "CHAHOO Limited", + [3]byte{80, 147, 79}: "Gradual Tecnologia Ltda.", + [3]byte{80, 151, 114}: "Westinghouse Digital", + [3]byte{80, 152, 113}: "Inventum Technologies Private Limited", + [3]byte{80, 152, 243}: "Rheem Australia Pty Ltd", + [3]byte{80, 158, 167}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 159, 39}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{80, 159, 59}: "OI ELECTRIC CO.,LTD", + [3]byte{80, 160, 84}: "Actineon", + [3]byte{80, 160, 191}: "Alba Fiber Systems Inc.", + [3]byte{80, 164, 200}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 164, 208}: "IEEE Registration Authority", + [3]byte{80, 166, 227}: "David Clark Company", + [3]byte{80, 167, 21}: "Aboundi, Inc.", + [3]byte{80, 167, 43}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{80, 167, 51}: "Ruckus Wireless", + [3]byte{80, 169, 222}: "Smartcom - Bulgaria AD", + [3]byte{80, 171, 62}: "Qibixx AG", + [3]byte{80, 171, 191}: "Hoseo Telecom", + [3]byte{80, 173, 213}: "Dynalec Corporation", + [3]byte{80, 175, 115}: "Shenzhen Bitland Information Technology Co., Ltd.", + [3]byte{80, 179, 99}: "Digitron da Amazonia S/A", + [3]byte{80, 182, 149}: "Micropoint Biotechnologies,Inc.", + [3]byte{80, 183, 195}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 184, 136}: "wi2be Tecnologia S/A", + [3]byte{80, 184, 162}: "ImTech Technologies LLC,", + [3]byte{80, 189, 95}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{80, 192, 6}: "Carmanah Signs", + [3]byte{80, 194, 113}: "SECURETECH INC", + [3]byte{80, 197, 141}: "Juniper Networks", + [3]byte{80, 199, 191}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{80, 200, 229}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 201, 113}: "GN Netcom A/S", + [3]byte{80, 201, 160}: "SKIPPER Electronics AS", + [3]byte{80, 204, 248}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{80, 205, 34}: "Avaya Inc", + [3]byte{80, 205, 50}: "NanJing Chaoran Science & Technology Co.,Ltd.", + [3]byte{80, 206, 117}: "Measy Electronics Co., Ltd.", + [3]byte{80, 210, 19}: "CviLux Corporation", + [3]byte{80, 210, 116}: "Steffes Corporation", + [3]byte{80, 211, 127}: "Yu Fly Mikly Way Science and Technology Co., Ltd.", + [3]byte{80, 213, 156}: "Thai Habel Industrial Co., Ltd.", + [3]byte{80, 214, 215}: "Takahata Precision", + [3]byte{80, 215, 83}: "CONELCOM GmbH", + [3]byte{80, 218, 0}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{80, 221, 79}: "Automation Components, Inc", + [3]byte{80, 223, 149}: "Lytx", + [3]byte{80, 224, 199}: "TurControlSystme AG", + [3]byte{80, 225, 74}: "Private", + [3]byte{80, 229, 73}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{80, 230, 102}: "Shenzhen Techtion Electronics Co., Ltd.", + [3]byte{80, 234, 214}: "Apple, Inc.", + [3]byte{80, 235, 26}: "Brocade Communications Systems, Inc.", + [3]byte{80, 237, 120}: "Changzhou Yongse Infotech Co.,Ltd", + [3]byte{80, 237, 148}: "EGATEL SL", + [3]byte{80, 240, 3}: "Open Stack, Inc.", + [3]byte{80, 240, 211}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 241, 74}: "Texas Instruments", + [3]byte{80, 244, 60}: "Leeo Inc", + [3]byte{80, 245, 32}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 245, 218}: "Amazon Technologies Inc.", + [3]byte{80, 246, 26}: "Kunshan JADE Technologies co., Ltd.", + [3]byte{80, 250, 132}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{80, 250, 171}: "L-tek d.o.o.", + [3]byte{80, 252, 48}: "Treehouse Labs", + [3]byte{80, 252, 159}: "Samsung Electronics Co.,Ltd", + [3]byte{80, 254, 242}: "Sify Technologies Ltd", + [3]byte{80, 255, 153}: "IEEE Registration Authority", + [3]byte{84, 3, 132}: "Hangkong Nano IC Technologies Co., Ltd", + [3]byte{84, 3, 245}: "EBN Technology Corp.", + [3]byte{84, 4, 150}: "Gigawave LTD", + [3]byte{84, 4, 166}: "ASUSTek COMPUTER INC.", + [3]byte{84, 5, 54}: "Vivago Oy", + [3]byte{84, 5, 95}: "Alcatel Lucent", + [3]byte{84, 5, 147}: "WOORI ELEC Co.,Ltd", + [3]byte{84, 9, 85}: "zte corporation", + [3]byte{84, 9, 141}: "deister electronic GmbH", + [3]byte{84, 16, 236}: "Microchip Technology Inc.", + [3]byte{84, 17, 47}: "Sulzer Pump Solutions Finland Oy", + [3]byte{84, 17, 95}: "Atamo Pty Ltd", + [3]byte{84, 19, 121}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{84, 20, 115}: "Wingtech Group (HongKong)Limited", + [3]byte{84, 20, 253}: "Orbbec 3D Technology International", + [3]byte{84, 25, 200}: "vivo Mobile Communication Co., Ltd.", + [3]byte{84, 27, 93}: "Techno-Innov", + [3]byte{84, 29, 251}: "Freestyle Energy Ltd", + [3]byte{84, 30, 86}: "Juniper Networks", + [3]byte{84, 31, 213}: "Advantage Electronics", + [3]byte{84, 32, 24}: "Tely Labs", + [3]byte{84, 33, 96}: "Resolution Products", + [3]byte{84, 34, 248}: "zte corporation", + [3]byte{84, 37, 234}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{84, 38, 150}: "Apple, Inc.", + [3]byte{84, 39, 30}: "AzureWave Technology Inc.", + [3]byte{84, 39, 88}: "Motorola (Wuhan) Mobility Technologies Communication Co., Ltd.", + [3]byte{84, 39, 108}: "Jiangsu Houge Technology Corp.", + [3]byte{84, 42, 156}: "LSY Defense, LLC.", + [3]byte{84, 42, 162}: "Alpha Networks Inc.", + [3]byte{84, 43, 87}: "Night Owl SP", + [3]byte{84, 44, 234}: "PROTECTRON", + [3]byte{84, 47, 137}: "Euclid Laboratories, Inc.", + [3]byte{84, 47, 138}: "TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO ", + [3]byte{84, 49, 49}: "Raster Vision Ltd", + [3]byte{84, 53, 48}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{84, 53, 223}: "Symeo GmbH", + [3]byte{84, 54, 155}: "1Verge Internet Technology (Beijing) Co., Ltd.", + [3]byte{84, 57, 104}: "Edgewater Networks Inc", + [3]byte{84, 57, 223}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{84, 61, 55}: "Ruckus Wireless", + [3]byte{84, 64, 173}: "Samsung Electronics Co.,Ltd", + [3]byte{84, 66, 73}: "Sony Corporation", + [3]byte{84, 68, 8}: "Nokia Corporation", + [3]byte{84, 70, 107}: "Shenzhen CZTIC Electronic Technology Co., Ltd ", + [3]byte{84, 72, 156}: "CDOUBLES ELECTRONICS CO. LTD.", + [3]byte{84, 74, 0}: "Cisco Systems, Inc", + [3]byte{84, 74, 5}: "wenglor sensoric gmbh", + [3]byte{84, 74, 22}: "Texas Instruments", + [3]byte{84, 75, 140}: "Juniper Networks", + [3]byte{84, 78, 69}: "Private", + [3]byte{84, 78, 144}: "Apple, Inc.", + [3]byte{84, 81, 27}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{84, 81, 70}: "AMG Systems Ltd.", + [3]byte{84, 83, 237}: "Sony Corporation", + [3]byte{84, 84, 20}: "Digital RF Corea, Inc", + [3]byte{84, 84, 207}: "PROBEDIGITAL CO.,LTD", + [3]byte{84, 90, 166}: "Espressif Inc.", + [3]byte{84, 94, 189}: "NL Technologies", + [3]byte{84, 95, 169}: "Teracom Limited", + [3]byte{84, 96, 9}: "Google, Inc.", + [3]byte{84, 97, 114}: "ZODIAC AEROSPACE SAS", + [3]byte{84, 97, 234}: "Zaplox AB", + [3]byte{84, 100, 217}: "Sagemcom Broadband SAS", + [3]byte{84, 101, 222}: "ARRIS Group, Inc.", + [3]byte{84, 103, 81}: "Compal Broadband Networks, Inc.", + [3]byte{84, 108, 14}: "Texas Instruments", + [3]byte{84, 109, 82}: "TOPVIEW OPTRONICS CORP.", + [3]byte{84, 114, 79}: "Apple, Inc.", + [3]byte{84, 115, 152}: "Toyo Electronics Corporation", + [3]byte{84, 116, 230}: "Webtech Wireless", + [3]byte{84, 117, 208}: "Cisco Systems, Inc", + [3]byte{84, 120, 26}: "Cisco Systems, Inc", + [3]byte{84, 121, 117}: "Nokia Corporation", + [3]byte{84, 124, 105}: "Cisco Systems, Inc", + [3]byte{84, 127, 84}: "INGENICO", + [3]byte{84, 127, 168}: "TELCO systems, s.r.o.", + [3]byte{84, 127, 238}: "Cisco Systems, Inc", + [3]byte{84, 129, 173}: "Eagle Research Corporation", + [3]byte{84, 132, 123}: "Digital Devices GmbH", + [3]byte{84, 136, 14}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{84, 137, 34}: "Zelfy Inc", + [3]byte{84, 137, 152}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{84, 140, 160}: "Liteon Technology Corporation", + [3]byte{84, 146, 190}: "Samsung Electronics Co.,Ltd", + [3]byte{84, 147, 89}: "SHENZHEN TWOWING TECHNOLOGIES CO.,LTD.", + [3]byte{84, 148, 120}: "Silvershore Technology Partners", + [3]byte{84, 154, 17}: "IEEE Registration Authority", + [3]byte{84, 154, 22}: "Uzushio Electric Co.,Ltd.", + [3]byte{84, 155, 18}: "Samsung Electronics Co.,Ltd", + [3]byte{84, 157, 133}: "EnerAccess inc", + [3]byte{84, 159, 19}: "Apple, Inc.", + [3]byte{84, 159, 53}: "Dell Inc.", + [3]byte{84, 160, 79}: "t-mac Technologies Ltd", + [3]byte{84, 160, 80}: "ASUSTek COMPUTER INC.", + [3]byte{84, 162, 116}: "Cisco Systems, Inc", + [3]byte{84, 163, 27}: "Shenzhen Linkworld Technology Co,.LTD", + [3]byte{84, 163, 250}: "BQT Solutions (Australia)Pty Ltd", + [3]byte{84, 165, 27}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{84, 165, 75}: "NSC Communications Siberia Ltd", + [3]byte{84, 166, 25}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{84, 169, 212}: "Minibar Systems", + [3]byte{84, 171, 58}: "QUANTA COMPUTER INC.", + [3]byte{84, 174, 39}: "Apple, Inc.", + [3]byte{84, 181, 108}: "Xi'an NovaStar Tech Co., Ltd", + [3]byte{84, 182, 32}: "SUHDOL E&C Co.Ltd.", + [3]byte{84, 183, 83}: "Hunan Fenghui Yinjia Science And Technology Co.,Ltd", + [3]byte{84, 184, 10}: "D-Link International", + [3]byte{84, 190, 83}: "zte corporation", + [3]byte{84, 190, 247}: "PEGATRON CORPORATION", + [3]byte{84, 196, 21}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{84, 200, 15}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{84, 205, 16}: "Panasonic Mobile Communications Co.,Ltd.", + [3]byte{84, 205, 167}: "Fujian Shenzhou Electronic Co.,Ltd", + [3]byte{84, 205, 238}: "ShenZhen Apexis Electronic Co.,Ltd", + [3]byte{84, 208, 180}: "Xiamen Four-Faith Communication Technology Co.,Ltd", + [3]byte{84, 208, 237}: "AXIM Communications", + [3]byte{84, 209, 99}: "MAX-TECH,INC", + [3]byte{84, 209, 176}: "Universal Laser Systems, Inc", + [3]byte{84, 210, 114}: "Nuki Home Solutions GmbH", + [3]byte{84, 212, 111}: "Cisco SPVTG", + [3]byte{84, 217, 228}: "BRILLIANTTS CO., LTD", + [3]byte{84, 220, 29}: "Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd", + [3]byte{84, 223, 0}: "Ulterius Technologies, LLC", + [3]byte{84, 223, 99}: "Intrakey technologies GmbH", + [3]byte{84, 224, 50}: "Juniper Networks", + [3]byte{84, 224, 97}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{84, 225, 64}: "INGENICO", + [3]byte{84, 225, 173}: "LCFC(HeFei) Electronics Technology co., ltd", + [3]byte{84, 226, 200}: "Dongguan Aoyuan Electronics Technology Co., Ltd", + [3]byte{84, 226, 224}: "ARRIS Group, Inc.", + [3]byte{84, 227, 176}: "JVL Industri Elektronik", + [3]byte{84, 227, 246}: "Alcatel-Lucent", + [3]byte{84, 228, 58}: "Apple, Inc.", + [3]byte{84, 228, 189}: "FN-LINK TECHNOLOGY LIMITED", + [3]byte{84, 230, 63}: "ShenZhen LingKeWeiEr Technology Co., Ltd.", + [3]byte{84, 230, 252}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{84, 234, 168}: "Apple, Inc.", + [3]byte{84, 237, 163}: "Navdy, Inc.", + [3]byte{84, 238, 117}: "Wistron InfoComm(Kunshan)Co.,Ltd.", + [3]byte{84, 239, 146}: "Shenzhen Elink Technology Co., LTD", + [3]byte{84, 239, 254}: "Fullpower Technologies, Inc.", + [3]byte{84, 242, 1}: "Samsung Electronics Co.,Ltd", + [3]byte{84, 245, 182}: "ORIENTAL PACIFIC INTERNATIONAL LIMITED", + [3]byte{84, 246, 102}: "Berthold Technologies GmbH and Co.KG", + [3]byte{84, 246, 197}: "FUJIAN STAR-NET COMMUNICATION CO.,LTD", + [3]byte{84, 248, 118}: "ABB AG", + [3]byte{84, 250, 62}: "Samsung Electronics Co.,Ltd", + [3]byte{84, 250, 150}: "Nokia", + [3]byte{84, 251, 88}: "WISEWARE, Lda", + [3]byte{84, 253, 191}: "Scheidt & Bachmann GmbH", + [3]byte{84, 255, 130}: "Davit Solution co.", + [3]byte{84, 255, 207}: "Mopria Alliance", + [3]byte{88, 0, 227}: "Liteon Technology Corporation", + [3]byte{88, 4, 203}: "Tianjin Huisun Technology Co.,Ltd.", + [3]byte{88, 5, 40}: "LABRIS NETWORKS", + [3]byte{88, 5, 86}: "Elettronica GF S.r.L.", + [3]byte{88, 8, 250}: "Fiber Optic & telecommunication INC.", + [3]byte{88, 9, 67}: "Private", + [3]byte{88, 9, 229}: "Kivic Inc.", + [3]byte{88, 10, 32}: "Cisco Systems, Inc", + [3]byte{88, 16, 140}: "Intelbras", + [3]byte{88, 18, 67}: "AcSiP Technology Corp.", + [3]byte{88, 22, 38}: "Avaya Inc", + [3]byte{88, 23, 12}: "Sony Mobile Communications AB", + [3]byte{88, 28, 189}: "Affinegy", + [3]byte{88, 29, 145}: "Advanced Mobile Telecom co.,ltd.", + [3]byte{88, 31, 40}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{88, 31, 103}: "Open-m technology limited", + [3]byte{88, 31, 170}: "Apple, Inc.", + [3]byte{88, 31, 239}: "Tuttnaer LTD", + [3]byte{88, 32, 177}: "Hewlett Packard", + [3]byte{88, 33, 54}: "KMB systems, s.r.o.", + [3]byte{88, 35, 140}: "Technicolor CH USA Inc.", + [3]byte{88, 42, 247}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{88, 43, 219}: "Pax AB", + [3]byte{88, 46, 254}: "Lighting Science Group", + [3]byte{88, 47, 66}: "Universal Electric Corporation", + [3]byte{88, 49, 18}: "DRUST", + [3]byte{88, 50, 119}: "Reliance Communications LLC", + [3]byte{88, 52, 59}: "Glovast Technology Ltd.", + [3]byte{88, 53, 217}: "Cisco Systems, Inc", + [3]byte{88, 60, 198}: "Omneality Ltd.", + [3]byte{88, 63, 84}: "LG Electronics (Mobile Communications)", + [3]byte{88, 64, 78}: "Apple, Inc.", + [3]byte{88, 66, 228}: "Baxter International Inc", + [3]byte{88, 68, 152}: "Xiaomi Communications Co Ltd", + [3]byte{88, 70, 143}: "Koncar Electronics and Informatics", + [3]byte{88, 70, 225}: "Baxter International Inc", + [3]byte{88, 71, 4}: "Shenzhen Webridge Technology Co.,Ltd", + [3]byte{88, 72, 34}: "Sony Mobile Communications AB", + [3]byte{88, 72, 192}: "COFLEC", + [3]byte{88, 73, 37}: "E3 Enterprise", + [3]byte{88, 73, 59}: "Palo Alto Networks", + [3]byte{88, 73, 186}: "Chitai Electronic Corp.", + [3]byte{88, 76, 25}: "Chongqing Guohong Technology Development Company Limited", + [3]byte{88, 76, 238}: "Digital One Technologies, Limited", + [3]byte{88, 80, 118}: "Linear Equipamentos Eletronicos SA", + [3]byte{88, 80, 171}: "TLS Corporation", + [3]byte{88, 80, 230}: "Best Buy Corporation", + [3]byte{88, 82, 138}: "Mitsubishi Electric Corporation", + [3]byte{88, 83, 192}: "Beijing Guang Runtong Technology Development Company co.,Ltd", + [3]byte{88, 85, 202}: "Apple, Inc.", + [3]byte{88, 86, 232}: "ARRIS Group, Inc.", + [3]byte{88, 87, 13}: "Danfoss Solar Inverters", + [3]byte{88, 96, 95}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{88, 99, 86}: "FN-LINK TECHNOLOGY LIMITED", + [3]byte{88, 99, 154}: "TPL SYSTEMES", + [3]byte{88, 101, 230}: "INFOMARK CO., LTD.", + [3]byte{88, 102, 186}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{88, 103, 26}: "Barnes&Noble", + [3]byte{88, 103, 127}: "Clare Controls Inc.", + [3]byte{88, 104, 93}: "Tempo Australia Pty Ltd", + [3]byte{88, 105, 108}: "Ruijie Networks Co.,LTD", + [3]byte{88, 105, 249}: "Fusion Transactive Ltd.", + [3]byte{88, 106, 177}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{88, 109, 143}: "Cisco-Linksys, LLC", + [3]byte{88, 110, 214}: "Private", + [3]byte{88, 112, 198}: "Shanghai Xiaoyi Technology Co., Ltd.", + [3]byte{88, 117, 33}: "CJSC RTSoft", + [3]byte{88, 118, 117}: "Beijing ECHO Technologies Co.,Ltd", + [3]byte{88, 118, 197}: "DIGI I'S LTD", + [3]byte{88, 122, 77}: "Stonesoft Corporation", + [3]byte{88, 123, 233}: "AirPro Technology India Pvt. Ltd", + [3]byte{88, 126, 97}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{88, 127, 87}: "Apple, Inc.", + [3]byte{88, 127, 102}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{88, 127, 183}: "SONAR INDUSTRIAL CO., LTD.", + [3]byte{88, 127, 200}: "S2M", + [3]byte{88, 130, 29}: "H. Schomäcker GmbH", + [3]byte{88, 130, 168}: "Microsoft", + [3]byte{88, 132, 228}: "IP500 Alliance e.V.", + [3]byte{88, 133, 110}: "QSC AG", + [3]byte{88, 135, 76}: "LITE-ON CLEAN ENERGY TECHNOLOGY CORP.", + [3]byte{88, 135, 226}: "Shenzhen Coship Electronics Co., Ltd.", + [3]byte{88, 139, 243}: "ZyXEL Communications Corporation", + [3]byte{88, 141, 9}: "Cisco Systems, Inc", + [3]byte{88, 145, 207}: "Intel Corporate", + [3]byte{88, 146, 13}: "Kinetic Avionics Limited", + [3]byte{88, 147, 150}: "Ruckus Wireless", + [3]byte{88, 148, 107}: "Intel Corporate", + [3]byte{88, 148, 207}: "Vertex Standard LMR, Inc.", + [3]byte{88, 151, 30}: "Cisco Systems, Inc", + [3]byte{88, 151, 189}: "Cisco Systems, Inc", + [3]byte{88, 152, 53}: "Technicolor", + [3]byte{88, 152, 111}: "Revolution Display", + [3]byte{88, 155, 11}: "Shineway Technologies, Inc.", + [3]byte{88, 156, 252}: "FreeBSD Foundation", + [3]byte{88, 162, 181}: "LG Electronics (Mobile Communications)", + [3]byte{88, 167, 111}: "iD corporation", + [3]byte{88, 168, 57}: "Intel Corporate", + [3]byte{88, 172, 120}: "Cisco Systems, Inc", + [3]byte{88, 176, 53}: "Apple, Inc.", + [3]byte{88, 176, 212}: "ZuniData Systems Inc.", + [3]byte{88, 182, 51}: "Ruckus Wireless", + [3]byte{88, 185, 97}: "SOLEM Electronique", + [3]byte{88, 185, 225}: "Crystalfontz America, Inc.", + [3]byte{88, 188, 39}: "Cisco Systems, Inc", + [3]byte{88, 188, 143}: "Cognitive Systems Corp.", + [3]byte{88, 189, 163}: "Nintendo Co., Ltd.", + [3]byte{88, 189, 249}: "Sigrand", + [3]byte{88, 191, 234}: "Cisco Systems, Inc", + [3]byte{88, 194, 50}: "NEC Corporation", + [3]byte{88, 195, 139}: "Samsung Electronics Co.,Ltd", + [3]byte{88, 207, 75}: "Lufkin Industries", + [3]byte{88, 208, 113}: "BW Broadcast", + [3]byte{88, 208, 143}: "IEEE 1904.1 Working Group", + [3]byte{88, 214, 122}: "TCPlink", + [3]byte{88, 214, 211}: "Dairy Cheq Inc", + [3]byte{88, 217, 213}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{88, 219, 141}: "Fast Co., Ltd.", + [3]byte{88, 220, 109}: "Exceptional Innovation, Inc.", + [3]byte{88, 224, 44}: "Micro Technic A/S", + [3]byte{88, 225, 108}: "Ying Hua Information Technology (Shanghai)Co., LTD", + [3]byte{88, 227, 38}: "Compass Technologies Inc.", + [3]byte{88, 228, 118}: "CENTRON COMMUNICATIONS TECHNOLOGIES FUJIAN CO.,LTD", + [3]byte{88, 230, 54}: "EVRsafe Technologies", + [3]byte{88, 231, 71}: "Deltanet AG", + [3]byte{88, 232, 8}: "AUTONICS CORPORATION", + [3]byte{88, 232, 118}: "IEEE Registration Authority", + [3]byte{88, 235, 20}: "Proteus Digital Health", + [3]byte{88, 236, 225}: "Newport Corporation", + [3]byte{88, 238, 206}: "Icon Time Systems", + [3]byte{88, 239, 104}: "Belkin International Inc.", + [3]byte{88, 241, 2}: "BLU Products Inc.", + [3]byte{88, 243, 135}: "HCCP", + [3]byte{88, 243, 156}: "Cisco Systems, Inc", + [3]byte{88, 244, 150}: "Source Chain", + [3]byte{88, 246, 123}: "Xia Men UnionCore Technology LTD.", + [3]byte{88, 246, 191}: "Kyoto University", + [3]byte{88, 249, 142}: "SECUDOS GmbH", + [3]byte{88, 251, 132}: "Intel Corporate", + [3]byte{88, 252, 115}: "Arria Live Media, Inc.", + [3]byte{88, 252, 219}: "IEEE Registration Authority", + [3]byte{88, 253, 32}: "Bravida Sakerhet AB", + [3]byte{92, 2, 106}: "Applied Vision Corporation", + [3]byte{92, 7, 111}: "Thought Creator", + [3]byte{92, 10, 91}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{92, 12, 187}: "CELIZION Inc.", + [3]byte{92, 14, 139}: "Extreme Networks", + [3]byte{92, 17, 147}: "Seal One AG", + [3]byte{92, 20, 55}: "Thyssenkrupp Aufzugswerke GmbH", + [3]byte{92, 21, 21}: "ADVAN", + [3]byte{92, 21, 225}: "AIDC TECHNOLOGY (S) PTE LTD", + [3]byte{92, 22, 199}: "Big Switch Networks", + [3]byte{92, 23, 55}: "I-View Now, LLC.", + [3]byte{92, 23, 211}: "LGE ", + [3]byte{92, 24, 181}: "Talon Communications", + [3]byte{92, 32, 208}: "Asoni Communication Co., Ltd.", + [3]byte{92, 34, 196}: "DAE EUN ELETRONICS CO., LTD", + [3]byte{92, 36, 67}: "O-Sung Telecom Co., Ltd.", + [3]byte{92, 36, 121}: "Baltech AG", + [3]byte{92, 37, 76}: "Avire Global Pte Ltd", + [3]byte{92, 38, 10}: "Dell Inc.", + [3]byte{92, 42, 239}: "Open Access Pty Ltd", + [3]byte{92, 43, 245}: "Vivint", + [3]byte{92, 46, 89}: "Samsung Electronics Co.,Ltd", + [3]byte{92, 46, 210}: "ABC(XiSheng) Electronics Co.,Ltd", + [3]byte{92, 49, 62}: "Texas Instruments", + [3]byte{92, 51, 39}: "Spazio Italia srl", + [3]byte{92, 51, 92}: "Swissphone Telecom AG", + [3]byte{92, 51, 142}: "Alpha Networks Inc.", + [3]byte{92, 53, 59}: "Compal Broadband Networks, Inc.", + [3]byte{92, 53, 218}: "There Corporation Oy", + [3]byte{92, 54, 184}: "TCL King Electrical Appliances (Huizhou) Co., Ltd", + [3]byte{92, 56, 224}: "Shanghai Super Electronics Technology Co.,LTD", + [3]byte{92, 59, 53}: "Gehirn Inc.", + [3]byte{92, 60, 39}: "Samsung Electronics Co.,Ltd", + [3]byte{92, 64, 88}: "Jefferson Audio Video Systems, Inc.", + [3]byte{92, 65, 231}: "Wiatec International Ltd.", + [3]byte{92, 67, 210}: "HAZEMEYER", + [3]byte{92, 69, 39}: "Juniper Networks", + [3]byte{92, 73, 121}: "AVM Audiovisuelles Marketing und Computersysteme GmbH", + [3]byte{92, 73, 125}: "Samsung Electronics Co.,Ltd", + [3]byte{92, 74, 31}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{92, 74, 38}: "Enguity Technology Corp", + [3]byte{92, 76, 169}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{92, 80, 21}: "Cisco Systems, Inc", + [3]byte{92, 81, 79}: "Intel Corporate", + [3]byte{92, 81, 136}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{92, 86, 237}: "3pleplay Electronics Private Limited", + [3]byte{92, 87, 26}: "ARRIS Group, Inc.", + [3]byte{92, 87, 200}: "Nokia Corporation", + [3]byte{92, 89, 72}: "Apple, Inc.", + [3]byte{92, 91, 53}: "Mist Systems, Inc.", + [3]byte{92, 91, 194}: "YIK Corporation", + [3]byte{92, 94, 171}: "Juniper Networks", + [3]byte{92, 99, 191}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{92, 105, 132}: "NUVICO", + [3]byte{92, 106, 125}: "KENTKART EGE ELEKTRONIK SAN. VE TIC. LTD. STI. ", + [3]byte{92, 106, 128}: "ZyXEL Communications Corporation", + [3]byte{92, 107, 50}: "Texas Instruments", + [3]byte{92, 107, 79}: "Hello Inc.", + [3]byte{92, 109, 32}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{92, 111, 79}: "S.A. SISTEL", + [3]byte{92, 112, 163}: "LG Electronics (Mobile Communications)", + [3]byte{92, 119, 87}: "Haivision Network Video", + [3]byte{92, 125, 94}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{92, 131, 143}: "Cisco Systems, Inc", + [3]byte{92, 132, 134}: "Brightsource Industries Israel LTD", + [3]byte{92, 134, 19}: "Beijing Zhoenet Technology Co., Ltd", + [3]byte{92, 134, 74}: "Secret Labs LLC", + [3]byte{92, 135, 120}: "Cybertelbridge co.,ltd", + [3]byte{92, 137, 154}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{92, 137, 212}: "Beijing Banner Electric Co.,Ltd", + [3]byte{92, 138, 56}: "Hewlett Packard", + [3]byte{92, 141, 78}: "Apple, Inc.", + [3]byte{92, 143, 224}: "ARRIS Group, Inc.", + [3]byte{92, 147, 162}: "Liteon Technology Corporation", + [3]byte{92, 149, 174}: "Apple, Inc.", + [3]byte{92, 150, 86}: "AzureWave Technology Inc.", + [3]byte{92, 150, 106}: "RTNET", + [3]byte{92, 150, 157}: "Apple, Inc.", + [3]byte{92, 151, 243}: "Apple, Inc.", + [3]byte{92, 153, 96}: "Samsung Electronics Co.,Ltd", + [3]byte{92, 154, 216}: "FUJITSU LIMITED", + [3]byte{92, 161, 120}: "TableTop Media (dba Ziosk)", + [3]byte{92, 163, 157}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{92, 163, 235}: "Lokel s.r.o.", + [3]byte{92, 164, 138}: "Cisco Systems, Inc", + [3]byte{92, 168, 106}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{92, 169, 51}: "Luma Home", + [3]byte{92, 170, 253}: "Sonos, Inc.", + [3]byte{92, 172, 76}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{92, 173, 207}: "Apple, Inc.", + [3]byte{92, 175, 6}: "LG Electronics (Mobile Communications)", + [3]byte{92, 176, 102}: "ARRIS Group, Inc.", + [3]byte{92, 179, 149}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{92, 180, 62}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{92, 181, 36}: "Sony Mobile Communications AB", + [3]byte{92, 181, 89}: "CNEX Labs", + [3]byte{92, 182, 204}: "NovaComm Technologies Inc.", + [3]byte{92, 184, 203}: "Allis Communications", + [3]byte{92, 185, 1}: "Hewlett Packard", + [3]byte{92, 186, 55}: "Microsoft Corporation", + [3]byte{92, 189, 158}: "HONGKONG MIRACLE EAGLE TECHNOLOGY(GROUP) LIMITED", + [3]byte{92, 194, 19}: "Fr. Sauter AG", + [3]byte{92, 197, 212}: "Intel Corporate", + [3]byte{92, 198, 208}: "Skyworth Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{92, 198, 233}: "Edifier International", + [3]byte{92, 199, 215}: "AZROAD TECHNOLOGY COMPANY LIMITED", + [3]byte{92, 201, 211}: "PALLADIUM ENERGY ELETRONICA DA AMAZONIA LTDA", + [3]byte{92, 202, 26}: "Microsoft Mobile Oy", + [3]byte{92, 202, 50}: "Theben AG", + [3]byte{92, 204, 160}: "Gridwiz Inc.", + [3]byte{92, 204, 255}: "Techroutes Network Pvt Ltd", + [3]byte{92, 206, 173}: "CDYNE Corporation", + [3]byte{92, 207, 127}: "Espressif Inc.", + [3]byte{92, 209, 53}: "Xtreme Power Systems", + [3]byte{92, 210, 228}: "Intel Corporate", + [3]byte{92, 212, 27}: "UCZOON Technology Co., LTD", + [3]byte{92, 212, 171}: "Zektor", + [3]byte{92, 214, 31}: "Qardio, Inc", + [3]byte{92, 217, 152}: "D-Link Corporation", + [3]byte{92, 218, 212}: "Murata Manufacturing Co., Ltd.", + [3]byte{92, 220, 150}: "Arcadyan Technology Corporation", + [3]byte{92, 221, 112}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{92, 224, 197}: "Intel Corporate", + [3]byte{92, 224, 202}: "FeiTian United (Beijing) System Technology Co., Ltd.", + [3]byte{92, 224, 246}: "NIC.br- Nucleo de Informacao e Coordenacao do Ponto BR", + [3]byte{92, 226, 35}: "Delphin Technology AG", + [3]byte{92, 226, 134}: "Nortel Networks", + [3]byte{92, 226, 244}: "AcSiP Technology Corp.", + [3]byte{92, 227, 14}: "ARRIS Group, Inc.", + [3]byte{92, 227, 182}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{92, 231, 191}: "New Singularity International Technical Development Co.,Ltd", + [3]byte{92, 232, 235}: "Samsung Electronics Co.,Ltd", + [3]byte{92, 235, 78}: "R. STAHL HMI Systems GmbH", + [3]byte{92, 235, 104}: "Cheerstar Technology Co., Ltd", + [3]byte{92, 238, 121}: "Global Digitech Co LTD", + [3]byte{92, 242, 7}: "Speco Technologies", + [3]byte{92, 242, 134}: "IEEE Registration Authority", + [3]byte{92, 243, 112}: "CC&C Technologies, Inc", + [3]byte{92, 243, 252}: "IBM Corp", + [3]byte{92, 244, 171}: "ZyXEL Communications Corporation", + [3]byte{92, 245, 13}: "Institute of microelectronic applications", + [3]byte{92, 245, 218}: "Apple, Inc.", + [3]byte{92, 246, 220}: "Samsung Electronics Co.,Ltd", + [3]byte{92, 247, 195}: "SYNTECH (HK) TECHNOLOGY LIMITED", + [3]byte{92, 247, 230}: "Apple, Inc.", + [3]byte{92, 248, 33}: "Texas Instruments", + [3]byte{92, 248, 161}: "Murata Manufacturing Co., Ltd.", + [3]byte{92, 249, 56}: "Apple, Inc.", + [3]byte{92, 249, 106}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{92, 249, 221}: "Dell Inc.", + [3]byte{92, 249, 240}: "Atomos Engineering P/L", + [3]byte{92, 252, 102}: "Cisco Systems, Inc", + [3]byte{92, 255, 53}: "Wistron Corporation", + [3]byte{92, 255, 255}: "Shenzhen Kezhonglong Optoelectronic Technology Co., Ltd", + [3]byte{96, 1, 148}: "Espressif Inc.", + [3]byte{96, 2, 146}: "PEGATRON CORPORATION", + [3]byte{96, 2, 180}: "Wistron Neweb Corporation", + [3]byte{96, 3, 8}: "Apple, Inc.", + [3]byte{96, 3, 71}: "Billion Electric Co. Ltd.", + [3]byte{96, 4, 23}: "POSBANK CO.,LTD", + [3]byte{96, 8, 16}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{96, 8, 55}: "ivvi Scientific(Nanchang)Co.Ltd", + [3]byte{96, 11, 3}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{96, 15, 119}: "SilverPlus, Inc", + [3]byte{96, 17, 153}: "Siama Systems Inc", + [3]byte{96, 18, 131}: "Soluciones Tecnologicas para la Salud y el Bienestar SA", + [3]byte{96, 18, 139}: "CANON INC.", + [3]byte{96, 20, 102}: "zte corporation", + [3]byte{96, 20, 179}: "CyberTAN Technology Inc.", + [3]byte{96, 21, 199}: "IdaTech", + [3]byte{96, 24, 46}: "ShenZhen Protruly Electronic Ltd co.", + [3]byte{96, 24, 136}: "zte corporation", + [3]byte{96, 25, 12}: "RRAMAC", + [3]byte{96, 25, 41}: "VOLTRONIC POWER TECHNOLOGY(SHENZHEN) CORP.", + [3]byte{96, 25, 112}: "HUIZHOU QIAOXING ELECTRONICS TECHNOLOGY CO., LTD.", + [3]byte{96, 25, 113}: "ARRIS Group, Inc.", + [3]byte{96, 29, 15}: "Midnite Solar", + [3]byte{96, 30, 2}: "EltexAlatau", + [3]byte{96, 33, 3}: "I4VINE, INC", + [3]byte{96, 33, 192}: "Murata Manufacturing Co., Ltd.", + [3]byte{96, 36, 193}: "Jiangsu Zhongxun Electronic Technology Co., Ltd", + [3]byte{96, 42, 84}: "CardioTek B.V.", + [3]byte{96, 42, 208}: "Cisco SPVTG", + [3]byte{96, 49, 151}: "ZyXEL Communications Corporation", + [3]byte{96, 50, 240}: "Mplus technology", + [3]byte{96, 51, 75}: "Apple, Inc.", + [3]byte{96, 53, 83}: "Buwon Technology", + [3]byte{96, 54, 150}: "The Sapling Company", + [3]byte{96, 54, 221}: "Intel Corporate", + [3]byte{96, 56, 14}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{96, 56, 224}: "Belkin International Inc.", + [3]byte{96, 57, 31}: "ABB Ltd", + [3]byte{96, 62, 123}: "Gafachi, Inc.", + [3]byte{96, 62, 202}: "Cambridge Medical Robotics Ltd", + [3]byte{96, 63, 197}: "COX CO., LTD", + [3]byte{96, 66, 127}: "SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD", + [3]byte{96, 68, 245}: "Easy Digital Ltd.", + [3]byte{96, 69, 94}: "Liptel s.r.o.", + [3]byte{96, 69, 189}: "Microsoft", + [3]byte{96, 69, 203}: "ASUSTek COMPUTER INC.", + [3]byte{96, 70, 22}: "XIAMEN VANN INTELLIGENT CO., LTD", + [3]byte{96, 71, 212}: "FORICS Electronic Technology Co., Ltd.", + [3]byte{96, 72, 38}: "Newbridge Technologies Int. Ltd.", + [3]byte{96, 73, 193}: "Avaya Inc", + [3]byte{96, 74, 28}: "SUYIN Corporation", + [3]byte{96, 75, 170}: "Private", + [3]byte{96, 80, 193}: "Kinetek Sports", + [3]byte{96, 81, 44}: "TCT mobile ltd", + [3]byte{96, 82, 208}: "FACTS Engineering ", + [3]byte{96, 83, 23}: "Sandstone Technologies", + [3]byte{96, 84, 100}: "Eyedro Green Solutions Inc.", + [3]byte{96, 87, 24}: "Intel Corporate", + [3]byte{96, 91, 180}: "AzureWave Technology Inc.", + [3]byte{96, 96, 31}: "SZ DJI TECHNOLOGY CO.,LTD", + [3]byte{96, 99, 249}: "Ciholas, Inc.", + [3]byte{96, 99, 253}: "Transcend Communication Beijing Co.,Ltd.", + [3]byte{96, 100, 5}: "Texas Instruments", + [3]byte{96, 100, 83}: "AOD Co.,Ltd.", + [3]byte{96, 100, 161}: "RADiflow Ltd.", + [3]byte{96, 103, 32}: "Intel Corporate", + [3]byte{96, 105, 68}: "Apple, Inc.", + [3]byte{96, 105, 155}: "isepos GmbH", + [3]byte{96, 107, 189}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 108, 102}: "Intel Corporate", + [3]byte{96, 109, 199}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{96, 115, 92}: "Cisco Systems, Inc", + [3]byte{96, 115, 188}: "zte corporation", + [3]byte{96, 116, 141}: "Atmaca Elektronik", + [3]byte{96, 118, 136}: "Velodyne", + [3]byte{96, 119, 226}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 126, 221}: "Microsoft Mobile Oy", + [3]byte{96, 129, 43}: "Custom Control Concepts", + [3]byte{96, 129, 249}: "Helium Systems, Inc", + [3]byte{96, 131, 52}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{96, 131, 178}: "GkWare e.K.", + [3]byte{96, 132, 59}: "Soladigm, Inc.", + [3]byte{96, 134, 69}: "Avery Weigh-Tronix, LLC", + [3]byte{96, 137, 60}: "Thermo Fisher Scientific P.O.A.", + [3]byte{96, 137, 177}: "Key Digital Systems", + [3]byte{96, 137, 183}: "KAEL MÜHENDİSLİK ELEKTRONİK TİCARET SANAYİ LİMİTED ŞİRKETİ", + [3]byte{96, 140, 43}: "Hanson Technology", + [3]byte{96, 141, 23}: "Sentrus Government Systems Division, Inc", + [3]byte{96, 143, 92}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 144, 132}: "DSSD Inc", + [3]byte{96, 145, 243}: "vivo Mobile Communication Co., Ltd.", + [3]byte{96, 146, 23}: "Apple, Inc.", + [3]byte{96, 150, 32}: "Private", + [3]byte{96, 153, 209}: "Vuzix / Lenovo", + [3]byte{96, 154, 164}: "GVI SECURITY INC.", + [3]byte{96, 154, 193}: "Apple, Inc.", + [3]byte{96, 156, 159}: "Brocade Communications Systems, Inc.", + [3]byte{96, 158, 100}: "Vivonic GmbH", + [3]byte{96, 159, 157}: "CloudSwitch", + [3]byte{96, 161, 10}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 163, 125}: "Apple, Inc.", + [3]byte{96, 164, 76}: "ASUSTek COMPUTER INC.", + [3]byte{96, 164, 208}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 168, 254}: "Nokia", + [3]byte{96, 169, 176}: "Merchandising Technologies, Inc", + [3]byte{96, 172, 200}: "KunTeng Inc.", + [3]byte{96, 175, 109}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 177, 133}: "ATH system", + [3]byte{96, 179, 135}: "Synergics Technologies GmbH", + [3]byte{96, 179, 196}: "Elber Srl", + [3]byte{96, 180, 247}: "Plume Design Inc", + [3]byte{96, 182, 6}: "Phorus", + [3]byte{96, 182, 23}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{96, 185, 51}: "Deutron Electronics Corp.", + [3]byte{96, 185, 130}: "RO.VE.R. Laboratories S.p.A.", + [3]byte{96, 187, 12}: "Beijing HuaqinWorld Technology Co,Ltd", + [3]byte{96, 188, 76}: "EWM Hightec Welding GmbH", + [3]byte{96, 189, 145}: "Move Innovation", + [3]byte{96, 190, 181}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{96, 192, 191}: "ON Semiconductor", + [3]byte{96, 193, 203}: "Fujian Great Power PLC Equipment Co.,Ltd", + [3]byte{96, 195, 151}: "2Wire Inc", + [3]byte{96, 197, 71}: "Apple, Inc.", + [3]byte{96, 197, 168}: "Beijing LT Honway Technology Co.,Ltd", + [3]byte{96, 197, 173}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 198, 88}: "PHYTRONIX Co.,Ltd.", + [3]byte{96, 199, 152}: "Verifone", + [3]byte{96, 201, 128}: "Trymus", + [3]byte{96, 203, 251}: "AirScape Inc.", + [3]byte{96, 205, 169}: "Abloomy", + [3]byte{96, 205, 197}: "Taiwan Carol Electronics., Ltd", + [3]byte{96, 208, 169}: "Samsung Electronics Co.,Ltd", + [3]byte{96, 209, 170}: "Vishal Telecommunications Pvt Ltd", + [3]byte{96, 210, 98}: "Tzukuri Pty Ltd", + [3]byte{96, 210, 185}: "REALAND BIO CO., LTD.", + [3]byte{96, 211, 10}: "Quatius Limited", + [3]byte{96, 216, 25}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{96, 217, 160}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{96, 217, 199}: "Apple, Inc.", + [3]byte{96, 218, 35}: "Estech Co.,Ltd", + [3]byte{96, 219, 42}: "HNS", + [3]byte{96, 222, 68}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{96, 224, 14}: "SHINSEI ELECTRONICS CO LTD", + [3]byte{96, 227, 39}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{96, 227, 172}: "LG Electronics (Mobile Communications)", + [3]byte{96, 230, 188}: "Sino-Telecom Technology Co.,Ltd.", + [3]byte{96, 231, 1}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{96, 231, 138}: "UNISEM", + [3]byte{96, 233, 86}: "Ayla Networks, Inc", + [3]byte{96, 235, 105}: "QUANTA COMPUTER INC.", + [3]byte{96, 238, 92}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{96, 239, 198}: "Shenzhen Chima Technologies Co Limited", + [3]byte{96, 241, 61}: "JABLOCOM s.r.o.", + [3]byte{96, 241, 137}: "Murata Manufacturing Co., Ltd.", + [3]byte{96, 242, 129}: "TRANWO TECHNOLOGY CO., LTD.", + [3]byte{96, 242, 239}: "VisionVera International Co., Ltd.", + [3]byte{96, 243, 218}: "Logic Way GmbH", + [3]byte{96, 244, 69}: "Apple, Inc.", + [3]byte{96, 244, 148}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{96, 245, 156}: "CRU-Dataport", + [3]byte{96, 246, 115}: "TERUMO CORPORATION", + [3]byte{96, 248, 29}: "Apple, Inc.", + [3]byte{96, 250, 205}: "Apple, Inc.", + [3]byte{96, 251, 66}: "Apple, Inc.", + [3]byte{96, 253, 86}: "WOORISYSTEMS CO., Ltd", + [3]byte{96, 254, 30}: "China Palms Telecom.Ltd", + [3]byte{96, 254, 32}: "2Wire Inc", + [3]byte{96, 254, 197}: "Apple, Inc.", + [3]byte{96, 254, 249}: "Thomas & Betts", + [3]byte{96, 255, 221}: "C.E. ELECTRONICS, INC", + [3]byte{100, 0, 45}: "Powerlinq Co., LTD", + [3]byte{100, 0, 106}: "Dell Inc.", + [3]byte{100, 0, 241}: "Cisco Systems, Inc", + [3]byte{100, 5, 190}: "NEW LIGHT LED", + [3]byte{100, 9, 76}: "Beijing Superbee Wireless Technology Co.,Ltd", + [3]byte{100, 9, 128}: "Xiaomi Communications Co Ltd", + [3]byte{100, 11, 74}: "Digital Telecom Technology Limited", + [3]byte{100, 13, 206}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{100, 13, 230}: "Petra Systems", + [3]byte{100, 14, 54}: "TAZTAG", + [3]byte{100, 14, 148}: "Pluribus Networks, Inc.", + [3]byte{100, 15, 40}: "2Wire Inc", + [3]byte{100, 16, 132}: "HEXIUM Technical Development Co., Ltd.", + [3]byte{100, 18, 37}: "Cisco Systems, Inc", + [3]byte{100, 18, 105}: "ARRIS Group, Inc.", + [3]byte{100, 19, 108}: "zte corporation", + [3]byte{100, 22, 127}: "Polycom", + [3]byte{100, 22, 141}: "Cisco Systems, Inc", + [3]byte{100, 22, 240}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{100, 26, 34}: "Heliospectra AB", + [3]byte{100, 28, 103}: "DIGIBRAS INDUSTRIA DO BRASILS/A", + [3]byte{100, 30, 129}: "Dowslake Microsystems", + [3]byte{100, 32, 12}: "Apple, Inc.", + [3]byte{100, 33, 132}: "Nippon Denki Kagaku Co.,LTD", + [3]byte{100, 34, 22}: "Shandong Taixin Electronic co.,Ltd", + [3]byte{100, 36, 0}: "Xorcom Ltd.", + [3]byte{100, 39, 55}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{100, 45, 183}: "SEUNGIL ELECTRONICS", + [3]byte{100, 49, 80}: "Hewlett Packard", + [3]byte{100, 49, 126}: "Dexin Corporation", + [3]byte{100, 52, 9}: "BITwave Pte Ltd", + [3]byte{100, 53, 28}: "e-CON SYSTEMS INDIA PVT LTD", + [3]byte{100, 58, 177}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{100, 62, 140}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{100, 63, 95}: "Exablaze", + [3]byte{100, 66, 20}: "Swisscom Energy Solutions AG", + [3]byte{100, 67, 70}: "GuangDong Quick Network Computer CO.,LTD", + [3]byte{100, 75, 195}: "Shanghai WOASiS Telecommunications Ltd., Co.", + [3]byte{100, 75, 240}: "CalDigit, Inc", + [3]byte{100, 77, 112}: "dSPACE GmbH", + [3]byte{100, 79, 116}: "LENUS Co., Ltd.", + [3]byte{100, 79, 176}: "Hyunjin.com", + [3]byte{100, 81, 6}: "Hewlett Packard", + [3]byte{100, 81, 126}: "LONG BEN (DONGGUAN) ELECTRONIC TECHNOLOGY CO.,LTD.", + [3]byte{100, 82, 153}: "The Chamberlain Group, Inc", + [3]byte{100, 83, 93}: "Frauscher Sensortechnik", + [3]byte{100, 84, 34}: "Equinox Payments", + [3]byte{100, 85, 99}: "Intelight Inc.", + [3]byte{100, 85, 127}: "NSFOCUS Information Technology Co., Ltd.", + [3]byte{100, 85, 177}: "ARRIS Group, Inc.", + [3]byte{100, 86, 1}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{100, 89, 248}: "Vodafone Omnitel B.V.", + [3]byte{100, 90, 4}: "Chicony Electronics Co., Ltd.", + [3]byte{100, 93, 146}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{100, 93, 215}: "Shenzhen Lifesense Medical Electronics Co., Ltd. ", + [3]byte{100, 94, 190}: "Yahoo! JAPAN", + [3]byte{100, 95, 255}: "Nicolet Neuro", + [3]byte{100, 97, 132}: "VELUX", + [3]byte{100, 98, 35}: "Cellient Co., Ltd.", + [3]byte{100, 100, 155}: "Juniper Networks", + [3]byte{100, 101, 192}: "Nuvon, Inc", + [3]byte{100, 102, 179}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{100, 103, 7}: "Beijing Omnific Technology, Ltd.", + [3]byte{100, 104, 12}: "Comtrend Corporation", + [3]byte{100, 105, 188}: "Hytera Communications Co .,ltd", + [3]byte{100, 106, 82}: "Avaya Inc", + [3]byte{100, 106, 116}: "AUTH-SERVERS, LLC", + [3]byte{100, 108, 178}: "Samsung Electronics Co.,Ltd", + [3]byte{100, 110, 108}: "Radio Datacom LLC", + [3]byte{100, 110, 234}: "Iskratel d.o.o.", + [3]byte{100, 112, 2}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{100, 114, 216}: "GooWi Technology Co.,Limited", + [3]byte{100, 115, 226}: "Arbiter Systems, Inc.", + [3]byte{100, 116, 246}: "Shooter Detection Systems", + [3]byte{100, 118, 87}: "Innovative Security Designs", + [3]byte{100, 118, 186}: "Apple, Inc.", + [3]byte{100, 119, 125}: "Hitron Technologies. Inc", + [3]byte{100, 119, 145}: "Samsung Electronics Co.,Ltd", + [3]byte{100, 121, 167}: "Phison Electronics Corp. ", + [3]byte{100, 123, 212}: "Texas Instruments", + [3]byte{100, 124, 52}: "Ubee Interactive Corp.", + [3]byte{100, 125, 129}: "YOKOTA INDUSTRIAL CO,.LTD", + [3]byte{100, 127, 218}: "TEKTELIC Communications Inc.", + [3]byte{100, 128, 139}: "VG Controls, Inc.", + [3]byte{100, 128, 153}: "Intel Corporate", + [3]byte{100, 129, 37}: "Alphatron Marine BV", + [3]byte{100, 135, 136}: "Juniper Networks", + [3]byte{100, 135, 215}: "ADB Broadband Italia", + [3]byte{100, 136, 255}: "Sichuan Changhong Electric Ltd.", + [3]byte{100, 137, 154}: "LG Electronics (Mobile Communications)", + [3]byte{100, 141, 158}: "IVT Electronic Co.,Ltd", + [3]byte{100, 153, 93}: "LGE ", + [3]byte{100, 153, 104}: "Elentec", + [3]byte{100, 153, 160}: "AG Elektronik AB", + [3]byte{100, 154, 18}: "P2 Mobile Technologies Limited", + [3]byte{100, 154, 190}: "Apple, Inc.", + [3]byte{100, 155, 36}: "V Technology Co., Ltd.", + [3]byte{100, 156, 129}: "Qualcomm Inc.", + [3]byte{100, 156, 142}: "Texas Instruments", + [3]byte{100, 158, 243}: "Cisco Systems, Inc", + [3]byte{100, 159, 247}: "Kone OYj", + [3]byte{100, 160, 231}: "Cisco Systems, Inc", + [3]byte{100, 162, 50}: "OOO Samlight", + [3]byte{100, 163, 65}: "Wonderlan (Beijing) Technology Co., Ltd.", + [3]byte{100, 163, 203}: "Apple, Inc.", + [3]byte{100, 165, 195}: "Apple, Inc.", + [3]byte{100, 166, 81}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{100, 166, 143}: "Zhongshan Readboy Electronics Co.,Ltd", + [3]byte{100, 167, 105}: "HTC Corporation", + [3]byte{100, 167, 221}: "Avaya Inc", + [3]byte{100, 168, 55}: "Juni Korea Co., Ltd", + [3]byte{100, 174, 12}: "Cisco Systems, Inc", + [3]byte{100, 174, 136}: "Polytec GmbH", + [3]byte{100, 176, 166}: "Apple, Inc.", + [3]byte{100, 178, 29}: "Chengdu Phycom Tech Co., Ltd.", + [3]byte{100, 179, 16}: "Samsung Electronics Co.,Ltd", + [3]byte{100, 179, 112}: "PowerComm Solutions LLC", + [3]byte{100, 180, 115}: "Xiaomi Communications Co Ltd", + [3]byte{100, 182, 74}: "ViVOtech, Inc.", + [3]byte{100, 184, 83}: "Samsung Electronics Co.,Ltd", + [3]byte{100, 185, 232}: "Apple, Inc.", + [3]byte{100, 186, 189}: "SDJ Technologies, Inc.", + [3]byte{100, 188, 12}: "LG Electronics (Mobile Communications)", + [3]byte{100, 188, 17}: "CombiQ AB", + [3]byte{100, 195, 84}: "Avaya Inc", + [3]byte{100, 197, 170}: "South African Broadcasting Corporation", + [3]byte{100, 198, 103}: "Barnes&Noble", + [3]byte{100, 198, 175}: "AXERRA Networks Ltd", + [3]byte{100, 201, 68}: "LARK Technologies, Inc", + [3]byte{100, 204, 46}: "Xiaomi Communications Co Ltd", + [3]byte{100, 208, 45}: "Next Generation Integration (NGI)", + [3]byte{100, 209, 84}: "Routerboard.com", + [3]byte{100, 209, 163}: "Sitecom Europe BV", + [3]byte{100, 210, 65}: "Keith & Koep GmbH", + [3]byte{100, 212, 189}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{100, 212, 218}: "Intel Corporate", + [3]byte{100, 216, 20}: "Cisco Systems, Inc", + [3]byte{100, 217, 18}: "Solidica, Inc.", + [3]byte{100, 217, 84}: "Taicang T&W Electronics", + [3]byte{100, 217, 137}: "Cisco Systems, Inc", + [3]byte{100, 218, 160}: "Robert Bosch Smart Home GmbH", + [3]byte{100, 219, 24}: "OpenPattern", + [3]byte{100, 219, 67}: "Motorola (Wuhan) Mobility Technologies Communication Co., Ltd.", + [3]byte{100, 219, 129}: "Syszone Co., Ltd.", + [3]byte{100, 219, 160}: "Select Comfort", + [3]byte{100, 220, 1}: "Static Systems Group PLC", + [3]byte{100, 222, 28}: "Kingnetic Pte Ltd", + [3]byte{100, 225, 97}: "DEP Corp.", + [3]byte{100, 229, 153}: "EFM Networks", + [3]byte{100, 230, 37}: "Woxu Wireless Co., Ltd", + [3]byte{100, 230, 130}: "Apple, Inc.", + [3]byte{100, 232, 79}: "Serialway Communication Technology Co. Ltd", + [3]byte{100, 232, 146}: "Morio Denki Co., Ltd.", + [3]byte{100, 232, 230}: "global moisture management system", + [3]byte{100, 233, 80}: "Cisco Systems, Inc", + [3]byte{100, 234, 197}: "SiboTech Automation Co., Ltd.", + [3]byte{100, 235, 140}: "Seiko Epson Corporation", + [3]byte{100, 237, 87}: "ARRIS Group, Inc.", + [3]byte{100, 237, 98}: "WOORI SYSTEMS Co., Ltd", + [3]byte{100, 242, 66}: "Gerdes Aktiengesellschaft", + [3]byte{100, 245, 14}: "Kinion Technology Company Limited", + [3]byte{100, 246, 157}: "Cisco Systems, Inc", + [3]byte{100, 249, 112}: "Kenade Electronics Technology Co.,LTD.", + [3]byte{100, 249, 135}: "Avvasi Inc.", + [3]byte{100, 251, 129}: "IEEE Registration Authority", + [3]byte{100, 252, 140}: "Zonar Systems", + [3]byte{104, 2, 53}: "Konten Networks Inc.", + [3]byte{104, 5, 113}: "Samsung Electronics Co.,Ltd", + [3]byte{104, 5, 202}: "Intel Corporate", + [3]byte{104, 7, 21}: "Intel Corporate", + [3]byte{104, 9, 39}: "Apple, Inc.", + [3]byte{104, 10, 215}: "Yancheng Kecheng Optoelectronic Technology Co., Ltd", + [3]byte{104, 18, 45}: "Special Instrument Development Co., Ltd.", + [3]byte{104, 18, 149}: "Lupine Lighting Systems GmbH", + [3]byte{104, 20, 1}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{104, 21, 144}: "Sagemcom Broadband SAS", + [3]byte{104, 21, 211}: "Zaklady Elektroniki i Mechaniki Precyzyjnej R&G S.A.", + [3]byte{104, 22, 5}: "Systems And Electronic Development FZCO", + [3]byte{104, 23, 41}: "Intel Corporate", + [3]byte{104, 25, 63}: "Digital Airways", + [3]byte{104, 26, 178}: "zte corporation", + [3]byte{104, 28, 162}: "Rosewill Inc.", + [3]byte{104, 29, 100}: "Sunwave Communications Co., Ltd", + [3]byte{104, 30, 139}: "InfoSight Corporation", + [3]byte{104, 31, 216}: "Siemens Industry, Inc.", + [3]byte{104, 35, 75}: "Nihon Dengyo Kousaku", + [3]byte{104, 38, 42}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{104, 39, 55}: "Samsung Electronics Co.,Ltd", + [3]byte{104, 40, 186}: "Dejai", + [3]byte{104, 40, 246}: "Vubiq Networks, Inc.", + [3]byte{104, 45, 220}: "Wuhan Changjiang Electro-Communication Equipment CO.,LTD", + [3]byte{104, 49, 254}: "Teladin Co.,Ltd.", + [3]byte{104, 53, 99}: "SHENZHEN LIOWN ELECTRONICS CO.,LTD.", + [3]byte{104, 54, 181}: "DriveScale, Inc.", + [3]byte{104, 55, 233}: "Amazon Technologies Inc.", + [3]byte{104, 59, 30}: "Countwise LTD", + [3]byte{104, 60, 125}: "Magic Intelligence Technology Limited", + [3]byte{104, 62, 52}: "MEIZU Technology Co., Ltd.", + [3]byte{104, 62, 236}: "ERECA", + [3]byte{104, 67, 82}: "Bhuu Limited", + [3]byte{104, 72, 152}: "Samsung Electronics Co.,Ltd", + [3]byte{104, 75, 136}: "Galtronics Telemetry Inc.", + [3]byte{104, 76, 168}: "Shenzhen Herotel Tech. Co., Ltd.", + [3]byte{104, 81, 183}: "PowerCloud Systems, Inc.", + [3]byte{104, 83, 108}: "SPnS Co.,Ltd", + [3]byte{104, 83, 136}: "P&S Technology", + [3]byte{104, 84, 193}: "ColorTokens, Inc.", + [3]byte{104, 84, 237}: "Alcatel-Lucent", + [3]byte{104, 84, 245}: "enLighted Inc", + [3]byte{104, 84, 253}: "Amazon Technologies Inc.", + [3]byte{104, 88, 197}: "ZF TRW Automotive", + [3]byte{104, 89, 127}: "Alcatel Lucent", + [3]byte{104, 91, 53}: "Apple, Inc.", + [3]byte{104, 91, 54}: "POWERTECH INDUSTRIAL CO., LTD.", + [3]byte{104, 93, 67}: "Intel Corporate", + [3]byte{104, 94, 107}: "PowerRay Co., Ltd.", + [3]byte{104, 99, 89}: "Advanced Digital Broadcast SA", + [3]byte{104, 100, 75}: "Apple, Inc.", + [3]byte{104, 105, 46}: "Zycoo Co.,Ltd", + [3]byte{104, 105, 117}: "Angler Labs Inc", + [3]byte{104, 105, 242}: "ComAp s.r.o.", + [3]byte{104, 110, 35}: "Wi3 Inc.", + [3]byte{104, 110, 72}: "Prophet Electronic Technology Corp.,Ltd", + [3]byte{104, 114, 81}: "Ubiquiti Networks Inc.", + [3]byte{104, 114, 220}: "CETORY.TV Company Limited", + [3]byte{104, 118, 79}: "Sony Mobile Communications AB", + [3]byte{104, 120, 72}: "Westunitis Co., Ltd.", + [3]byte{104, 120, 76}: "Nortel Networks", + [3]byte{104, 121, 36}: "ELS-GmbH & Co. KG", + [3]byte{104, 121, 237}: "SHARP Corporation", + [3]byte{104, 124, 200}: "Measurement Systems S. de R.L.", + [3]byte{104, 124, 213}: "Y Soft Corporation, a.s.", + [3]byte{104, 127, 116}: "Cisco-Linksys, LLC", + [3]byte{104, 131, 26}: "Pandora Mobility Corporation", + [3]byte{104, 132, 112}: "eSSys Co.,Ltd", + [3]byte{104, 133, 64}: "IGI Mobile, Inc.", + [3]byte{104, 133, 106}: "OuterLink Corporation", + [3]byte{104, 134, 167}: "Cisco Systems, Inc", + [3]byte{104, 134, 231}: "Orbotix, Inc.", + [3]byte{104, 135, 107}: "INQ Mobile Limited", + [3]byte{104, 137, 193}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{104, 138, 181}: "EDP Servicos", + [3]byte{104, 138, 240}: "zte corporation", + [3]byte{104, 143, 132}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{104, 145, 208}: "IEEE Registration Authority", + [3]byte{104, 146, 52}: "Ruckus Wireless", + [3]byte{104, 147, 97}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{104, 148, 35}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{104, 150, 123}: "Apple, Inc.", + [3]byte{104, 151, 75}: "Shenzhen Costar Electronics Co. Ltd.", + [3]byte{104, 151, 232}: "Society of Motion Picture & Television Engineers", + [3]byte{104, 153, 205}: "Cisco Systems, Inc", + [3]byte{104, 154, 183}: "Atelier Vision Corporation", + [3]byte{104, 156, 94}: "AcSiP Technology Corp.", + [3]byte{104, 156, 112}: "Apple, Inc.", + [3]byte{104, 156, 226}: "Cisco Systems, Inc", + [3]byte{104, 158, 25}: "Texas Instruments", + [3]byte{104, 159, 240}: "zte corporation", + [3]byte{104, 160, 246}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{104, 161, 183}: "Honghao Mingchuan Technology (Beijing) CO.,Ltd.", + [3]byte{104, 163, 120}: "FREEBOX SAS", + [3]byte{104, 163, 196}: "Liteon Technology Corporation", + [3]byte{104, 164, 14}: "BSH Bosch and Siemens Home Appliances GmbH", + [3]byte{104, 168, 40}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{104, 168, 109}: "Apple, Inc.", + [3]byte{104, 170, 210}: "DATECS LTD.,", + [3]byte{104, 171, 138}: "RF IDeas", + [3]byte{104, 174, 32}: "Apple, Inc.", + [3]byte{104, 175, 19}: "Futura Mobility", + [3]byte{104, 176, 148}: "INESA ELECTRON CO.,LTD", + [3]byte{104, 179, 94}: "Shenzhen Neostra Technology Co.Ltd", + [3]byte{104, 180, 58}: "WaterFurnace International, Inc.", + [3]byte{104, 181, 153}: "Hewlett Packard", + [3]byte{104, 182, 252}: "Hitron Technologies. Inc", + [3]byte{104, 184, 217}: "Act KDE, Inc.", + [3]byte{104, 185, 131}: "b-plus GmbH", + [3]byte{104, 188, 12}: "Cisco Systems, Inc", + [3]byte{104, 189, 171}: "Cisco Systems, Inc", + [3]byte{104, 196, 77}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{104, 201, 11}: "Texas Instruments", + [3]byte{104, 202, 0}: "Octopus Systems Limited", + [3]byte{104, 204, 110}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{104, 204, 156}: "Mine Site Technologies", + [3]byte{104, 205, 15}: "U Tek Company Limited", + [3]byte{104, 206, 78}: "L-3 Communications Infrared Products", + [3]byte{104, 209, 253}: "Shenzhen Trimax Technology Co.,Ltd", + [3]byte{104, 210, 71}: "Portalis LC", + [3]byte{104, 217, 37}: "ProSys Development Services", + [3]byte{104, 217, 60}: "Apple, Inc.", + [3]byte{104, 219, 103}: "Nantong Coship Electronics Co., Ltd", + [3]byte{104, 219, 150}: "OPWILL Technologies CO .,LTD", + [3]byte{104, 219, 202}: "Apple, Inc.", + [3]byte{104, 220, 232}: "PacketStorm Communications", + [3]byte{104, 223, 221}: "Xiaomi Communications Co Ltd", + [3]byte{104, 225, 102}: "Private", + [3]byte{104, 228, 31}: "Unglaube Identech GmbH", + [3]byte{104, 232, 235}: "Linktel Technologies Co.,Ltd", + [3]byte{104, 235, 174}: "Samsung Electronics Co.,Ltd", + [3]byte{104, 235, 197}: "Angstrem Telecom", + [3]byte{104, 236, 98}: "YODO Technology Corp. Ltd.", + [3]byte{104, 237, 67}: "BlackBerry RTS", + [3]byte{104, 237, 164}: "Shenzhen Seavo Technology Co.,Ltd", + [3]byte{104, 238, 150}: "Cisco SPVTG", + [3]byte{104, 239, 189}: "Cisco Systems, Inc", + [3]byte{104, 240, 109}: "ALONG INDUSTRIAL CO., LIMITED", + [3]byte{104, 240, 188}: "Shenzhen LiWiFi Technology Co., Ltd", + [3]byte{104, 241, 37}: "Data Controls Inc.", + [3]byte{104, 247, 40}: "LCFC(HeFei) Electronics Technology co., ltd", + [3]byte{104, 248, 149}: "Redflow Limited", + [3]byte{104, 249, 86}: "Objetivos y Servicio de Valor Añadido", + [3]byte{104, 251, 126}: "Apple, Inc.", + [3]byte{104, 251, 149}: "Generalplus Technology Inc.", + [3]byte{104, 252, 179}: "Next Level Security Systems, Inc.", + [3]byte{108, 2, 115}: "Shenzhen Jin Yun Video Equipment Co., Ltd.", + [3]byte{108, 4, 96}: "RBH Access Technologies Inc.", + [3]byte{108, 9, 214}: "Digiquest Electronics LTD", + [3]byte{108, 11, 132}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{108, 14, 13}: "Sony Mobile Communications AB", + [3]byte{108, 14, 230}: "Chengdu Xiyida Electronic Technology Co,.Ltd", + [3]byte{108, 15, 106}: "JDC Tech Co., Ltd.", + [3]byte{108, 20, 247}: "Erhardt+Leimer GmbH", + [3]byte{108, 21, 249}: "Nautronix Limited", + [3]byte{108, 22, 14}: "ShotTracker", + [3]byte{108, 24, 17}: "Decatur Electronics", + [3]byte{108, 25, 143}: "D-Link International", + [3]byte{108, 25, 192}: "Apple, Inc.", + [3]byte{108, 30, 112}: "Guangzhou YBDS IT Co.,Ltd", + [3]byte{108, 30, 144}: "Hansol Technics Co., Ltd.", + [3]byte{108, 32, 86}: "Cisco Systems, Inc", + [3]byte{108, 34, 171}: "Ainsworth Game Technology", + [3]byte{108, 35, 185}: "Sony Mobile Communications AB", + [3]byte{108, 36, 131}: "Microsoft Mobile Oy", + [3]byte{108, 37, 185}: "BBK EDUCATIONAL ELECTRONICS CORP.,LTD.", + [3]byte{108, 39, 121}: "Microsoft Mobile Oy", + [3]byte{108, 41, 149}: "Intel Corporate", + [3]byte{108, 44, 6}: "OOO NPP Systemotechnika-NN", + [3]byte{108, 46, 51}: "Accelink Technologies Co.,Ltd.", + [3]byte{108, 46, 114}: "B&B EXPORTING LIMITED", + [3]byte{108, 46, 133}: "Sagemcom Broadband SAS", + [3]byte{108, 47, 44}: "Samsung Electronics Co.,Ltd", + [3]byte{108, 50, 222}: "Indieon Technologies Pvt. Ltd.", + [3]byte{108, 51, 169}: "Magicjack LP", + [3]byte{108, 56, 161}: "Ubee Interactive Corp.", + [3]byte{108, 57, 29}: "Beijing ZhongHuaHun Network Information center", + [3]byte{108, 58, 132}: "Shenzhen Aero-Startech. Co.Ltd", + [3]byte{108, 59, 107}: "Routerboard.com", + [3]byte{108, 59, 229}: "Hewlett Packard", + [3]byte{108, 60, 83}: "SoundHawk Corp", + [3]byte{108, 62, 109}: "Apple, Inc.", + [3]byte{108, 62, 156}: "KE Knestel Elektronik GmbH", + [3]byte{108, 64, 8}: "Apple, Inc.", + [3]byte{108, 64, 198}: "Nimbus Data Systems, Inc.", + [3]byte{108, 65, 106}: "Cisco Systems, Inc", + [3]byte{108, 68, 24}: "Zappware", + [3]byte{108, 69, 152}: "Antex Electronic Corp.", + [3]byte{108, 74, 57}: "BITA", + [3]byte{108, 75, 127}: "Vossloh-Schwabe Deutschland GmbH", + [3]byte{108, 75, 144}: "LiteON", + [3]byte{108, 80, 77}: "Cisco Systems, Inc", + [3]byte{108, 87, 121}: "Aclima, Inc.", + [3]byte{108, 89, 64}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{108, 89, 118}: "Shanghai Tricheer Technology Co.,Ltd.", + [3]byte{108, 90, 52}: "Shenzhen Haitianxiong Electronic Co., Ltd.", + [3]byte{108, 90, 181}: "TCL Technoly Electronics (Huizhou) Co., Ltd.", + [3]byte{108, 92, 20}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{108, 92, 222}: "SunReports, Inc.", + [3]byte{108, 93, 99}: "ShenZhen Rapoo Technology Co., Ltd.", + [3]byte{108, 94, 122}: "Ubiquitous Internet Telecom Co., Ltd", + [3]byte{108, 95, 28}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{108, 97, 38}: "Rinicom Holdings", + [3]byte{108, 98, 109}: "Micro-Star INT'L CO., LTD", + [3]byte{108, 100, 26}: "Penguin Computing", + [3]byte{108, 110, 254}: "Core Logic Inc.", + [3]byte{108, 111, 24}: "Stereotaxis, Inc.", + [3]byte{108, 112, 57}: "Novar GmbH", + [3]byte{108, 112, 159}: "Apple, Inc.", + [3]byte{108, 113, 189}: "EZELINK TELECOM", + [3]byte{108, 113, 217}: "AzureWave Technology Inc.", + [3]byte{108, 114, 32}: "D-Link International", + [3]byte{108, 114, 231}: "Apple, Inc.", + [3]byte{108, 117, 13}: "WiFiSONG", + [3]byte{108, 118, 96}: "KYOCERA Corporation", + [3]byte{108, 129, 254}: "Mitsuba Corporation", + [3]byte{108, 131, 54}: "Samsung Electronics Co.,Ltd", + [3]byte{108, 131, 102}: "Nanjing SAC Power Grid Automation Co., Ltd.", + [3]byte{108, 134, 134}: "Technonia", + [3]byte{108, 136, 20}: "Intel Corporate", + [3]byte{108, 139, 47}: "zte corporation", + [3]byte{108, 140, 219}: "Otus Technologies Ltd", + [3]byte{108, 141, 101}: "Wireless Glue Networks, Inc.", + [3]byte{108, 141, 193}: "Apple, Inc.", + [3]byte{108, 143, 181}: "Microsoft Mobile Oy", + [3]byte{108, 144, 177}: "SanLogic Inc", + [3]byte{108, 146, 191}: "Inspur Electronic Information Industry Co.,Ltd.", + [3]byte{108, 147, 84}: "Yaojin Technology (Shenzhen) Co., LTD.", + [3]byte{108, 148, 248}: "Apple, Inc.", + [3]byte{108, 149, 34}: "Scalys", + [3]byte{108, 152, 235}: "Riverbed Technology, Inc.", + [3]byte{108, 153, 137}: "Cisco Systems, Inc", + [3]byte{108, 154, 201}: "Valentine Research, Inc.", + [3]byte{108, 155, 2}: "Nokia Corporation", + [3]byte{108, 156, 233}: "Nimble Storage", + [3]byte{108, 156, 237}: "Cisco Systems, Inc", + [3]byte{108, 161, 0}: "Intel Corporate", + [3]byte{108, 166, 130}: "EDAM information & communications", + [3]byte{108, 167, 95}: "zte corporation", + [3]byte{108, 167, 128}: "Nokia Corporation", + [3]byte{108, 167, 250}: "YOUNGBO ENGINEERING INC.", + [3]byte{108, 168, 73}: "Avaya Inc", + [3]byte{108, 168, 88}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{108, 169, 6}: "Telefield Ltd", + [3]byte{108, 169, 111}: "TransPacket AS", + [3]byte{108, 170, 179}: "Ruckus Wireless", + [3]byte{108, 171, 49}: "Apple, Inc.", + [3]byte{108, 171, 77}: "Digital Payment Technologies", + [3]byte{108, 172, 96}: "Venetex Corp", + [3]byte{108, 173, 63}: "Hubbell Building Automation, Inc.", + [3]byte{108, 173, 239}: "KZ Broadband Technologies, Ltd. ", + [3]byte{108, 173, 248}: "AzureWave Technology Inc.", + [3]byte{108, 174, 139}: "IBM Corporation", + [3]byte{108, 176, 206}: "NETGEAR", + [3]byte{108, 179, 17}: "Shenzhen Lianrui Electronics Co.,Ltd", + [3]byte{108, 179, 80}: "Anhui comhigher tech co.,ltd", + [3]byte{108, 180, 167}: "Landauer, Inc.", + [3]byte{108, 181, 107}: "HUMAX Co., Ltd.", + [3]byte{108, 183, 244}: "Samsung Electronics Co.,Ltd", + [3]byte{108, 185, 197}: "Delta Networks, Inc.", + [3]byte{108, 190, 233}: "Alcatel-Lucent IPD", + [3]byte{108, 191, 181}: "Noon Technology Co., Ltd", + [3]byte{108, 193, 210}: "ARRIS Group, Inc.", + [3]byte{108, 194, 23}: "Hewlett Packard", + [3]byte{108, 194, 107}: "Apple, Inc.", + [3]byte{108, 202, 8}: "ARRIS Group, Inc.", + [3]byte{108, 208, 50}: "LG Electronics", + [3]byte{108, 209, 70}: "Smartek d.o.o.", + [3]byte{108, 209, 176}: "WING SING ELECTRONICS HONG KONG LIMITED", + [3]byte{108, 214, 138}: "LG Electronics (Mobile Communications)", + [3]byte{108, 220, 106}: "Promethean Limited", + [3]byte{108, 224, 30}: "Modcam AB", + [3]byte{108, 224, 176}: "SOUND4", + [3]byte{108, 227, 182}: "Nera Telecommunications Ltd.", + [3]byte{108, 228, 206}: "Villiger Security Solutions AG", + [3]byte{108, 232, 115}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{108, 233, 7}: "Nokia Corporation", + [3]byte{108, 233, 131}: "Gastron Co., LTD.", + [3]byte{108, 235, 178}: "Dongguan Sen DongLv Electronics Co.,Ltd", + [3]byte{108, 236, 90}: "Hon Hai Precision Ind. CO.,Ltd.", + [3]byte{108, 236, 161}: "SHENZHEN CLOU ELECTRONICS CO. LTD.", + [3]byte{108, 236, 235}: "Texas Instruments", + [3]byte{108, 239, 198}: "SHENZHEN TWOWING TECHNOLOGIES CO.,LTD.", + [3]byte{108, 240, 73}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{108, 243, 115}: "Samsung Electronics Co.,Ltd", + [3]byte{108, 243, 127}: "Aruba Networks", + [3]byte{108, 245, 232}: "Mooredoll Inc.", + [3]byte{108, 249, 124}: "Nanoptix Inc.", + [3]byte{108, 250, 88}: "Avaya Inc", + [3]byte{108, 250, 137}: "Cisco Systems, Inc", + [3]byte{108, 250, 167}: "AMPAK Technology, Inc.", + [3]byte{108, 253, 185}: "Proware Technologies Co Ltd.", + [3]byte{108, 255, 190}: "MPB Communications Inc.", + [3]byte{112, 1, 54}: "FATEK Automation Corporation", + [3]byte{112, 2, 88}: "01DB-METRAVIB", + [3]byte{112, 5, 20}: "LG Electronics (Mobile Communications)", + [3]byte{112, 11, 192}: "Dewav Technology Company", + [3]byte{112, 15, 199}: "SHENZHEN IKINLOOP TECHNOLOGY CO.,LTD.", + [3]byte{112, 15, 236}: "Poindus Systems Corp.", + [3]byte{112, 16, 92}: "Cisco Systems, Inc", + [3]byte{112, 16, 111}: "Hewlett Packard Enterprise", + [3]byte{112, 17, 36}: "Apple, Inc.", + [3]byte{112, 17, 174}: "Music Life LTD", + [3]byte{112, 20, 4}: "Limited Liability Company", + [3]byte{112, 20, 166}: "Apple, Inc.", + [3]byte{112, 24, 139}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{112, 26, 4}: "Liteon Technology Corporation", + [3]byte{112, 26, 237}: "ADVAS CO., LTD.", + [3]byte{112, 28, 231}: "Intel Corporate", + [3]byte{112, 29, 127}: "Comtech Technology Co., Ltd.", + [3]byte{112, 29, 196}: "NorthStar Battery Company, LLC", + [3]byte{112, 32, 132}: "Hon Hai Precision Ind. Co., Ltd.", + [3]byte{112, 35, 147}: "fos4X GmbH", + [3]byte{112, 37, 38}: "Nokia", + [3]byte{112, 37, 89}: "CyberTAN Technology Inc.", + [3]byte{112, 40, 139}: "Samsung Electronics Co.,Ltd", + [3]byte{112, 41, 0}: "Shenzhen ChipTrip Technology Co,Ltd", + [3]byte{112, 42, 125}: "EpSpot AB", + [3]byte{112, 43, 29}: "E-Domus International Limited", + [3]byte{112, 44, 31}: "Wisol", + [3]byte{112, 45, 132}: "i4C Innovations", + [3]byte{112, 45, 209}: "Newings Communication CO., LTD.", + [3]byte{112, 46, 34}: "zte corporation", + [3]byte{112, 47, 75}: "PolyVision Inc.", + [3]byte{112, 47, 151}: "Aava Mobile Oy", + [3]byte{112, 48, 24}: "Avaya Inc", + [3]byte{112, 48, 93}: "Ubiquoss Inc", + [3]byte{112, 48, 94}: "Nanjing Zhongke Menglian Information Technology Co.,LTD", + [3]byte{112, 49, 135}: "ACX GmbH", + [3]byte{112, 50, 213}: "Athena Wireless Communications Inc", + [3]byte{112, 56, 17}: "Invensys Rail", + [3]byte{112, 56, 180}: "Low Tech Solutions", + [3]byte{112, 56, 238}: "Avaya Inc", + [3]byte{112, 58, 14}: "Aruba Networks", + [3]byte{112, 58, 203}: "Google, Inc.", + [3]byte{112, 58, 216}: "Shenzhen Afoundry Electronic Co., Ltd", + [3]byte{112, 60, 3}: "RadiAnt Co.,Ltd", + [3]byte{112, 60, 57}: "SEAWING Kft", + [3]byte{112, 61, 21}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{112, 62, 172}: "Apple, Inc.", + [3]byte{112, 65, 183}: "Edwards Lifesciences LLC", + [3]byte{112, 70, 66}: "CHYNG HONG ELECTRONIC CO., LTD.", + [3]byte{112, 72, 15}: "Apple, Inc.", + [3]byte{112, 74, 174}: "Xstream Flow (Pty) Ltd", + [3]byte{112, 74, 228}: "Rinstrum Pty Ltd", + [3]byte{112, 76, 237}: "TMRG, Inc.", + [3]byte{112, 77, 123}: "ASUSTek COMPUTER INC.", + [3]byte{112, 78, 1}: "KWANGWON TECH CO., LTD.", + [3]byte{112, 78, 102}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{112, 79, 87}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{112, 80, 175}: "BSkyB Ltd", + [3]byte{112, 82, 197}: "Avaya Inc", + [3]byte{112, 83, 63}: "Alfa Instrumentos Eletronicos Ltda.", + [3]byte{112, 84, 210}: "PEGATRON CORPORATION", + [3]byte{112, 84, 245}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{112, 86, 129}: "Apple, Inc.", + [3]byte{112, 88, 18}: "Panasonic AVC Networks Company", + [3]byte{112, 89, 87}: "Medallion Instrumentation Systems", + [3]byte{112, 89, 134}: "OOO TTV", + [3]byte{112, 90, 15}: "Hewlett Packard", + [3]byte{112, 90, 158}: "Technicolor CH USA Inc.", + [3]byte{112, 90, 182}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{112, 91, 46}: "M2Communication Inc.", + [3]byte{112, 92, 173}: "Konami Gaming Inc", + [3]byte{112, 94, 170}: "Action Target, Inc.", + [3]byte{112, 96, 222}: "LaVision GmbH", + [3]byte{112, 97, 115}: "Calantec GmbH", + [3]byte{112, 98, 184}: "D-Link International", + [3]byte{112, 100, 23}: "ORBIS TECNOLOGIA ELECTRICA S.A.", + [3]byte{112, 101, 130}: "Suzhou Hanming Technologies Co., Ltd.", + [3]byte{112, 102, 27}: "Sonova AG", + [3]byte{112, 104, 121}: "Saijo Denki International Co., Ltd.", + [3]byte{112, 109, 236}: "Wifi-soft LLC", + [3]byte{112, 111, 129}: "Private", + [3]byte{112, 112, 13}: "Apple, Inc.", + [3]byte{112, 112, 76}: "Purple Communications, Inc", + [3]byte{112, 113, 179}: "Brain Corporation", + [3]byte{112, 113, 188}: "PEGATRON CORPORATION", + [3]byte{112, 114, 13}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{112, 114, 60}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{112, 114, 207}: "EdgeCore Networks", + [3]byte{112, 115, 203}: "Apple, Inc.", + [3]byte{112, 118, 48}: "ARRIS Group, Inc.", + [3]byte{112, 118, 221}: "Oxyguard International A/S", + [3]byte{112, 118, 240}: "LevelOne Communications (India) Private Limited", + [3]byte{112, 118, 255}: "KERLINK", + [3]byte{112, 119, 129}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{112, 121, 56}: "Wuxi Zhanrui Electronic Technology Co.,LTD", + [3]byte{112, 121, 144}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{112, 123, 232}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{112, 124, 24}: "ADATA Technology Co., Ltd", + [3]byte{112, 124, 105}: "Avaya Inc", + [3]byte{112, 126, 67}: "ARRIS Group, Inc.", + [3]byte{112, 126, 222}: "NASTEC LTD.", + [3]byte{112, 129, 5}: "Cisco Systems, Inc", + [3]byte{112, 129, 235}: "Apple, Inc.", + [3]byte{112, 130, 14}: "as electronics GmbH", + [3]byte{112, 130, 142}: "OleumTech Corporation", + [3]byte{112, 133, 194}: "ASRock Incorporation", + [3]byte{112, 133, 198}: "ARRIS Group, Inc.", + [3]byte{112, 136, 77}: "JAPAN RADIO CO., LTD.", + [3]byte{112, 138, 9}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{112, 139, 120}: "citygrow technology co., ltd", + [3]byte{112, 139, 205}: "ASUSTek COMPUTER INC.", + [3]byte{112, 141, 9}: "Nokia Corporation", + [3]byte{112, 145, 143}: "Weber-Stephen Products LLC", + [3]byte{112, 147, 131}: "Intelligent Optical Network High Tech CO.,LTD.", + [3]byte{112, 147, 248}: "Space Monkey, Inc.", + [3]byte{112, 151, 86}: "Happyelectronics Co.,Ltd", + [3]byte{112, 154, 11}: "Italian Institute of Technology", + [3]byte{112, 155, 165}: "Shenzhen Y&D Electronics Co.,LTD.", + [3]byte{112, 155, 252}: "Bryton Inc.", + [3]byte{112, 156, 143}: "Nero AG", + [3]byte{112, 158, 41}: "Sony Interactive Entertainment Inc.", + [3]byte{112, 158, 134}: "X6D Limited", + [3]byte{112, 159, 45}: "zte corporation", + [3]byte{112, 161, 145}: "Trendsetter Medical, LLC", + [3]byte{112, 162, 179}: "Apple, Inc.", + [3]byte{112, 164, 28}: "Advanced Wireless Dynamics S.L.", + [3]byte{112, 166, 106}: "Prox Dynamics AS", + [3]byte{112, 168, 76}: "MONAD., Inc.", + [3]byte{112, 168, 227}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{112, 170, 178}: "BlackBerry RTS", + [3]byte{112, 173, 84}: "Malvern Instruments Ltd", + [3]byte{112, 175, 36}: "TP Vision Belgium NV", + [3]byte{112, 175, 37}: "Nishiyama Industry Co.,LTD.", + [3]byte{112, 175, 106}: "SHENZHEN FENGLIAN TECHNOLOGY CO., LTD.", + [3]byte{112, 176, 53}: "Shenzhen Zowee Technology Co., Ltd", + [3]byte{112, 176, 140}: "Shenou Communication Equipment Co.,Ltd", + [3]byte{112, 177, 78}: "ARRIS Group, Inc.", + [3]byte{112, 178, 101}: "Hiltron s.r.l.", + [3]byte{112, 179, 213}: "IEEE Registration Authority", + [3]byte{112, 181, 153}: "Embedded Technologies s.r.o.", + [3]byte{112, 185, 33}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{112, 186, 239}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{112, 191, 62}: "Charles River Laboratories", + [3]byte{112, 198, 172}: "Bosch Automotive Aftermarket", + [3]byte{112, 199, 111}: "INNO S", + [3]byte{112, 202, 77}: "Shenzhen lnovance Technology Co.,Ltd.", + [3]byte{112, 202, 155}: "Cisco Systems, Inc", + [3]byte{112, 205, 96}: "Apple, Inc.", + [3]byte{112, 211, 121}: "Cisco Systems, Inc", + [3]byte{112, 212, 242}: "RIM", + [3]byte{112, 213, 126}: "Scalar Corporation", + [3]byte{112, 213, 231}: "Wellcore Corporation", + [3]byte{112, 214, 182}: "Metrum Technologies", + [3]byte{112, 216, 128}: "Upos System sp. z o.o.", + [3]byte{112, 217, 35}: "vivo Mobile Communication Co., Ltd.", + [3]byte{112, 217, 49}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{112, 218, 156}: "TECSEN", + [3]byte{112, 219, 152}: "Cisco Systems, Inc", + [3]byte{112, 221, 161}: "Tellabs", + [3]byte{112, 222, 226}: "Apple, Inc.", + [3]byte{112, 224, 39}: "HONGYU COMMUNICATION TECHNOLOGY LIMITED", + [3]byte{112, 225, 57}: "3view Ltd", + [3]byte{112, 226, 76}: "SAE IT-systems GmbH & Co. KG", + [3]byte{112, 226, 132}: "Wistron Infocomm (Zhongshan) Corporation", + [3]byte{112, 228, 34}: "Cisco Systems, Inc", + [3]byte{112, 231, 44}: "Apple, Inc.", + [3]byte{112, 232, 67}: "Beijing C&W Optical Communication Technology Co.,Ltd.", + [3]byte{112, 236, 228}: "Apple, Inc.", + [3]byte{112, 238, 80}: "Netatmo", + [3]byte{112, 240, 135}: "Apple, Inc.", + [3]byte{112, 241, 118}: "Data Modul AG", + [3]byte{112, 241, 150}: "Actiontec Electronics, Inc", + [3]byte{112, 241, 161}: "Liteon Technology Corporation", + [3]byte{112, 241, 229}: "Xetawave LLC", + [3]byte{112, 243, 149}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{112, 248, 231}: "IEEE Registration Authority", + [3]byte{112, 249, 39}: "Samsung Electronics Co.,Ltd", + [3]byte{112, 249, 109}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{112, 252, 140}: "OneAccess SA", + [3]byte{112, 255, 92}: "Cheerzing Communication(Xiamen)Technology Co.,Ltd", + [3]byte{112, 255, 118}: "Texas Instruments", + [3]byte{116, 3, 189}: "BUFFALO.INC", + [3]byte{116, 4, 43}: "Lenovo Mobile Communication (Wuhan) Company Limited", + [3]byte{116, 10, 188}: "JSJS Designs (Europe) Limited", + [3]byte{116, 14, 219}: "Optowiz Co., Ltd", + [3]byte{116, 20, 137}: "SRT Wireless", + [3]byte{116, 21, 226}: "Tri-Sen Systems Corporation", + [3]byte{116, 24, 101}: "Shanghai DareGlobal Technologies Co.,Ltd", + [3]byte{116, 25, 248}: "IEEE Registration Authority", + [3]byte{116, 27, 178}: "Apple, Inc.", + [3]byte{116, 30, 147}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{116, 31, 74}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{116, 35, 68}: "Xiaomi Communications Co Ltd", + [3]byte{116, 37, 138}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{116, 38, 172}: "Cisco Systems, Inc", + [3]byte{116, 39, 60}: "ChangYang Technology (Nanjing) Co., LTD", + [3]byte{116, 39, 234}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{116, 41, 175}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{116, 43, 15}: "Infinidat Ltd.", + [3]byte{116, 43, 98}: "FUJITSU LIMITED", + [3]byte{116, 45, 10}: "Norfolk Elektronik AG", + [3]byte{116, 46, 252}: "DirectPacket Research, Inc,", + [3]byte{116, 47, 104}: "AzureWave Technology Inc.", + [3]byte{116, 49, 112}: "Arcadyan Technology Corporation", + [3]byte{116, 50, 86}: "NT-ware Systemprg GmbH", + [3]byte{116, 55, 47}: "Tongfang Shenzhen Cloudcomputing Technology Co.,Ltd", + [3]byte{116, 56, 137}: "ANNAX Anzeigesysteme GmbH", + [3]byte{116, 58, 101}: "NEC Corporation", + [3]byte{116, 62, 43}: "Ruckus Wireless", + [3]byte{116, 62, 203}: "Gentrice tech", + [3]byte{116, 68, 1}: "NETGEAR", + [3]byte{116, 69, 138}: "Samsung Electronics Co.,Ltd", + [3]byte{116, 70, 160}: "Hewlett Packard", + [3]byte{116, 74, 164}: "zte corporation", + [3]byte{116, 75, 233}: "EXPLORER HYPERTECH CO.,LTD", + [3]byte{116, 77, 121}: "Arrive Systems Inc.", + [3]byte{116, 81, 186}: "Xiaomi Communications Co Ltd", + [3]byte{116, 83, 39}: "COMMSEN CO., LIMITED", + [3]byte{116, 84, 125}: "Cisco SPVTG", + [3]byte{116, 86, 18}: "ARRIS Group, Inc.", + [3]byte{116, 87, 152}: "TRUMPF Laser GmbH + Co. KG", + [3]byte{116, 90, 170}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{116, 92, 159}: "TCT mobile ltd", + [3]byte{116, 94, 28}: "PIONEER CORPORATION", + [3]byte{116, 95, 0}: "Samsung Semiconductor Inc.", + [3]byte{116, 95, 174}: "TSL PPL", + [3]byte{116, 97, 75}: "Chongqing Huijiatong Information Technology Co., Ltd.", + [3]byte{116, 99, 223}: "VTS GmbH", + [3]byte{116, 101, 209}: "Atlinks", + [3]byte{116, 102, 48}: "T:mi Ytti", + [3]byte{116, 103, 247}: "Extreme Networks", + [3]byte{116, 106, 58}: "Aperi Corporation", + [3]byte{116, 106, 137}: "Rezolt Corporation", + [3]byte{116, 106, 143}: "VS Vision Systems GmbH", + [3]byte{116, 107, 130}: "MOVEK ", + [3]byte{116, 111, 25}: "ICARVISIONS (SHENZHEN) TECHNOLOGY CO., LTD.", + [3]byte{116, 111, 61}: "Contec GmbH", + [3]byte{116, 111, 247}: "Wistron Neweb Corporation", + [3]byte{116, 114, 176}: "Guangzhou Shiyuan Electronics Co., Ltd. ", + [3]byte{116, 114, 242}: "Chipsip Technology Co., Ltd.", + [3]byte{116, 115, 54}: "MICRODIGTAL Inc", + [3]byte{116, 117, 72}: "Amazon Technologies Inc.", + [3]byte{116, 120, 24}: "Jurumani Solutions", + [3]byte{116, 123, 122}: "ETH Inc.", + [3]byte{116, 125, 182}: "Aliwei Communications, Inc", + [3]byte{116, 126, 26}: "Red Embedded Design Limited", + [3]byte{116, 126, 45}: "Beijing Thomson CITIC Digital Technology Co. LTD.", + [3]byte{116, 129, 20}: "Apple, Inc.", + [3]byte{116, 133, 42}: "PEGATRON CORPORATION", + [3]byte{116, 134, 122}: "Dell Inc.", + [3]byte{116, 135, 169}: "OCT Technology Co., Ltd.", + [3]byte{116, 136, 42}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{116, 136, 139}: "ADB Broadband Italia", + [3]byte{116, 138, 105}: "Korea Image Technology Co., Ltd", + [3]byte{116, 141, 8}: "Apple, Inc.", + [3]byte{116, 142, 8}: "Bestek Corp.", + [3]byte{116, 142, 248}: "Brocade Communications Systems, Inc.", + [3]byte{116, 143, 27}: "MasterImage 3D", + [3]byte{116, 143, 77}: "MEN Mikro Elektronik GmbH", + [3]byte{116, 144, 80}: "Renesas Electronics Corporation", + [3]byte{116, 145, 26}: "Ruckus Wireless", + [3]byte{116, 145, 189}: "Four systems Co.,Ltd.", + [3]byte{116, 147, 164}: "Zebra Technologies Corp.", + [3]byte{116, 148, 61}: "AgJunction", + [3]byte{116, 150, 55}: "Todaair Electronic Co., Ltd", + [3]byte{116, 151, 129}: "zte corporation", + [3]byte{116, 153, 117}: "IBM Corporation", + [3]byte{116, 156, 82}: "Huizhou Desay SV Automotive Co., Ltd.", + [3]byte{116, 156, 227}: "KodaCloud Canada, Inc", + [3]byte{116, 157, 143}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{116, 157, 220}: "2Wire Inc", + [3]byte{116, 160, 47}: "Cisco Systems, Inc", + [3]byte{116, 160, 99}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{116, 162, 230}: "Cisco Systems, Inc", + [3]byte{116, 163, 74}: "ZIMI CORPORATION", + [3]byte{116, 164, 167}: "QRS Music Technologies, Inc.", + [3]byte{116, 164, 181}: "Powerleader Science and Technology Co. Ltd.", + [3]byte{116, 165, 40}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{116, 167, 34}: "LG Electronics (Mobile Communications)", + [3]byte{116, 167, 142}: "zte corporation", + [3]byte{116, 172, 95}: "Qiku Internet Network Scientific (Shenzhen) Co., Ltd.", + [3]byte{116, 173, 183}: "China Mobile Group Device Co.,Ltd.", + [3]byte{116, 174, 118}: "iNovo Broadband, Inc.", + [3]byte{116, 176, 12}: "Network Video Technologies, Inc", + [3]byte{116, 180, 114}: "CIESSE", + [3]byte{116, 181, 126}: "zte corporation", + [3]byte{116, 185, 235}: "JinQianMao Technology Co.,Ltd.", + [3]byte{116, 186, 219}: "Longconn Electornics(shenzhen)Co.,Ltd", + [3]byte{116, 190, 8}: "ATEK Products, LLC", + [3]byte{116, 191, 161}: "HYUNTECK", + [3]byte{116, 191, 183}: "Nusoft Corporation", + [3]byte{116, 194, 70}: "Amazon Technologies Inc.", + [3]byte{116, 195, 48}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{116, 198, 33}: "Zhejiang Hite Renewable Energy Co.,LTD", + [3]byte{116, 198, 59}: "AzureWave Technology Inc.", + [3]byte{116, 201, 154}: "Ericsson AB", + [3]byte{116, 201, 163}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{116, 202, 37}: "Calxeda, Inc.", + [3]byte{116, 204, 57}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{116, 205, 12}: "Smith Myers Communications Ltd.", + [3]byte{116, 206, 86}: "Packet Force Technology Limited Company", + [3]byte{116, 208, 43}: "ASUSTek COMPUTER INC.", + [3]byte{116, 208, 220}: "ERICSSON AB", + [3]byte{116, 212, 53}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{116, 214, 117}: "WYMA Tecnologia", + [3]byte{116, 214, 234}: "Texas Instruments", + [3]byte{116, 215, 202}: "Panasonic Corporation Automotive", + [3]byte{116, 216, 80}: "Evrisko Systems", + [3]byte{116, 218, 56}: "Edimax Technology Co. Ltd.", + [3]byte{116, 218, 234}: "Texas Instruments", + [3]byte{116, 219, 209}: "Ebay Inc", + [3]byte{116, 222, 43}: "Liteon Technology Corporation", + [3]byte{116, 223, 191}: "Liteon Technology Corporation", + [3]byte{116, 224, 110}: "Ergophone GmbH", + [3]byte{116, 225, 74}: "IEEE Registration Authority", + [3]byte{116, 225, 182}: "Apple, Inc.", + [3]byte{116, 226, 119}: "Vizmonet Pte Ltd", + [3]byte{116, 226, 140}: "Microsoft Corporation", + [3]byte{116, 226, 245}: "Apple, Inc.", + [3]byte{116, 228, 36}: "APISTE CORPORATION", + [3]byte{116, 229, 11}: "Intel Corporate", + [3]byte{116, 229, 55}: "RADSPIN", + [3]byte{116, 229, 67}: "Liteon Technology Corporation", + [3]byte{116, 230, 226}: "Dell Inc.", + [3]byte{116, 231, 198}: "ARRIS Group, Inc.", + [3]byte{116, 234, 58}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{116, 234, 232}: "ARRIS Group, Inc.", + [3]byte{116, 236, 241}: "Acumen", + [3]byte{116, 240, 109}: "AzureWave Technology Inc.", + [3]byte{116, 240, 125}: "BnCOM Co.,Ltd", + [3]byte{116, 241, 2}: "Beijing HCHCOM Technology Co., Ltd", + [3]byte{116, 244, 19}: "Maxwell Forest", + [3]byte{116, 246, 18}: "ARRIS Group, Inc.", + [3]byte{116, 247, 38}: "Neuron Robotics", + [3]byte{116, 248, 93}: "Berkeley Nucleonics Corp", + [3]byte{116, 248, 219}: "IEEE Registration Authority", + [3]byte{116, 253, 160}: "Compupal (Group) Corporation ", + [3]byte{116, 254, 72}: "ADVANTECH CO., LTD.", + [3]byte{116, 255, 76}: "Skyworth Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{116, 255, 125}: "Wren Sound Systems, LLC", + [3]byte{120, 0, 158}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 2, 143}: "Adaptive Spectrum and Signal Alignment (ASSIA), Inc.", + [3]byte{120, 2, 183}: "ShenZhen Ultra Easy Technology CO.,LTD", + [3]byte{120, 2, 248}: "Xiaomi Communications Co Ltd", + [3]byte{120, 5, 65}: "Queclink Wireless Solutions Co., Ltd", + [3]byte{120, 7, 56}: "Z.U.K. Elzab S.A.", + [3]byte{120, 10, 199}: "Baofeng TV Co., Ltd.", + [3]byte{120, 12, 184}: "Intel Corporate", + [3]byte{120, 17, 133}: "NBS Payment Solutions Inc.", + [3]byte{120, 18, 184}: "ORANTEK LIMITED", + [3]byte{120, 24, 129}: "AzureWave Technology Inc.", + [3]byte{120, 25, 46}: "NASCENT Technology", + [3]byte{120, 25, 247}: "Juniper Networks", + [3]byte{120, 28, 90}: "SHARP Corporation", + [3]byte{120, 29, 186}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{120, 29, 253}: "Jabil Inc", + [3]byte{120, 31, 219}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 32, 121}: "ID Tech", + [3]byte{120, 34, 61}: "Affirmed Networks", + [3]byte{120, 35, 174}: "ARRIS Group, Inc.", + [3]byte{120, 36, 175}: "ASUSTek COMPUTER INC.", + [3]byte{120, 37, 68}: "Omnima Limited", + [3]byte{120, 37, 173}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 40, 202}: "Sonos, Inc.", + [3]byte{120, 43, 203}: "Dell Inc.", + [3]byte{120, 46, 239}: "Nokia Corporation", + [3]byte{120, 48, 59}: "Stephen Technologies Co.,Limited", + [3]byte{120, 48, 225}: "UltraClenz, LLC", + [3]byte{120, 49, 43}: "zte corporation", + [3]byte{120, 49, 193}: "Apple, Inc.", + [3]byte{120, 50, 79}: "Millennium Group, Inc.", + [3]byte{120, 58, 132}: "Apple, Inc.", + [3]byte{120, 60, 227}: "Kai-EE", + [3]byte{120, 61, 91}: "TELNET Redes Inteligentes S.A.", + [3]byte{120, 62, 83}: "BSkyB Ltd", + [3]byte{120, 63, 21}: "EasySYNC Ltd.", + [3]byte{120, 64, 228}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 68, 5}: "FUJITU(HONG KONG) ELECTRONIC Co.,LTD.", + [3]byte{120, 68, 118}: "Zioncom Electronics (Shenzhen) Ltd.", + [3]byte{120, 69, 97}: "CyberTAN Technology Inc.", + [3]byte{120, 69, 196}: "Dell Inc.", + [3]byte{120, 70, 196}: "DAEHAP HYPER-TECH", + [3]byte{120, 71, 29}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 72, 89}: "Hewlett Packard", + [3]byte{120, 73, 29}: "The Will-Burt Company", + [3]byte{120, 75, 8}: "f.robotics acquisitions ltd", + [3]byte{120, 75, 135}: "Murata Manufacturing Co., Ltd.", + [3]byte{120, 79, 67}: "Apple, Inc.", + [3]byte{120, 81, 12}: "LiveU Ltd.", + [3]byte{120, 82, 26}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 82, 98}: "Shenzhen Hojy Software Co., Ltd.", + [3]byte{120, 83, 242}: "ROXTON Ltd.", + [3]byte{120, 84, 46}: "D-Link International", + [3]byte{120, 85, 23}: "SankyuElectronics", + [3]byte{120, 87, 18}: "Mobile Integration Workgroup", + [3]byte{120, 88, 243}: "Vachen Co.,Ltd", + [3]byte{120, 89, 62}: "RAFI GmbH & Co.KG", + [3]byte{120, 89, 94}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 89, 104}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{120, 92, 114}: "Hioso Technology Co., Ltd.", + [3]byte{120, 95, 76}: "Argox Information Co., Ltd.", + [3]byte{120, 97, 124}: "MITSUMI ELECTRIC CO.,LTD ", + [3]byte{120, 100, 230}: "Green Motive Technology Limited", + [3]byte{120, 102, 174}: "ZTEC Instruments, Inc.", + [3]byte{120, 104, 247}: "YSTen Technology Co.,Ltd", + [3]byte{120, 106, 137}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{120, 108, 28}: "Apple, Inc.", + [3]byte{120, 113, 156}: "ARRIS Group, Inc.", + [3]byte{120, 125, 72}: "ITEL MOBILE LIMITED", + [3]byte{120, 126, 97}: "Apple, Inc.", + [3]byte{120, 127, 98}: "GiK mbH", + [3]byte{120, 129, 143}: "Server Racks Australia Pty Ltd", + [3]byte{120, 132, 60}: "Sony Corporation", + [3]byte{120, 132, 238}: "INDRA ESPACIO S.A.", + [3]byte{120, 136, 138}: "CDR Sp. z o.o. Sp. k.", + [3]byte{120, 137, 115}: "CMC", + [3]byte{120, 138, 32}: "Ubiquiti Networks Inc.", + [3]byte{120, 139, 119}: "Standar Telecom", + [3]byte{120, 140, 84}: "Eltek Technologies LTD", + [3]byte{120, 141, 247}: "Hitron Technologies. Inc", + [3]byte{120, 142, 51}: "Jiangsu SEUIC Technology Co.,Ltd", + [3]byte{120, 146, 62}: "Nokia Corporation", + [3]byte{120, 146, 156}: "Intel Corporate", + [3]byte{120, 148, 180}: "Sercomm Corporation.", + [3]byte{120, 150, 130}: "zte corporation", + [3]byte{120, 150, 132}: "ARRIS Group, Inc.", + [3]byte{120, 152, 253}: "Q9 Networks Inc.", + [3]byte{120, 153, 92}: "Nationz Technologies Inc", + [3]byte{120, 153, 102}: "Musilab Electronics (DongGuan)Co.,Ltd.", + [3]byte{120, 153, 143}: "MEDILINE ITALIA SRL", + [3]byte{120, 156, 133}: "August Home, Inc.", + [3]byte{120, 156, 231}: "Shenzhen Aikede Technology Co., Ltd", + [3]byte{120, 158, 208}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 159, 76}: "HOERBIGER Elektronik GmbH", + [3]byte{120, 159, 112}: "Apple, Inc.", + [3]byte{120, 159, 135}: "Siemens AG I IA PP PRM", + [3]byte{120, 160, 81}: "iiNet Labs Pty Ltd ", + [3]byte{120, 161, 6}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{120, 161, 131}: "Advidia", + [3]byte{120, 162, 160}: "Nintendo Co., Ltd.", + [3]byte{120, 163, 81}: "SHENZHEN ZHIBOTONG ELECTRONICS CO.,LTD", + [3]byte{120, 163, 228}: "Apple, Inc.", + [3]byte{120, 165, 4}: "Texas Instruments", + [3]byte{120, 165, 221}: "Shenzhen Smarteye Digital Electronics Co., Ltd", + [3]byte{120, 166, 131}: "Precidata", + [3]byte{120, 166, 189}: "DAEYEON Control&Instrument Co,.Ltd", + [3]byte{120, 167, 20}: "Amphenol", + [3]byte{120, 168, 115}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 171, 96}: "ABB Australia", + [3]byte{120, 171, 187}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 172, 191}: "Igneous Systems", + [3]byte{120, 172, 192}: "Hewlett Packard", + [3]byte{120, 174, 12}: "Far South Networks", + [3]byte{120, 175, 88}: "GIMASI SA", + [3]byte{120, 179, 185}: "ShangHai sunup lighting CO.,LTD", + [3]byte{120, 179, 206}: "Elo touch solutions", + [3]byte{120, 181, 210}: "Ever Treasure Industrial Limited", + [3]byte{120, 182, 193}: "AOBO Telecom Co.,Ltd", + [3]byte{120, 184, 26}: "INTER SALES A/S", + [3]byte{120, 184, 75}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{120, 186, 208}: "Shinybow Technology Co. Ltd.", + [3]byte{120, 186, 249}: "Cisco Systems, Inc", + [3]byte{120, 189, 188}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 190, 182}: "Enhanced Vision", + [3]byte{120, 190, 189}: "STULZ GmbH", + [3]byte{120, 193, 167}: "zte corporation", + [3]byte{120, 194, 192}: "IEEE Registration Authority", + [3]byte{120, 195, 233}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 196, 14}: "H&D Wireless", + [3]byte{120, 196, 171}: "Shenzhen Runsil Technology Co.,Ltd", + [3]byte{120, 197, 229}: "Texas Instruments", + [3]byte{120, 198, 187}: "Innovasic, Inc.", + [3]byte{120, 202, 4}: "Nokia Corporation", + [3]byte{120, 202, 57}: "Apple, Inc.", + [3]byte{120, 202, 94}: "ELNO", + [3]byte{120, 202, 131}: "IEEE Registration Authority", + [3]byte{120, 203, 51}: "DHC Software Co.,Ltd", + [3]byte{120, 203, 104}: "DAEHAP HYPER-TECH", + [3]byte{120, 205, 142}: "SMC Networks Inc", + [3]byte{120, 208, 4}: "Neousys Technology Inc.", + [3]byte{120, 209, 41}: "Vicos", + [3]byte{120, 211, 79}: "Pace-O-Matic, Inc.", + [3]byte{120, 211, 141}: "HONGKONG YUNLINK TECHNOLOGY LIMITED", + [3]byte{120, 213, 181}: "NAVIELEKTRO KY", + [3]byte{120, 214, 111}: "Aristocrat Technologies Australia Pty. Ltd.", + [3]byte{120, 214, 178}: "Toshiba", + [3]byte{120, 214, 240}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{120, 215, 82}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{120, 215, 95}: "Apple, Inc.", + [3]byte{120, 217, 159}: "NuCom HK Ltd.", + [3]byte{120, 218, 110}: "Cisco Systems, Inc", + [3]byte{120, 218, 179}: "GBO Technology", + [3]byte{120, 221, 8}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{120, 221, 214}: "c-scape", + [3]byte{120, 222, 228}: "Texas Instruments", + [3]byte{120, 227, 181}: "Hewlett Packard", + [3]byte{120, 228, 0}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{120, 231, 209}: "Hewlett Packard", + [3]byte{120, 232, 182}: "zte corporation", + [3]byte{120, 233, 128}: "RainUs Co.,Ltd", + [3]byte{120, 235, 20}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{120, 235, 57}: "Instituto Nacional de Tecnología Industrial", + [3]byte{120, 236, 34}: "Shanghai Qihui Telecom Technology Co., LTD", + [3]byte{120, 236, 116}: "Kyland-USA", + [3]byte{120, 239, 76}: "Unetconvergence Co., Ltd.", + [3]byte{120, 242, 158}: "PEGATRON CORPORATION", + [3]byte{120, 245, 87}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{120, 245, 229}: "BEGA Gantenbrink-Leuchten KG", + [3]byte{120, 245, 253}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{120, 247, 190}: "Samsung Electronics Co.,Ltd", + [3]byte{120, 247, 208}: "Silverbrook Research", + [3]byte{120, 248, 130}: "LG Electronics (Mobile Communications)", + [3]byte{120, 249, 68}: "Private", + [3]byte{120, 252, 20}: "Family Zone Cyber Safety Ltd ", + [3]byte{120, 253, 148}: "Apple, Inc.", + [3]byte{120, 254, 61}: "Juniper Networks", + [3]byte{120, 254, 65}: "Socus networks", + [3]byte{120, 254, 226}: "Shanghai Diveo Technology Co., Ltd", + [3]byte{120, 255, 87}: "Intel Corporate", + [3]byte{120, 255, 202}: "TECNO MOBILE LIMITED", + [3]byte{124, 1, 135}: "Curtis Instruments, Inc.", + [3]byte{124, 1, 145}: "Apple, Inc.", + [3]byte{124, 2, 188}: "Hansung Electronics Co. LTD", + [3]byte{124, 3, 76}: "Sagemcom Broadband SAS", + [3]byte{124, 3, 201}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{124, 3, 216}: "Sagemcom Broadband SAS", + [3]byte{124, 4, 208}: "Apple, Inc.", + [3]byte{124, 5, 7}: "PEGATRON CORPORATION", + [3]byte{124, 5, 30}: "RAFAEL LTD.", + [3]byte{124, 6, 35}: "Ultra Electronics Sonar System Division", + [3]byte{124, 8, 217}: "Shanghai B-Star Technology Co", + [3]byte{124, 9, 43}: "Bekey A/S", + [3]byte{124, 10, 80}: "J-MEX Inc.", + [3]byte{124, 11, 198}: "Samsung Electronics Co.,Ltd", + [3]byte{124, 14, 206}: "Cisco Systems, Inc", + [3]byte{124, 16, 21}: "Brilliant Home Technology, Inc.", + [3]byte{124, 17, 190}: "Apple, Inc.", + [3]byte{124, 17, 203}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{124, 17, 205}: "QianTang Technology", + [3]byte{124, 20, 118}: "Damall Technologies SAS", + [3]byte{124, 22, 13}: "Saia-Burgess Controls AG", + [3]byte{124, 24, 205}: "E-TRON Co.,Ltd.", + [3]byte{124, 26, 3}: "8Locations Co., Ltd.", + [3]byte{124, 26, 252}: "Dalian Co-Edifice Video Technology Co., Ltd", + [3]byte{124, 28, 241}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{124, 29, 217}: "Xiaomi Communications Co Ltd", + [3]byte{124, 30, 82}: "Microsoft", + [3]byte{124, 30, 179}: "2N TELEKOMUNIKACE a.s.", + [3]byte{124, 32, 72}: "KoamTac", + [3]byte{124, 32, 100}: "Alcatel-Lucent IPD", + [3]byte{124, 37, 135}: "chaowifi.com", + [3]byte{124, 38, 52}: "ARRIS Group, Inc.", + [3]byte{124, 38, 100}: "Sagemcom Broadband SAS", + [3]byte{124, 43, 225}: "Shenzhen Ferex Electrical Co.,Ltd", + [3]byte{124, 44, 243}: "Secure Electrans Ltd", + [3]byte{124, 46, 13}: "Blackmagic Design", + [3]byte{124, 47, 128}: "Gigaset Communications GmbH", + [3]byte{124, 51, 110}: "MEG Electronics Inc.", + [3]byte{124, 53, 72}: "Transcend Information", + [3]byte{124, 56, 102}: "Texas Instruments", + [3]byte{124, 56, 108}: "Real Time Logic", + [3]byte{124, 57, 32}: "SSOMA SECURITY", + [3]byte{124, 59, 213}: "Imago Group", + [3]byte{124, 60, 182}: "Shenzhen Homecare Technology Co.,Ltd.", + [3]byte{124, 62, 157}: "PATECH", + [3]byte{124, 67, 143}: "E-Band Communications Corp.", + [3]byte{124, 68, 76}: "Entertainment Solutions, S.L.", + [3]byte{124, 70, 133}: "Motorola (Wuhan) Mobility Technologies Communication Co., Ltd.", + [3]byte{124, 71, 124}: "IEEE Registration Authority", + [3]byte{124, 73, 185}: "Plexus Manufacturing Sdn Bhd", + [3]byte{124, 74, 130}: "Portsmith LLC", + [3]byte{124, 74, 168}: "MindTree Wireless PVT Ltd", + [3]byte{124, 75, 120}: "Red Sun Synthesis Pte Ltd", + [3]byte{124, 76, 88}: "Scale Computing, Inc.", + [3]byte{124, 76, 165}: "BSkyB Ltd", + [3]byte{124, 79, 125}: "Sawwave", + [3]byte{124, 79, 181}: "Arcadyan Technology Corporation", + [3]byte{124, 80, 73}: "Apple, Inc.", + [3]byte{124, 83, 74}: "Metamako", + [3]byte{124, 85, 231}: "YSI, Inc.", + [3]byte{124, 87, 78}: "COBI GmbH", + [3]byte{124, 90, 28}: "Sophos Ltd", + [3]byte{124, 90, 103}: "JNC Systems, Inc.", + [3]byte{124, 92, 248}: "Intel Corporate", + [3]byte{124, 96, 151}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{124, 97, 147}: "HTC Corporation", + [3]byte{124, 102, 157}: "Texas Instruments", + [3]byte{124, 103, 162}: "Intel Corporate", + [3]byte{124, 105, 246}: "Cisco Systems, Inc", + [3]byte{124, 106, 179}: "IBC TECHNOLOGIES INC.", + [3]byte{124, 106, 195}: "GatesAir, Inc", + [3]byte{124, 106, 219}: "SafeTone Technology Co.,Ltd", + [3]byte{124, 106, 243}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{124, 107, 51}: "Tenyu Tech Co. Ltd.", + [3]byte{124, 107, 82}: "Tigaro Wireless", + [3]byte{124, 107, 247}: "NTI co., ltd.", + [3]byte{124, 108, 57}: "PIXSYS SRL", + [3]byte{124, 108, 143}: "AMS NEVE LTD", + [3]byte{124, 109, 98}: "Apple, Inc.", + [3]byte{124, 109, 248}: "Apple, Inc.", + [3]byte{124, 111, 6}: "Caterpillar Trimble Control Technologies", + [3]byte{124, 111, 248}: "ShenZhen ACTO Digital Video Technology Co.,Ltd.", + [3]byte{124, 112, 188}: "IEEE Registration Authority", + [3]byte{124, 113, 118}: "Wuxi iData Technology Company Ltd.", + [3]byte{124, 114, 228}: "Unikey Technologies", + [3]byte{124, 115, 139}: "Cocoon Alarm Ltd", + [3]byte{124, 118, 115}: "ENMAS GmbH", + [3]byte{124, 120, 126}: "Samsung Electronics Co.,Ltd", + [3]byte{124, 121, 232}: "PayRange Inc.", + [3]byte{124, 122, 83}: "Phytrex Technology Corp.", + [3]byte{124, 122, 145}: "Intel Corporate", + [3]byte{124, 123, 139}: "Control Concepts, Inc.", + [3]byte{124, 123, 228}: "Z'SEDAI KENKYUSHO CORPORATION", + [3]byte{124, 125, 61}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{124, 125, 65}: "Jinmuyu Electronics Co., Ltd.", + [3]byte{124, 130, 45}: "Nortec", + [3]byte{124, 130, 116}: "Shenzhen Hikeen Technology CO.,LTD", + [3]byte{124, 131, 6}: "Glen Dimplex Nordic as", + [3]byte{124, 141, 145}: "Shanghai Hongzhuo Information Technology co.,LTD", + [3]byte{124, 142, 228}: "Texas Instruments", + [3]byte{124, 145, 34}: "Samsung Electronics Co.,Ltd", + [3]byte{124, 148, 178}: "Philips Healthcare PCCI", + [3]byte{124, 149, 177}: "Aerohive Networks Inc.", + [3]byte{124, 149, 243}: "Cisco Systems, Inc", + [3]byte{124, 151, 99}: "Openmatics s.r.o.", + [3]byte{124, 154, 155}: "VSE valencia smart energy", + [3]byte{124, 161, 93}: "GN ReSound A/S", + [3]byte{124, 162, 55}: "King Slide Technology CO., LTD.", + [3]byte{124, 162, 62}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{124, 162, 155}: "D.SignT GmbH & Co. KG", + [3]byte{124, 166, 29}: "MHL, LLC", + [3]byte{124, 169, 125}: "Objenious", + [3]byte{124, 171, 37}: "MESMO TECHNOLOGY INC.", + [3]byte{124, 172, 178}: "Bosch Software Innovations GmbH", + [3]byte{124, 173, 116}: "Cisco Systems, Inc", + [3]byte{124, 176, 62}: "OSRAM GmbH", + [3]byte{124, 176, 194}: "Intel Corporate", + [3]byte{124, 177, 93}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{124, 177, 119}: "Satelco AG", + [3]byte{124, 178, 27}: "Cisco SPVTG", + [3]byte{124, 178, 50}: "Hui Zhou Gaoshengda Technology Co.,LTD", + [3]byte{124, 178, 92}: "Acacia Communications", + [3]byte{124, 181, 66}: "ACES Technology", + [3]byte{124, 183, 51}: "ASKEY COMPUTER CORP", + [3]byte{124, 183, 123}: "Paradigm Electronics Inc", + [3]byte{124, 185, 96}: "Shanghai X-Cheng telecom LTD", + [3]byte{124, 187, 111}: "Cosco Electronics Co., Ltd.", + [3]byte{124, 187, 138}: "Nintendo Co., Ltd.", + [3]byte{124, 189, 6}: "AE REFUsol", + [3]byte{124, 191, 136}: "Mobilicom LTD", + [3]byte{124, 191, 177}: "ARRIS Group, Inc.", + [3]byte{124, 195, 161}: "Apple, Inc.", + [3]byte{124, 196, 239}: "Devialet", + [3]byte{124, 197, 55}: "Apple, Inc.", + [3]byte{124, 198, 196}: "Kolff Computer Supplies b.v.", + [3]byte{124, 199, 9}: "SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.", + [3]byte{124, 200, 171}: "Acro Associates, Inc.", + [3]byte{124, 200, 208}: "TIANJIN YAAN TECHNOLOGY CO., LTD.", + [3]byte{124, 200, 215}: "Damalisk", + [3]byte{124, 201, 90}: "EMC", + [3]byte{124, 203, 13}: "Antaira Technologies, LLC", + [3]byte{124, 203, 226}: "IEEE Registration Authority", + [3]byte{124, 204, 31}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{124, 204, 184}: "Intel Corporate", + [3]byte{124, 205, 17}: "MS-Magnet", + [3]byte{124, 205, 60}: "Guangzhou Juzing Technology Co., Ltd", + [3]byte{124, 207, 207}: "Shanghai SEARI Intelligent System Co., Ltd", + [3]byte{124, 209, 195}: "Apple, Inc.", + [3]byte{124, 211, 10}: "INVENTEC Corporation", + [3]byte{124, 215, 98}: "Freestyle Technology Pty Ltd", + [3]byte{124, 216, 68}: "Enmotus Inc", + [3]byte{124, 217, 254}: "New Cosmos Electric Co., Ltd.", + [3]byte{124, 218, 132}: "Dongnian Networks Inc.", + [3]byte{124, 221, 17}: "Chongqing MAS SCI&TECH.Co.,Ltd", + [3]byte{124, 221, 32}: "IOXOS Technologies S.A.", + [3]byte{124, 221, 144}: "Shenzhen Ogemray Technology Co., Ltd.", + [3]byte{124, 224, 68}: "NEON Inc", + [3]byte{124, 225, 255}: "Computer Performance, Inc. DBA Digital Loggers, Inc.", + [3]byte{124, 228, 170}: "Private", + [3]byte{124, 229, 36}: "Quirky, Inc.", + [3]byte{124, 229, 107}: "ESEN Optoelectronics Technology Co.,Ltd.", + [3]byte{124, 233, 124}: "ITEL MOBILE LIMITED", + [3]byte{124, 233, 211}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{124, 235, 174}: "Ridgeline Instruments", + [3]byte{124, 235, 234}: "ASCT", + [3]byte{124, 236, 121}: "Texas Instruments", + [3]byte{124, 237, 141}: "Microsoft", + [3]byte{124, 239, 24}: "Creative Product Design Pty. Ltd.", + [3]byte{124, 239, 138}: "Inhon International Ltd.", + [3]byte{124, 240, 95}: "Apple, Inc.", + [3]byte{124, 240, 152}: "Bee Beans Technologies, Inc.", + [3]byte{124, 240, 186}: "Linkwell Telesystems Pvt Ltd", + [3]byte{124, 244, 41}: "NUUO Inc. ", + [3]byte{124, 248, 84}: "Samsung Electronics Co.,Ltd", + [3]byte{124, 249, 14}: "Samsung Electronics Co.,Ltd", + [3]byte{124, 249, 92}: "U.I. Lapp GmbH", + [3]byte{124, 250, 223}: "Apple, Inc.", + [3]byte{124, 252, 60}: "Visteon Corporation", + [3]byte{124, 254, 40}: "Salutron Inc.", + [3]byte{124, 254, 78}: "Shenzhen Safe vision Technology Co.,LTD", + [3]byte{124, 254, 144}: "Mellanox Technologies, Inc.", + [3]byte{124, 255, 98}: "Huizhou Super Electron Technology Co.,Ltd.", + [3]byte{128, 0, 11}: "Intel Corporate", + [3]byte{128, 0, 16}: "AT&T", + [3]byte{128, 0, 110}: "Apple, Inc.", + [3]byte{128, 1, 132}: "HTC Corporation", + [3]byte{128, 2, 223}: "ORA Inc.", + [3]byte{128, 5, 223}: "Montage Technology Group Limited", + [3]byte{128, 7, 162}: "Esson Technology Inc.", + [3]byte{128, 9, 2}: "Keysight Technologies, Inc.", + [3]byte{128, 10, 6}: "COMTEC co.,ltd", + [3]byte{128, 10, 128}: "IEEE Registration Authority", + [3]byte{128, 11, 81}: "Chengdu XGimi Technology Co.,Ltd", + [3]byte{128, 13, 215}: "Latticework, Inc", + [3]byte{128, 14, 36}: "ForgetBox", + [3]byte{128, 19, 130}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{128, 20, 64}: "Sunlit System Technology Corp", + [3]byte{128, 20, 168}: "Guangzhou V-SOLUTION Electronic Technology Co., Ltd.", + [3]byte{128, 22, 183}: "Brunel University", + [3]byte{128, 23, 125}: "Nortel Networks", + [3]byte{128, 24, 68}: "Dell Inc.", + [3]byte{128, 24, 167}: "Samsung Electronics Co.,Ltd", + [3]byte{128, 25, 52}: "Intel Corporate", + [3]byte{128, 25, 103}: "Shanghai Reallytek Information Technology Co.,Ltd", + [3]byte{128, 25, 254}: "JianLing Technology CO., LTD", + [3]byte{128, 29, 170}: "Avaya Inc", + [3]byte{128, 31, 2}: "Edimax Technology Co. Ltd.", + [3]byte{128, 32, 175}: "Trade FIDES, a.s.", + [3]byte{128, 34, 117}: "Beijing Beny Wave Technology Co Ltd", + [3]byte{128, 38, 137}: "D-Link International", + [3]byte{128, 41, 148}: "Technicolor CH USA Inc.", + [3]byte{128, 42, 168}: "Ubiquiti Networks Inc.", + [3]byte{128, 42, 250}: "Germaneers GmbH", + [3]byte{128, 45, 225}: "Solarbridge Technologies", + [3]byte{128, 46, 20}: "azeti Networks AG", + [3]byte{128, 47, 222}: "Zurich Instruments AG", + [3]byte{128, 48, 220}: "Texas Instruments", + [3]byte{128, 52, 87}: "OT Systems Limited", + [3]byte{128, 55, 115}: "NETGEAR", + [3]byte{128, 56, 150}: "SHARP Corporation", + [3]byte{128, 56, 188}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{128, 56, 253}: "LeapFrog Enterprises, Inc.", + [3]byte{128, 57, 229}: "PATLITE CORPORATION", + [3]byte{128, 58, 10}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{128, 59, 42}: "ABB Xiamen Low Voltage Equipment Co.,Ltd.", + [3]byte{128, 59, 154}: "ghe-ces electronic ag", + [3]byte{128, 63, 93}: "Winstars Technology Ltd", + [3]byte{128, 63, 214}: "bytes at work AG", + [3]byte{128, 65, 78}: "BBK EDUCATIONAL ELECTRONICS CORP.,LTD.", + [3]byte{128, 66, 124}: "Adolf Tedsen GmbH & Co. KG", + [3]byte{128, 71, 49}: "Packet Design, Inc.", + [3]byte{128, 72, 165}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{128, 73, 113}: "Apple, Inc.", + [3]byte{128, 75, 32}: "Ventilation Control", + [3]byte{128, 78, 129}: "Samsung Electronics Co.,Ltd", + [3]byte{128, 79, 88}: "ThinkEco, Inc.", + [3]byte{128, 80, 27}: "Nokia Corporation", + [3]byte{128, 80, 103}: "W & D TECHNOLOGY CORPORATION", + [3]byte{128, 86, 242}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{128, 87, 25}: "Samsung Electronics Co.,Ltd", + [3]byte{128, 88, 197}: "NovaTec Kommunikationstechnik GmbH", + [3]byte{128, 88, 248}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{128, 89, 253}: "Noviga", + [3]byte{128, 90, 4}: "LG Electronics (Mobile Communications)", + [3]byte{128, 94, 192}: "YEALINK(XIAMEN) NETWORK TECHNOLOGY CO.,LTD.", + [3]byte{128, 96, 7}: "RIM", + [3]byte{128, 97, 143}: "Shenzhen sangfei consumer communications co.,ltd", + [3]byte{128, 100, 89}: "Nimbus Inc.", + [3]byte{128, 101, 109}: "Samsung Electronics Co.,Ltd", + [3]byte{128, 101, 233}: "BenQ Corporation", + [3]byte{128, 102, 41}: "Prescope Technologies CO.,LTD.", + [3]byte{128, 106, 176}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{128, 108, 27}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{128, 108, 139}: "KAESER KOMPRESSOREN AG", + [3]byte{128, 108, 188}: "NET New Electronic Technology GmbH", + [3]byte{128, 113, 31}: "Juniper Networks", + [3]byte{128, 113, 122}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{128, 115, 159}: "KYOCERA Corporation", + [3]byte{128, 116, 89}: "K's Co.,Ltd.", + [3]byte{128, 118, 147}: "Newag SA", + [3]byte{128, 121, 174}: "ShanDong Tecsunrise Co.,Ltd", + [3]byte{128, 122, 127}: "ABB Genway Xiamen Electrical Equipment CO., LTD", + [3]byte{128, 122, 191}: "HTC Corporation", + [3]byte{128, 123, 30}: "Corsair Components", + [3]byte{128, 123, 133}: "IEEE Registration Authority", + [3]byte{128, 125, 27}: "Neosystem Co. Ltd.", + [3]byte{128, 125, 227}: "Chongqing Sichuan Instrument Microcircuit Co.LTD.", + [3]byte{128, 129, 165}: "TONGQING COMMUNICATION EQUIPMENT (SHENZHEN) Co.,Ltd", + [3]byte{128, 130, 135}: "ATCOM Technology Co.Ltd.", + [3]byte{128, 134, 152}: "Netronics Technologies Inc.", + [3]byte{128, 134, 242}: "Intel Corporate", + [3]byte{128, 137, 23}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{128, 139, 92}: "Shenzhen Runhuicheng Technology Co., Ltd", + [3]byte{128, 140, 151}: "Kaonmedia CO., LTD.", + [3]byte{128, 145, 42}: "Lih Rong electronic Enterprise Co., Ltd.", + [3]byte{128, 145, 192}: "AgileMesh, Inc.", + [3]byte{128, 146, 159}: "Apple, Inc.", + [3]byte{128, 147, 147}: "Xapt GmbH", + [3]byte{128, 148, 108}: "TOKYO RADAR CORPORATION", + [3]byte{128, 150, 177}: "ARRIS Group, Inc.", + [3]byte{128, 150, 202}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{128, 151, 27}: "Altenergy Power System,Inc.", + [3]byte{128, 155, 32}: "Intel Corporate", + [3]byte{128, 159, 171}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{128, 161, 171}: "Intellisis", + [3]byte{128, 161, 215}: "Shanghai DareGlobal Technologies Co.,Ltd", + [3]byte{128, 165, 137}: "AzureWave Technology Inc.", + [3]byte{128, 168, 93}: "Osterhout Design Group", + [3]byte{128, 170, 164}: "USAG", + [3]byte{128, 172, 172}: "Juniper Networks", + [3]byte{128, 173, 103}: "Kasda Networks Inc", + [3]byte{128, 178, 25}: "ELEKTRON TECHNOLOGY UK LIMITED", + [3]byte{128, 178, 137}: "Forworld Electronics Ltd.", + [3]byte{128, 179, 42}: "Alstom Grid", + [3]byte{128, 182, 134}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{128, 183, 9}: "Viptela, Inc", + [3]byte{128, 185, 92}: "ELFTECH Co., Ltd.", + [3]byte{128, 186, 172}: "TeleAdapt Ltd", + [3]byte{128, 186, 230}: "Neets", + [3]byte{128, 187, 235}: "Satmap Systems Ltd", + [3]byte{128, 190, 5}: "Apple, Inc.", + [3]byte{128, 193, 110}: "Hewlett Packard", + [3]byte{128, 197, 230}: "Microsoft Corporation", + [3]byte{128, 198, 63}: "Remec Broadband Wireless , LLC", + [3]byte{128, 198, 171}: "Technicolor CH USA Inc.", + [3]byte{128, 198, 202}: "Endian s.r.l.", + [3]byte{128, 200, 98}: "Openpeak, Inc", + [3]byte{128, 206, 177}: "Theissen Training Systems GmbH", + [3]byte{128, 207, 65}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{128, 208, 25}: "Embed, Inc", + [3]byte{128, 208, 155}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{128, 209, 96}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{128, 209, 139}: "Hangzhou I'converge Technology Co.,Ltd", + [3]byte{128, 210, 29}: "AzureWave Technology Inc.", + [3]byte{128, 212, 51}: "LzLabs GmbH", + [3]byte{128, 212, 165}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{128, 214, 5}: "Apple, Inc.", + [3]byte{128, 215, 51}: "QSR Automations, Inc.", + [3]byte{128, 219, 49}: "Power Quotient International Co., Ltd.", + [3]byte{128, 224, 29}: "Cisco Systems, Inc", + [3]byte{128, 228, 218}: "IEEE Registration Authority", + [3]byte{128, 230, 80}: "Apple, Inc.", + [3]byte{128, 232, 111}: "Cisco Systems, Inc", + [3]byte{128, 234, 35}: "Wistron Neweb Corporation", + [3]byte{128, 234, 150}: "Apple, Inc.", + [3]byte{128, 234, 202}: "Dialog Semiconductor Hellas SA", + [3]byte{128, 235, 119}: "Wistron Corporation", + [3]byte{128, 237, 44}: "Apple, Inc.", + [3]byte{128, 238, 115}: "Shuttle Inc.", + [3]byte{128, 242, 94}: "Kyynel", + [3]byte{128, 245, 3}: "ARRIS Group, Inc.", + [3]byte{128, 245, 147}: "IRCO Sistemas de Telecomunicación S.A.", + [3]byte{128, 246, 46}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{128, 248, 235}: "RayTight", + [3]byte{128, 250, 91}: "CLEVO CO.", + [3]byte{128, 251, 6}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{128, 255, 168}: "UNIDIS", + [3]byte{132, 0, 45}: "PEGATRON CORPORATION", + [3]byte{132, 0, 210}: "Sony Mobile Communications AB", + [3]byte{132, 1, 167}: "Greyware Automation Products, Inc", + [3]byte{132, 4, 210}: "Kirale Technologies SL", + [3]byte{132, 11, 45}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{132, 15, 69}: "Shanghai GMT Digital Technologies Co., Ltd", + [3]byte{132, 16, 13}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{132, 17, 158}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 22, 249}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{132, 23, 21}: "GP Electronics (HK) Ltd.", + [3]byte{132, 23, 102}: "Weifang GoerTek Electronics Co., Ltd", + [3]byte{132, 24, 38}: "Osram GmbH", + [3]byte{132, 24, 58}: "Ruckus Wireless", + [3]byte{132, 24, 136}: "Juniper Networks", + [3]byte{132, 27, 56}: "Shenzhen Excelsecu Data Technology Co.,Ltd", + [3]byte{132, 27, 94}: "NETGEAR", + [3]byte{132, 30, 38}: "KERNEL-I Co.,LTD", + [3]byte{132, 33, 65}: "Shenzhen Ginwave Technologies Ltd.", + [3]byte{132, 33, 241}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 36, 141}: "Zebra Technologies Inc", + [3]byte{132, 37, 25}: "Samsung Electronics", + [3]byte{132, 37, 63}: "Silex Technology, Inc", + [3]byte{132, 37, 164}: "Tariox Limited", + [3]byte{132, 37, 219}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 38, 21}: "ADB Broadband Italia", + [3]byte{132, 38, 43}: "Nokia", + [3]byte{132, 38, 144}: "BEIJING THOUGHT SCIENCE CO.,LTD.", + [3]byte{132, 39, 206}: "Corporation of the Presiding Bishop of The Church of Jesus Christ of Latter-day Saints", + [3]byte{132, 40, 90}: "Saffron Solutions Inc", + [3]byte{132, 41, 20}: "EMPORIA TELECOM Produktions- und VertriebsgesmbH & Co KG", + [3]byte{132, 41, 153}: "Apple, Inc.", + [3]byte{132, 43, 43}: "Dell Inc.", + [3]byte{132, 43, 80}: "Huria Co.,Ltd.", + [3]byte{132, 43, 188}: "Modelleisenbahn GmbH", + [3]byte{132, 46, 39}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 47, 117}: "Innokas Group", + [3]byte{132, 48, 229}: "SkyHawke Technologies, LLC", + [3]byte{132, 50, 234}: "ANHUI WANZTEN P&T CO., LTD", + [3]byte{132, 52, 151}: "Hewlett Packard", + [3]byte{132, 54, 17}: "hyungseul publishing networks", + [3]byte{132, 56, 53}: "Apple, Inc.", + [3]byte{132, 56, 56}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{132, 58, 75}: "Intel Corporate", + [3]byte{132, 61, 198}: "Cisco Systems, Inc", + [3]byte{132, 63, 78}: "Tri-Tech Manufacturing, Inc.", + [3]byte{132, 64, 118}: "Drivenets", + [3]byte{132, 68, 100}: "ServerU Inc", + [3]byte{132, 71, 101}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 72, 35}: "WOXTER TECHNOLOGY Co. Ltd", + [3]byte{132, 73, 21}: "vArmour Networks, Inc.", + [3]byte{132, 75, 183}: "Beijing Sankuai Online Technology Co.,Ltd", + [3]byte{132, 75, 245}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{132, 79, 3}: "Ablelink Electronics Ltd", + [3]byte{132, 81, 129}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 85, 165}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 86, 156}: "Coho Data, Inc.,", + [3]byte{132, 87, 135}: "DVR C&C Co., Ltd.", + [3]byte{132, 90, 129}: "ffly4u", + [3]byte{132, 91, 18}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 92, 147}: "Chabrier Services", + [3]byte{132, 93, 215}: "Shenzhen Netcom Electronics Co.,Ltd", + [3]byte{132, 97, 160}: "ARRIS Group, Inc.", + [3]byte{132, 98, 35}: "Shenzhen Coship Electronics Co., Ltd.", + [3]byte{132, 98, 166}: "EuroCB (Phils), Inc.", + [3]byte{132, 99, 214}: "Microsoft Corporation", + [3]byte{132, 104, 62}: "Intel Corporate", + [3]byte{132, 106, 237}: "Wireless Tsukamoto.,co.LTD", + [3]byte{132, 110, 177}: "Park Assist LLC", + [3]byte{132, 114, 7}: "I&C Technology", + [3]byte{132, 115, 3}: "Letv Mobile and Intelligent Information Technology (Beijing) Corporation Ltd.", + [3]byte{132, 116, 42}: "zte corporation", + [3]byte{132, 118, 22}: "Addat s.r.o.", + [3]byte{132, 119, 120}: "Cochlear Limited", + [3]byte{132, 120, 139}: "Apple, Inc.", + [3]byte{132, 120, 172}: "Cisco Systems, Inc", + [3]byte{132, 121, 115}: "Shanghai Baud Data Communication Co.,Ltd.", + [3]byte{132, 122, 136}: "HTC Corporation", + [3]byte{132, 123, 235}: "Dell Inc.", + [3]byte{132, 125, 80}: "Holley Metering Limited", + [3]byte{132, 126, 64}: "Texas Instruments", + [3]byte{132, 128, 45}: "Cisco Systems, Inc", + [3]byte{132, 130, 244}: "Beijing Huasun Unicreate Technology Co., Ltd", + [3]byte{132, 131, 25}: "Hangzhou Zero Zero Technology Co., Ltd.", + [3]byte{132, 131, 54}: "Newrun", + [3]byte{132, 131, 113}: "Avaya Inc", + [3]byte{132, 132, 51}: "Paradox Engineering SA", + [3]byte{132, 133, 6}: "Apple, Inc.", + [3]byte{132, 133, 10}: "Hella Sonnen- und Wetterschutztechnik GmbH", + [3]byte{132, 134, 243}: "Greenvity Communications", + [3]byte{132, 137, 173}: "Apple, Inc.", + [3]byte{132, 141, 132}: "Rajant Corporation", + [3]byte{132, 141, 199}: "Cisco SPVTG", + [3]byte{132, 142, 12}: "Apple, Inc.", + [3]byte{132, 142, 150}: "Embertec Pty Ltd", + [3]byte{132, 142, 223}: "Sony Mobile Communications AB", + [3]byte{132, 143, 105}: "Dell Inc.", + [3]byte{132, 144, 0}: "Arnold & Richter Cine Technik", + [3]byte{132, 147, 12}: "InCoax Networks Europe AB", + [3]byte{132, 148, 140}: "Hitron Technologies. Inc", + [3]byte{132, 150, 129}: "Cathay Communication Co.,Ltd", + [3]byte{132, 150, 216}: "ARRIS Group, Inc.", + [3]byte{132, 151, 184}: "Memjet Inc.", + [3]byte{132, 152, 102}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 156, 166}: "Arcadyan Technology Corporation", + [3]byte{132, 157, 100}: "SMC Corporation", + [3]byte{132, 157, 197}: "Centera Photonics Inc.", + [3]byte{132, 159, 181}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 161, 52}: "Apple, Inc.", + [3]byte{132, 164, 35}: "Sagemcom Broadband SAS", + [3]byte{132, 164, 102}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 166, 200}: "Intel Corporate", + [3]byte{132, 167, 131}: "Alcatel Lucent", + [3]byte{132, 167, 136}: "Perples", + [3]byte{132, 168, 228}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 169, 145}: "Cyber Trans Japan Co.,Ltd.", + [3]byte{132, 169, 196}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 172, 164}: "Beijing Novel Super Digital TV Technology Co., Ltd", + [3]byte{132, 172, 251}: "Crouzet Automatismes", + [3]byte{132, 173, 88}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 175, 31}: "Beat System Service Co,. Ltd.", + [3]byte{132, 175, 236}: "BUFFALO.INC", + [3]byte{132, 177, 83}: "Apple, Inc.", + [3]byte{132, 178, 97}: "Cisco Systems, Inc", + [3]byte{132, 181, 23}: "Cisco Systems, Inc", + [3]byte{132, 181, 65}: "Samsung Electronics Co.,Ltd", + [3]byte{132, 181, 156}: "Juniper Networks", + [3]byte{132, 184, 2}: "Cisco Systems, Inc", + [3]byte{132, 186, 59}: "CANON INC.", + [3]byte{132, 190, 82}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 193, 193}: "Juniper Networks", + [3]byte{132, 194, 228}: "Jiangsu Qinheng Co., Ltd.", + [3]byte{132, 195, 232}: "Vaillant GmbH", + [3]byte{132, 199, 39}: "Gnodal Ltd", + [3]byte{132, 199, 169}: "C3PO S.A.", + [3]byte{132, 199, 234}: "Sony Mobile Communications AB", + [3]byte{132, 200, 177}: "Incognito Software Systems Inc.", + [3]byte{132, 201, 178}: "D-Link International", + [3]byte{132, 205, 98}: "ShenZhen IDWELL Technology CO.,Ltd", + [3]byte{132, 207, 191}: "Fairphone", + [3]byte{132, 211, 42}: "IEEE 1905.1", + [3]byte{132, 212, 126}: "Aruba Networks", + [3]byte{132, 212, 200}: "Widex A/S", + [3]byte{132, 214, 208}: "Amazon Technologies Inc.", + [3]byte{132, 217, 49}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{132, 217, 200}: "Unipattern Co.,", + [3]byte{132, 219, 47}: "Sierra Wireless Inc", + [3]byte{132, 219, 172}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{132, 219, 252}: "Nokia", + [3]byte{132, 221, 32}: "Texas Instruments", + [3]byte{132, 221, 183}: "Cilag GmbH International", + [3]byte{132, 222, 61}: "Crystal Vision Ltd", + [3]byte{132, 223, 12}: "NET2GRID BV", + [3]byte{132, 223, 25}: "Chuango Security Technology Corporation", + [3]byte{132, 224, 88}: "ARRIS Group, Inc.", + [3]byte{132, 224, 244}: "IEEE Registration Authority", + [3]byte{132, 227, 35}: "Green Wave Telecommunication SDN BHD", + [3]byte{132, 228, 217}: "Shenzhen NEED technology Ltd.", + [3]byte{132, 230, 41}: "Bluwan SA", + [3]byte{132, 231, 20}: "Liang Herng Enterprise,Co.Ltd.", + [3]byte{132, 234, 153}: "Vieworks", + [3]byte{132, 235, 24}: "Texas Instruments", + [3]byte{132, 237, 51}: "BBMC Co.,Ltd", + [3]byte{132, 239, 24}: "Intel Corporate", + [3]byte{132, 241, 41}: "Metrascale Inc.", + [3]byte{132, 244, 147}: "OMS spol. s.r.o.", + [3]byte{132, 246, 76}: "Cross Point BV", + [3]byte{132, 246, 250}: "Miovision Technologies Incorporated", + [3]byte{132, 252, 172}: "Apple, Inc.", + [3]byte{132, 252, 254}: "Apple, Inc.", + [3]byte{132, 254, 158}: "RTC Industries, Inc.", + [3]byte{132, 254, 220}: "Borqs Beijing Ltd.", + [3]byte{136, 1, 242}: "Vitec System Engineering Inc.", + [3]byte{136, 3, 85}: "Arcadyan Technology Corporation", + [3]byte{136, 7, 75}: "LG Electronics (Mobile Communications)", + [3]byte{136, 9, 5}: "MTMCommunications", + [3]byte{136, 9, 175}: "Masimo Corporation", + [3]byte{136, 15, 16}: "Huami Information Technology Co.,Ltd.", + [3]byte{136, 15, 182}: "Jabil Circuits India Pvt Ltd,-EHTP unit", + [3]byte{136, 16, 54}: "Panodic(ShenZhen) Electronics Limted", + [3]byte{136, 18, 78}: "Qualcomm Inc.", + [3]byte{136, 20, 43}: "Protonic Holland", + [3]byte{136, 21, 68}: "Meraki, Inc.", + [3]byte{136, 24, 174}: "Tamron Co., Ltd", + [3]byte{136, 27, 153}: "SHENZHEN XIN FEI JIA ELECTRONIC CO. LTD.", + [3]byte{136, 29, 252}: "Cisco Systems, Inc", + [3]byte{136, 31, 161}: "Apple, Inc.", + [3]byte{136, 32, 18}: "LMI Technologies", + [3]byte{136, 33, 227}: "Nebusens, S.L.", + [3]byte{136, 35, 100}: "Watchnet DVR Inc", + [3]byte{136, 35, 254}: "TTTech Computertechnik AG", + [3]byte{136, 37, 44}: "Arcadyan Technology Corporation", + [3]byte{136, 37, 147}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{136, 40, 179}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 41, 80}: "Dalian Netmoon Tech Develop Co.,Ltd", + [3]byte{136, 43, 215}: "ADDÉNERGIE TECHNOLOGIES", + [3]byte{136, 46, 90}: "storONE", + [3]byte{136, 48, 138}: "Murata Manufacturing Co., Ltd.", + [3]byte{136, 50, 155}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{136, 51, 20}: "Texas Instruments", + [3]byte{136, 51, 190}: "Ivenix, Inc.", + [3]byte{136, 53, 76}: "Transics", + [3]byte{136, 54, 18}: "SRC Computers, LLC", + [3]byte{136, 54, 108}: "EFM Networks", + [3]byte{136, 59, 139}: "Cheering Connection Co. Ltd.", + [3]byte{136, 60, 28}: "MERCURY CORPORATION", + [3]byte{136, 63, 211}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 65, 87}: "Shenzhen Atsmart Technology Co.,Ltd.", + [3]byte{136, 65, 193}: "ORBISAT DA AMAZONIA IND E AEROL SA", + [3]byte{136, 65, 252}: "AirTies Wireless Networks", + [3]byte{136, 67, 225}: "Cisco Systems, Inc", + [3]byte{136, 68, 119}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 68, 246}: "Nokia Corporation", + [3]byte{136, 70, 42}: "Telechips Inc.", + [3]byte{136, 74, 234}: "Texas Instruments", + [3]byte{136, 75, 57}: "Siemens AG, Healthcare Sector", + [3]byte{136, 76, 207}: "Pulzze Systems, Inc", + [3]byte{136, 80, 221}: "Infiniband Trade Association ", + [3]byte{136, 81, 251}: "Hewlett Packard", + [3]byte{136, 83, 46}: "Intel Corporate", + [3]byte{136, 83, 149}: "Apple, Inc.", + [3]byte{136, 83, 212}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 87, 109}: "XTA Electronics Ltd", + [3]byte{136, 87, 238}: "BUFFALO.INC", + [3]byte{136, 90, 146}: "Cisco Systems, Inc", + [3]byte{136, 91, 221}: "Aerohive Networks Inc.", + [3]byte{136, 92, 71}: "Alcatel Lucent", + [3]byte{136, 93, 144}: "IEEE Registration Authority", + [3]byte{136, 97, 90}: "Siano Mobile Silicon Ltd.", + [3]byte{136, 99, 223}: "Apple, Inc.", + [3]byte{136, 102, 57}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 102, 165}: "Apple, Inc.", + [3]byte{136, 104, 92}: "Shenzhen ChuangDao & Perpetual Eternal Technology Co.,Ltd", + [3]byte{136, 106, 177}: "vivo Mobile Communication Co., Ltd.", + [3]byte{136, 107, 15}: "Bluegiga Technologies OY", + [3]byte{136, 107, 68}: "Sunnovo International Limited", + [3]byte{136, 107, 110}: "Apple, Inc.", + [3]byte{136, 107, 118}: "CHINA HOPEFUL GROUP HOPEFUL ELECTRIC CO.,LTD", + [3]byte{136, 112, 51}: "Hangzhou Silan Microelectronic Inc", + [3]byte{136, 112, 140}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{136, 112, 239}: "SC Professional Trading Co., Ltd.", + [3]byte{136, 113, 229}: "Amazon Technologies Inc.", + [3]byte{136, 115, 132}: "Toshiba", + [3]byte{136, 115, 152}: "K2E Tekpoint", + [3]byte{136, 117, 86}: "Cisco Systems, Inc", + [3]byte{136, 120, 115}: "Intel Corporate", + [3]byte{136, 120, 156}: "Game Technologies SA", + [3]byte{136, 121, 91}: "Konka Group Co., Ltd.", + [3]byte{136, 121, 126}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{136, 127, 3}: "Comper Technology Investment Limited", + [3]byte{136, 131, 34}: "Samsung Electronics Co.,Ltd", + [3]byte{136, 134, 3}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 134, 160}: "Simton Technologies, Ltd.", + [3]byte{136, 135, 23}: "CANON INC.", + [3]byte{136, 135, 221}: "DarbeeVision Inc.", + [3]byte{136, 137, 20}: "All Components Incorporated", + [3]byte{136, 137, 100}: "GSI Electronics Inc.", + [3]byte{136, 139, 93}: "Storage Appliance Corporation ", + [3]byte{136, 140, 25}: "Brady Corp Asia Pacific Ltd", + [3]byte{136, 144, 141}: "Cisco Systems, Inc", + [3]byte{136, 145, 102}: "Viewcooper Corp.", + [3]byte{136, 145, 221}: "Racktivity", + [3]byte{136, 148, 113}: "Brocade Communications Systems, Inc.", + [3]byte{136, 148, 126}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{136, 148, 249}: "Gemicom Technology, Inc.", + [3]byte{136, 149, 185}: "Unified Packet Systems Crop", + [3]byte{136, 150, 118}: "TTC MARCONI s.r.o.", + [3]byte{136, 150, 182}: "Global Fire Equipment S.A.", + [3]byte{136, 150, 242}: "Valeo Schalter und Sensoren GmbH", + [3]byte{136, 151, 223}: "Entrypass Corporation Sdn. Bhd.", + [3]byte{136, 152, 33}: "TERAON", + [3]byte{136, 155, 57}: "Samsung Electronics Co.,Ltd", + [3]byte{136, 156, 166}: "BTB Korea INC", + [3]byte{136, 159, 250}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{136, 160, 132}: "Formation Data Systems", + [3]byte{136, 162, 94}: "Juniper Networks", + [3]byte{136, 162, 215}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 163, 204}: "Amatis Controls", + [3]byte{136, 165, 189}: "QPCOM INC.", + [3]byte{136, 166, 198}: "Sagemcom Broadband SAS", + [3]byte{136, 167, 60}: "Ragentek Technology Group", + [3]byte{136, 172, 193}: "Generiton Co., Ltd. ", + [3]byte{136, 173, 67}: "PEGATRON CORPORATION", + [3]byte{136, 173, 210}: "Samsung Electronics Co.,Ltd", + [3]byte{136, 174, 29}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{136, 177, 104}: "Delta Control GmbH", + [3]byte{136, 177, 225}: "Mojo Networks, Inc.", + [3]byte{136, 182, 39}: "Gembird Europe BV", + [3]byte{136, 184, 208}: "Dongguan Koppo Electronic Co.,Ltd", + [3]byte{136, 186, 127}: "Qfiednet Co., Ltd.", + [3]byte{136, 191, 213}: "Simple Audio Ltd", + [3]byte{136, 194, 66}: "Poynt Co.", + [3]byte{136, 194, 85}: "Texas Instruments", + [3]byte{136, 195, 110}: "Beijing Ereneben lnformation Technology Limited", + [3]byte{136, 195, 179}: "SOVICO", + [3]byte{136, 198, 38}: "Logitech, Inc", + [3]byte{136, 198, 99}: "Apple, Inc.", + [3]byte{136, 201, 208}: "LG Electronics (Mobile Communications)", + [3]byte{136, 203, 135}: "Apple, Inc.", + [3]byte{136, 203, 165}: "Suzhou Torchstar Intelligent Technology Co.,Ltd", + [3]byte{136, 204, 69}: "Skyworth Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{136, 206, 250}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 207, 152}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 211, 123}: "FirmTek, LLC", + [3]byte{136, 215, 188}: "DEP Company", + [3]byte{136, 217, 98}: "Canopus Systems US LLC", + [3]byte{136, 220, 150}: "SENAO Networks, Inc.", + [3]byte{136, 221, 121}: "Voltaire", + [3]byte{136, 222, 169}: "Roku, Inc.", + [3]byte{136, 224, 160}: "Shenzhen VisionSTOR Technologies Co., Ltd", + [3]byte{136, 224, 243}: "Juniper Networks", + [3]byte{136, 225, 97}: "Art Beijing Science and Technology Development Co., Ltd.", + [3]byte{136, 227, 171}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{136, 230, 3}: "Avotek corporation", + [3]byte{136, 230, 40}: "Shenzhen Kezhonglong Optoelectronic Technology Co.,Ltd", + [3]byte{136, 231, 18}: "Whirlpool Corporation", + [3]byte{136, 231, 166}: "iKnowledge Integration Corp.", + [3]byte{136, 232, 127}: "Apple, Inc.", + [3]byte{136, 232, 248}: "YONG TAI ELECTRONIC (DONGGUAN) LTD.", + [3]byte{136, 233, 23}: "Tamaggo", + [3]byte{136, 237, 28}: "Cudo Communication Co., Ltd.", + [3]byte{136, 240, 49}: "Cisco Systems, Inc", + [3]byte{136, 240, 119}: "Cisco Systems, Inc", + [3]byte{136, 244, 136}: "cellon communications technology(shenzhen)Co.,Ltd.", + [3]byte{136, 244, 144}: "Jetmobile Pte Ltd", + [3]byte{136, 247, 199}: "Technicolor CH USA Inc.", + [3]byte{136, 253, 21}: "LINEEYE CO., LTD", + [3]byte{136, 254, 214}: "ShangHai WangYong Software Co., Ltd.", + [3]byte{140, 0, 109}: "Apple, Inc.", + [3]byte{140, 4, 255}: "Technicolor CH USA Inc.", + [3]byte{140, 5, 81}: "Koubachi AG", + [3]byte{140, 7, 140}: "FLOW DATA INC", + [3]byte{140, 8, 139}: "Remote Solution", + [3]byte{140, 9, 244}: "ARRIS Group, Inc.", + [3]byte{140, 12, 144}: "Ruckus Wireless", + [3]byte{140, 12, 163}: "Amper", + [3]byte{140, 13, 118}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{140, 14, 227}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{140, 16, 212}: "Sagemcom Broadband SAS", + [3]byte{140, 17, 203}: "ABUS Security-Center GmbH & Co. KG", + [3]byte{140, 24, 217}: "Shenzhen RF Technology Co., Ltd", + [3]byte{140, 25, 45}: "IEEE Registration Authority", + [3]byte{140, 26, 191}: "Samsung Electronics Co.,Ltd", + [3]byte{140, 31, 148}: "RF Surgical System Inc. ", + [3]byte{140, 33, 10}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{140, 39, 29}: "QuantHouse", + [3]byte{140, 39, 138}: "Vocollect Inc", + [3]byte{140, 41, 55}: "Apple, Inc.", + [3]byte{140, 45, 170}: "Apple, Inc.", + [3]byte{140, 47, 57}: "IBA Dosimetry GmbH", + [3]byte{140, 47, 166}: "Solid Optics B.V.", + [3]byte{140, 51, 48}: "EmFirst Co., Ltd.", + [3]byte{140, 51, 87}: "HiteVision Digital Media Technology Co.,Ltd.", + [3]byte{140, 52, 253}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{140, 58, 227}: "LG Electronics (Mobile Communications)", + [3]byte{140, 60, 7}: "Skiva Technologies, Inc.", + [3]byte{140, 60, 74}: "NAKAYO Inc", + [3]byte{140, 65, 242}: "RDA Technologies Ltd.", + [3]byte{140, 68, 53}: "Shanghai BroadMobi Communication Technology Co., Ltd.", + [3]byte{140, 74, 238}: "GIGA TMS INC", + [3]byte{140, 75, 89}: "3D Imaging & Simulations Corp", + [3]byte{140, 76, 220}: "PLANEX COMMUNICATIONS INC.", + [3]byte{140, 77, 185}: "Unmonday Ltd", + [3]byte{140, 77, 234}: "Cerio Corporation", + [3]byte{140, 81, 5}: "Shenzhen ireadygo Information Technology CO.,LTD.", + [3]byte{140, 83, 247}: "A&D ENGINEERING CO., LTD.", + [3]byte{140, 84, 29}: "LGE ", + [3]byte{140, 86, 157}: "Imaging Solutions Group", + [3]byte{140, 86, 197}: "Nintendo Co., Ltd.", + [3]byte{140, 87, 155}: "Wistron Neweb Corporation", + [3]byte{140, 87, 253}: "LVX Western", + [3]byte{140, 88, 119}: "Apple, Inc.", + [3]byte{140, 89, 139}: "C Technologies AB", + [3]byte{140, 89, 195}: "ADB Italia ", + [3]byte{140, 90, 240}: "Exeltech Solar Products", + [3]byte{140, 92, 161}: "d-broad,INC", + [3]byte{140, 93, 96}: "UCI Corporation Co.,Ltd.", + [3]byte{140, 95, 223}: "Beijing Railway Signal Factory", + [3]byte{140, 96, 79}: "Cisco Systems, Inc", + [3]byte{140, 96, 231}: "MPGIO CO.,LTD", + [3]byte{140, 97, 2}: "Beijing Baofengmojing Technologies Co., Ltd", + [3]byte{140, 100, 11}: "Beyond Devices d.o.o.", + [3]byte{140, 100, 34}: "Sony Mobile Communications AB", + [3]byte{140, 104, 120}: "Nortek-AS", + [3]byte{140, 106, 228}: "Viogem Limited", + [3]byte{140, 109, 80}: "SHENZHEN MTC CO LTD", + [3]byte{140, 112, 90}: "Intel Corporate", + [3]byte{140, 113, 248}: "Samsung Electronics Co.,Ltd", + [3]byte{140, 115, 110}: "FUJITSU LIMITED", + [3]byte{140, 118, 193}: "Goden Tech Limited", + [3]byte{140, 119, 18}: "Samsung Electronics Co.,Ltd", + [3]byte{140, 119, 22}: "LONGCHEER TELECOMMUNICATION LIMITED", + [3]byte{140, 120, 215}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{140, 121, 103}: "zte corporation", + [3]byte{140, 123, 157}: "Apple, Inc.", + [3]byte{140, 124, 146}: "Apple, Inc.", + [3]byte{140, 124, 181}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{140, 124, 255}: "Brocade Communications Systems, Inc.", + [3]byte{140, 126, 179}: "Lytro, Inc.", + [3]byte{140, 127, 59}: "ARRIS Group, Inc.", + [3]byte{140, 130, 168}: "Insigma Technology Co.,Ltd", + [3]byte{140, 132, 1}: "Private", + [3]byte{140, 135, 59}: "Leica Camera AG", + [3]byte{140, 137, 122}: "AUGTEK", + [3]byte{140, 137, 165}: "Micro-Star INT'L CO., LTD", + [3]byte{140, 138, 110}: "ESTUN AUTOMATION TECHNOLOY CO., LTD", + [3]byte{140, 138, 187}: "Beijing Orient View Technology Co., Ltd.", + [3]byte{140, 139, 131}: "Texas Instruments", + [3]byte{140, 142, 118}: "taskit GmbH", + [3]byte{140, 142, 242}: "Apple, Inc.", + [3]byte{140, 143, 233}: "Apple, Inc.", + [3]byte{140, 144, 211}: "Nokia", + [3]byte{140, 145, 9}: "Toyoshima Electric Technoeogy(Suzhou) Co.,Ltd.", + [3]byte{140, 146, 54}: "Aus.Linx Technology Co., Ltd.", + [3]byte{140, 147, 81}: "Jigowatts Inc.", + [3]byte{140, 148, 207}: "Encell Technology, Inc.", + [3]byte{140, 153, 230}: "TCT mobile ltd", + [3]byte{140, 160, 72}: "Beijing NeTopChip Technology Co.,LTD", + [3]byte{140, 162, 253}: "Starry, Inc.", + [3]byte{140, 165, 161}: "Oregano Systems - Design & Consulting GmbH", + [3]byte{140, 166, 223}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{140, 169, 130}: "Intel Corporate", + [3]byte{140, 171, 142}: "Shanghai Feixun Communication Co.,Ltd.", + [3]byte{140, 174, 76}: "Plugable Technologies", + [3]byte{140, 174, 137}: "Y-cam Solutions Ltd", + [3]byte{140, 176, 148}: "Airtech I&C Co., Ltd", + [3]byte{140, 182, 79}: "Cisco Systems, Inc", + [3]byte{140, 183, 247}: "Shenzhen UniStrong Science & Technology Co., Ltd", + [3]byte{140, 184, 44}: "IPitomy Communications", + [3]byte{140, 184, 100}: "AcSiP Technology Corp.", + [3]byte{140, 190, 190}: "Xiaomi Communications Co Ltd", + [3]byte{140, 191, 157}: "Shanghai Xinyou Information Technology Ltd. Co.", + [3]byte{140, 191, 166}: "Samsung Electronics Co.,Ltd", + [3]byte{140, 193, 33}: "Panasonic Corporation AVC Networks Company", + [3]byte{140, 197, 225}: "ShenZhen Konka Telecommunication Technology Co.,Ltd", + [3]byte{140, 198, 97}: "Current, powered by GE", + [3]byte{140, 199, 170}: "Radinet Communications Inc.", + [3]byte{140, 199, 208}: "zhejiang ebang communication co.,ltd", + [3]byte{140, 200, 205}: "Samsung Electronics Co.,Ltd", + [3]byte{140, 200, 244}: "IEEE Registration Authority", + [3]byte{140, 205, 162}: "ACTP, Inc.", + [3]byte{140, 205, 232}: "Nintendo Co., Ltd.", + [3]byte{140, 207, 92}: "BEFEGA GmbH", + [3]byte{140, 209, 123}: "CG Mobile", + [3]byte{140, 210, 233}: "NIPPON SMT Co.Ltd", + [3]byte{140, 211, 162}: "VisSim AS", + [3]byte{140, 214, 40}: "Ikor Metering", + [3]byte{140, 219, 37}: "ESG Solutions", + [3]byte{140, 220, 212}: "Hewlett Packard", + [3]byte{140, 221, 141}: "Wifly-City System Inc.", + [3]byte{140, 222, 82}: "ISSC Technologies Corp.", + [3]byte{140, 222, 153}: "Comlab Inc.", + [3]byte{140, 223, 157}: "NEC Corporation", + [3]byte{140, 224, 129}: "zte corporation", + [3]byte{140, 225, 23}: "zte corporation", + [3]byte{140, 226, 218}: "Circle Media Inc", + [3]byte{140, 231, 72}: "Private", + [3]byte{140, 231, 140}: "DK Networks", + [3]byte{140, 231, 179}: "Sonardyne International Ltd", + [3]byte{140, 234, 27}: "Edgecore Networks Corporation", + [3]byte{140, 235, 198}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{140, 238, 198}: "Precepscion Pty. Ltd.", + [3]byte{140, 242, 40}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{140, 245, 163}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{140, 248, 19}: "ORANGE POLSKA", + [3]byte{140, 249, 69}: "Power Automation pte Ltd", + [3]byte{140, 249, 201}: "MESADA Technology Co.,Ltd.", + [3]byte{140, 250, 186}: "Apple, Inc.", + [3]byte{140, 253, 240}: "Qualcomm Inc.", + [3]byte{144, 0, 78}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{144, 0, 219}: "Samsung Electronics Co.,Ltd", + [3]byte{144, 1, 59}: "Sagemcom Broadband SAS", + [3]byte{144, 2, 138}: "Shenzhen Shidean Legrand Electronic Products Co.,Ltd", + [3]byte{144, 2, 169}: "Zhejiang Dahua Technology Co., Ltd.", + [3]byte{144, 3, 37}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{144, 3, 183}: "PARROT SA", + [3]byte{144, 6, 40}: "Samsung Electronics Co.,Ltd", + [3]byte{144, 9, 23}: "Far-sighted mobile", + [3]byte{144, 10, 57}: "Wiio, Inc.", + [3]byte{144, 10, 58}: "PSG Plastic Service GmbH", + [3]byte{144, 11, 193}: "Sprocomm Technologies CO.,Ltd", + [3]byte{144, 12, 180}: "Alinket Electronic Technology Co., Ltd", + [3]byte{144, 13, 102}: "Digimore Electronics Co., Ltd", + [3]byte{144, 13, 203}: "ARRIS Group, Inc.", + [3]byte{144, 14, 131}: "Monico Monitoring, Inc.", + [3]byte{144, 23, 17}: "Hagenuk Marinekommunikation GmbH", + [3]byte{144, 23, 155}: "Nanomegas", + [3]byte{144, 23, 172}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{144, 24, 94}: "Apex Tool Group GmbH & Co OHG", + [3]byte{144, 24, 124}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{144, 24, 174}: "Shanghai Meridian Technologies, Co. Ltd.", + [3]byte{144, 25, 0}: "SCS SA", + [3]byte{144, 26, 202}: "ARRIS Group, Inc.", + [3]byte{144, 27, 14}: "Fujitsu Technology Solutions GmbH", + [3]byte{144, 29, 39}: "zte corporation", + [3]byte{144, 30, 221}: "GREAT COMPUTER CORPORATION", + [3]byte{144, 32, 58}: "BYD Precision Manufacture Co.,Ltd", + [3]byte{144, 32, 131}: "General Engine Management Systems Ltd.", + [3]byte{144, 33, 6}: "BSkyB Ltd", + [3]byte{144, 33, 85}: "HTC Corporation", + [3]byte{144, 33, 129}: "Shanghai Huaqin Telecom Technology Co.,Ltd", + [3]byte{144, 35, 236}: "Availink, Inc.", + [3]byte{144, 39, 228}: "Apple, Inc.", + [3]byte{144, 43, 52}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{144, 44, 199}: "C-MAX Asia Limited", + [3]byte{144, 46, 28}: "Intel Corporate", + [3]byte{144, 46, 135}: "LabJack", + [3]byte{144, 49, 205}: "Onyx Healthcare Inc.", + [3]byte{144, 52, 43}: "Gatekeeper Systems, Inc.", + [3]byte{144, 52, 252}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{144, 53, 110}: "Vodafone Omnitel N.V.", + [3]byte{144, 56, 9}: "Ericsson AB", + [3]byte{144, 56, 223}: "Changzhou Tiannengbo System Co. Ltd.", + [3]byte{144, 58, 160}: "Nokia", + [3]byte{144, 58, 230}: "PARROT SA", + [3]byte{144, 60, 146}: "Apple, Inc.", + [3]byte{144, 60, 174}: "Yunnan KSEC Digital Technology Co.,Ltd.", + [3]byte{144, 61, 90}: "Shenzhen Wision Technology Holding Limited", + [3]byte{144, 61, 107}: "Zicon Technology Corp.", + [3]byte{144, 62, 171}: "ARRIS Group, Inc.", + [3]byte{144, 69, 6}: "Tokyo Boeki Medisys Inc.", + [3]byte{144, 70, 162}: "Tedipay UK Ltd", + [3]byte{144, 70, 183}: "Vadaro Pte Ltd", + [3]byte{144, 71, 22}: "RORZE CORPORATION", + [3]byte{144, 72, 154}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{144, 73, 250}: "Intel Corporate", + [3]byte{144, 76, 229}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{144, 77, 74}: "Sagemcom Broadband SAS", + [3]byte{144, 78, 43}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{144, 80, 90}: "unGlue, Inc", + [3]byte{144, 80, 123}: "Advanced PANMOBIL Systems GmbH & Co. KG", + [3]byte{144, 81, 63}: "Elettronica Santerno SpA", + [3]byte{144, 84, 70}: "TES ELECTRONIC SOLUTIONS", + [3]byte{144, 85, 174}: "Ericsson, EAB/RWI/K", + [3]byte{144, 86, 130}: "Lenbrook Industries Limited", + [3]byte{144, 86, 146}: "Autotalks Ltd.", + [3]byte{144, 89, 175}: "Texas Instruments", + [3]byte{144, 92, 68}: "Compal Broadband Networks, Inc.", + [3]byte{144, 95, 46}: "TCT mobile ltd", + [3]byte{144, 95, 141}: "modas GmbH", + [3]byte{144, 96, 241}: "Apple, Inc.", + [3]byte{144, 97, 12}: "Fida International (S) Pte Ltd", + [3]byte{144, 103, 23}: "Alphion India Private Limited", + [3]byte{144, 103, 28}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{144, 103, 181}: "Alcatel-Lucent", + [3]byte{144, 103, 243}: "Alcatel Lucent", + [3]byte{144, 104, 195}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{144, 108, 172}: "Fortinet, Inc.", + [3]byte{144, 109, 200}: "DLG Automação Industrial Ltda", + [3]byte{144, 110, 187}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{144, 111, 24}: "Private", + [3]byte{144, 111, 169}: "NANJING PUTIAN TELECOMMUNICATIONS TECHNOLOGY CO.,LTD.", + [3]byte{144, 112, 37}: "Garea Microsys Co.,Ltd.", + [3]byte{144, 114, 64}: "Apple, Inc.", + [3]byte{144, 114, 130}: "Sagemcom Broadband SAS", + [3]byte{144, 121, 144}: "Benchmark Electronics Romania SRL", + [3]byte{144, 122, 10}: "Gebr. Bode GmbH & Co KG", + [3]byte{144, 122, 40}: "Beijing Morncloud Information And Technology Co. Ltd.", + [3]byte{144, 122, 241}: "Wally", + [3]byte{144, 126, 186}: "UTEK TECHNOLOGY (SHENZHEN) CO.,LTD", + [3]byte{144, 127, 97}: "Chicony Electronics Co., Ltd.", + [3]byte{144, 130, 96}: "IEEE 1904.1 Working Group", + [3]byte{144, 131, 122}: "General Electric Water & Process Technologies", + [3]byte{144, 132, 13}: "Apple, Inc.", + [3]byte{144, 132, 43}: "LEGO System A/S", + [3]byte{144, 134, 116}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{144, 136, 162}: "IONICS TECHNOLOGY ME LTDA", + [3]byte{144, 140, 9}: "Total Phase", + [3]byte{144, 140, 68}: "H.K ZONGMU TECHNOLOGY CO., LTD.", + [3]byte{144, 140, 99}: "GZ Weedong Networks Technology Co. , Ltd", + [3]byte{144, 141, 29}: "GH Technologies", + [3]byte{144, 141, 108}: "Apple, Inc.", + [3]byte{144, 141, 120}: "D-Link International", + [3]byte{144, 143, 207}: "UNO System Co., Ltd", + [3]byte{144, 144, 60}: "TRISON TECHNOLOGY CORPORATION", + [3]byte{144, 144, 96}: "RSI VIDEO TECHNOLOGIES", + [3]byte{144, 146, 180}: "Diehl BGT Defence GmbH & Co. KG", + [3]byte{144, 148, 228}: "D-Link International", + [3]byte{144, 151, 213}: "Espressif Inc.", + [3]byte{144, 152, 100}: "Impex-Sat GmbH&Co KG", + [3]byte{144, 153, 22}: "ELVEES NeoTek OJSC", + [3]byte{144, 157, 224}: "Newland Design + Assoc. Inc.", + [3]byte{144, 159, 51}: "EFM Networks", + [3]byte{144, 159, 67}: "Accutron Instruments Inc.", + [3]byte{144, 162, 16}: "United Telecoms Ltd", + [3]byte{144, 162, 218}: "GHEO SA", + [3]byte{144, 164, 106}: "SISNET CO., LTD", + [3]byte{144, 164, 222}: "Wistron Neweb Corporation", + [3]byte{144, 166, 47}: "NAVER", + [3]byte{144, 167, 131}: "JSW PACIFIC CORPORATION ", + [3]byte{144, 167, 193}: "Pakedge Device and Software Inc.", + [3]byte{144, 172, 63}: "BrightSign LLC", + [3]byte{144, 174, 27}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{144, 176, 237}: "Apple, Inc.", + [3]byte{144, 177, 28}: "Dell Inc.", + [3]byte{144, 177, 52}: "ARRIS Group, Inc.", + [3]byte{144, 178, 31}: "Apple, Inc.", + [3]byte{144, 182, 134}: "Murata Manufacturing Co., Ltd.", + [3]byte{144, 184, 208}: "Joyent, Inc.", + [3]byte{144, 185, 49}: "Apple, Inc.", + [3]byte{144, 185, 125}: "Johnson Outdoors Marine Electronics d/b/a Minnkota", + [3]byte{144, 193, 21}: "Sony Mobile Communications AB", + [3]byte{144, 193, 198}: "Apple, Inc.", + [3]byte{144, 195, 95}: "Nanjing Jiahao Technology Co., Ltd.", + [3]byte{144, 198, 130}: "IEEE Registration Authority", + [3]byte{144, 199, 146}: "ARRIS Group, Inc.", + [3]byte{144, 199, 216}: "zte corporation", + [3]byte{144, 201, 155}: "Recore Systems", + [3]byte{144, 204, 36}: "Synaptics, Inc", + [3]byte{144, 205, 182}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{144, 207, 21}: "Nokia Corporation", + [3]byte{144, 207, 111}: "Dlogixs Co Ltd", + [3]byte{144, 207, 125}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{144, 209, 27}: "Palomar Medical Technologies", + [3]byte{144, 215, 79}: "Bookeen", + [3]byte{144, 215, 190}: "Wavelab Global Inc.", + [3]byte{144, 215, 235}: "Texas Instruments", + [3]byte{144, 216, 82}: "Comtec Co., Ltd.", + [3]byte{144, 216, 243}: "zte corporation", + [3]byte{144, 217, 44}: "HUG-WITSCHI AG", + [3]byte{144, 218, 78}: "AVANU", + [3]byte{144, 218, 106}: "FOCUS H&S Co., Ltd.", + [3]byte{144, 219, 70}: "E-LEAD ELECTRONIC CO., LTD", + [3]byte{144, 223, 183}: "s.m.s smart microwave sensors GmbH", + [3]byte{144, 223, 251}: "HOMERIDER SYSTEMS", + [3]byte{144, 224, 240}: "IEEE 1722a Working Group", + [3]byte{144, 226, 186}: "Intel Corporate", + [3]byte{144, 230, 186}: "ASUSTek COMPUTER INC.", + [3]byte{144, 231, 196}: "HTC Corporation", + [3]byte{144, 234, 96}: "SPI Lasers Ltd ", + [3]byte{144, 238, 217}: "UNIVERSAL DE DESARROLLOS ELECTRÓNICOS, SA", + [3]byte{144, 239, 104}: "ZyXEL Communications Corporation", + [3]byte{144, 240, 82}: "MEIZU Technology Co., Ltd.", + [3]byte{144, 241, 170}: "Samsung Electronics Co.,Ltd", + [3]byte{144, 241, 176}: "Hangzhou Anheng Info&Tech CO.,LTD", + [3]byte{144, 242, 120}: "Radius Gateway", + [3]byte{144, 243, 5}: "HUMAX Co., Ltd.", + [3]byte{144, 243, 183}: "Kirisun Communications Co., Ltd.", + [3]byte{144, 244, 193}: "Rand McNally", + [3]byte{144, 246, 82}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{144, 247, 47}: "Phillips Machine & Welding Co., Inc. ", + [3]byte{144, 251, 91}: "Avaya Inc", + [3]byte{144, 251, 166}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{144, 253, 97}: "Apple, Inc.", + [3]byte{144, 255, 121}: "Metro Ethernet Forum", + [3]byte{148, 0, 112}: "Nokia Corporation", + [3]byte{148, 1, 73}: "AutoHotBox", + [3]byte{148, 1, 194}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 4, 156}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{148, 5, 182}: "Liling FullRiver Electronics & Technology Ltd", + [3]byte{148, 9, 55}: "HUMAX Co., Ltd.", + [3]byte{148, 11, 45}: "NetView Technologies(Shenzhen) Co., Ltd", + [3]byte{148, 11, 213}: "Himax Technologies, Inc", + [3]byte{148, 12, 109}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{148, 16, 62}: "Belkin International Inc.", + [3]byte{148, 17, 218}: "ITF Fröschl GmbH", + [3]byte{148, 22, 115}: "Point Core SARL", + [3]byte{148, 24, 130}: "Hewlett Packard Enterprise", + [3]byte{148, 29, 28}: "TLab West Systems AB", + [3]byte{148, 32, 83}: "Nokia Corporation", + [3]byte{148, 33, 151}: "Stalmart Technology Limited", + [3]byte{148, 35, 110}: "Shenzhen Junlan Electronic Ltd", + [3]byte{148, 44, 179}: "HUMAX Co., Ltd.", + [3]byte{148, 46, 23}: "Schneider Electric Canada Inc", + [3]byte{148, 46, 99}: "Finsécur", + [3]byte{148, 49, 155}: "Alphatronics BV", + [3]byte{148, 51, 221}: "Taco Inc", + [3]byte{148, 53, 10}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 54, 224}: "Sichuan Bihong Broadcast & Television New Technologies Co.,Ltd", + [3]byte{148, 57, 229}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{148, 58, 240}: "Nokia Corporation", + [3]byte{148, 59, 177}: "Kaonmedia CO., LTD.", + [3]byte{148, 61, 201}: "Asahi Net, Inc.", + [3]byte{148, 64, 162}: "Anywave Communication Technologies, Inc.", + [3]byte{148, 68, 68}: "LG Innotek", + [3]byte{148, 68, 82}: "Belkin International Inc.", + [3]byte{148, 70, 150}: "BaudTec Corporation", + [3]byte{148, 74, 9}: "BitWise Controls", + [3]byte{148, 74, 12}: "Sercomm Corporation", + [3]byte{148, 80, 71}: "Rechnerbetriebsgruppe", + [3]byte{148, 80, 137}: "SimonsVoss Technologies GmbH", + [3]byte{148, 81, 3}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 81, 61}: "iSmart Alarm, Inc.", + [3]byte{148, 81, 191}: "Hyundai ESG", + [3]byte{148, 83, 48}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{148, 84, 147}: "Rigado, LLC", + [3]byte{148, 87, 165}: "Hewlett Packard", + [3]byte{148, 89, 7}: "Shanghai HITE-BELDEN Network Technology Co., Ltd.", + [3]byte{148, 89, 45}: "EKE Building Technology Systems Ltd", + [3]byte{148, 91, 126}: "TRILOBIT LTDA.", + [3]byte{148, 97, 30}: "Wata Electronics Co.,Ltd. ", + [3]byte{148, 97, 36}: "Pason Systems", + [3]byte{148, 98, 105}: "ARRIS Group, Inc.", + [3]byte{148, 99, 209}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 101, 45}: "OnePlus Technology (Shenzhen) Co., Ltd", + [3]byte{148, 101, 156}: "Intel Corporate", + [3]byte{148, 102, 231}: "WOM Engineering", + [3]byte{148, 112, 210}: "WINFIRM TECHNOLOGY", + [3]byte{148, 113, 172}: "TCT mobile ltd", + [3]byte{148, 117, 110}: "QinetiQ North America", + [3]byte{148, 118, 183}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 119, 43}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{148, 124, 62}: "Polewall Norge AS", + [3]byte{148, 129, 164}: "Azuray Technologies", + [3]byte{148, 133, 122}: "Evantage Industries Corp", + [3]byte{148, 134, 205}: "SEOUL ELECTRONICS&TELECOM", + [3]byte{148, 134, 212}: "Surveillance Pro Corporation", + [3]byte{148, 135, 124}: "ARRIS Group, Inc.", + [3]byte{148, 136, 21}: "Infinique Worldwide Inc", + [3]byte{148, 136, 84}: "Texas Instruments", + [3]byte{148, 136, 94}: "Surfilter Network Technology Co., Ltd. ", + [3]byte{148, 139, 3}: "EAGET Innovation and Technology Co., Ltd.", + [3]byte{148, 139, 193}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 141, 80}: "Beamex Oy Ab", + [3]byte{148, 142, 137}: "INDUSTRIAS UNIDAS SA DE CV", + [3]byte{148, 143, 238}: "Verizon Telematics", + [3]byte{148, 146, 188}: "SYNTECH(HK) TECHNOLOGY LIMITED", + [3]byte{148, 148, 38}: "Apple, Inc.", + [3]byte{148, 149, 160}: "Google, Inc.", + [3]byte{148, 152, 162}: "Shanghai LISTEN TECH.LTD", + [3]byte{148, 153, 1}: "Shenzhen YITOA Digital Appliance CO.,LTD", + [3]byte{148, 154, 169}: "Microsoft Corporation", + [3]byte{148, 155, 253}: "Trans New Technology, Inc.", + [3]byte{148, 156, 85}: "Alta Data Technologies", + [3]byte{148, 159, 62}: "Sonos, Inc.", + [3]byte{148, 159, 63}: "Optek Digital Technology company limited", + [3]byte{148, 159, 180}: "ChengDu JiaFaAnTai Technology Co.,Ltd", + [3]byte{148, 160, 78}: "Bostex Technology Co., LTD", + [3]byte{148, 161, 162}: "AMPAK Technology, Inc.", + [3]byte{148, 167, 183}: "zte corporation", + [3]byte{148, 167, 188}: "BodyMedia, Inc.", + [3]byte{148, 170, 184}: "Joview(Beijing) Technology Co. Ltd.", + [3]byte{148, 171, 222}: "OMX Technology - FZE", + [3]byte{148, 172, 202}: "trivum technologies GmbH", + [3]byte{148, 174, 97}: "Alcatel Lucent", + [3]byte{148, 174, 227}: "Belden Hirschmann Industries (Suzhou) Ltd.", + [3]byte{148, 177, 10}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 178, 204}: "PIONEER CORPORATION", + [3]byte{148, 180, 15}: "Aruba Networks", + [3]byte{148, 184, 25}: "Nokia", + [3]byte{148, 184, 197}: "RuggedCom Inc.", + [3]byte{148, 185, 180}: "Aptos Technology", + [3]byte{148, 186, 49}: "Visiontec da Amazônia Ltda.", + [3]byte{148, 186, 86}: "Shenzhen Coship Electronics Co., Ltd.", + [3]byte{148, 187, 174}: "Husqvarna AB", + [3]byte{148, 191, 30}: "eflow Inc. / Smart Device Planning and Development Division", + [3]byte{148, 191, 149}: "Shenzhen Coship Electronics Co., Ltd", + [3]byte{148, 192, 20}: "Sorter Sp. j. Konrad Grzeszczyk MichaA, Ziomek", + [3]byte{148, 192, 56}: "Tallac Networks", + [3]byte{148, 193, 80}: "2Wire Inc", + [3]byte{148, 195, 228}: "SCA Schucker Gmbh & Co KG", + [3]byte{148, 196, 233}: "PowerLayer Microsystems HongKong Limited", + [3]byte{148, 198, 235}: "NOVA electronics, Inc.", + [3]byte{148, 199, 175}: "Raylios Technology", + [3]byte{148, 201, 96}: "Zhongshan B&T technology.co.,ltd", + [3]byte{148, 201, 98}: "Teseq AG", + [3]byte{148, 202, 15}: "Honeywell Analytics", + [3]byte{148, 204, 185}: "ARRIS Group, Inc.", + [3]byte{148, 205, 172}: "Creowave Oy", + [3]byte{148, 206, 44}: "Sony Mobile Communications AB", + [3]byte{148, 206, 49}: "CTS Limited", + [3]byte{148, 208, 25}: "Cydle Corp.", + [3]byte{148, 212, 23}: "GPI KOREA INC.", + [3]byte{148, 212, 105}: "Cisco Systems, Inc", + [3]byte{148, 214, 14}: "shenzhen yunmao information technologies co., ltd", + [3]byte{148, 215, 35}: "Shanghai DareGlobal Technologies Co.,Ltd", + [3]byte{148, 215, 113}: "Samsung Electronics Co.,Ltd", + [3]byte{148, 216, 89}: "TCT mobile ltd", + [3]byte{148, 217, 60}: "ENELPS", + [3]byte{148, 219, 73}: "SITCORP", + [3]byte{148, 219, 201}: "AzureWave Technology Inc.", + [3]byte{148, 219, 218}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{148, 221, 63}: "A+V Link Technologies, Corp.", + [3]byte{148, 222, 14}: "SmartOptics AS", + [3]byte{148, 222, 128}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{148, 223, 78}: "Wistron InfoComm(Kunshan)Co.,Ltd.", + [3]byte{148, 223, 88}: "IJ Electron CO.,Ltd.", + [3]byte{148, 224, 208}: "HealthStream Taiwan Inc.", + [3]byte{148, 226, 38}: "D. ORtiz Consulting, LLC", + [3]byte{148, 226, 253}: "Boge Kompressoren OTTO Boge GmbH & Co. KG", + [3]byte{148, 231, 17}: "Xirka Dama Persada PT", + [3]byte{148, 232, 72}: "FYLDE MICRO LTD", + [3]byte{148, 232, 197}: "ARRIS Group, Inc.", + [3]byte{148, 233, 106}: "Apple, Inc.", + [3]byte{148, 233, 121}: "Liteon Technology Corporation", + [3]byte{148, 233, 140}: "Nokia", + [3]byte{148, 235, 44}: "Google, Inc.", + [3]byte{148, 235, 205}: "BlackBerry RTS", + [3]byte{148, 241, 158}: "HUIZHOU MAORONG INTELLIGENT TECHNOLOGY CO.,LTD", + [3]byte{148, 242, 120}: "Elma Electronic", + [3]byte{148, 245, 81}: "Cadi Scientific Pte Ltd", + [3]byte{148, 246, 101}: "Ruckus Wireless", + [3]byte{148, 246, 146}: "Geminico co.,Ltd.", + [3]byte{148, 246, 163}: "Apple, Inc.", + [3]byte{148, 247, 32}: "Tianjin Deviser Electronics Instrument Co., Ltd", + [3]byte{148, 250, 232}: "Shenzhen Eycom Technology Co., Ltd ", + [3]byte{148, 251, 41}: "Zebra Technologies Inc.", + [3]byte{148, 251, 178}: "Shenzhen Gongjin Electronics Co.,Ltd", + [3]byte{148, 253, 29}: "WhereWhen Corp", + [3]byte{148, 253, 46}: "Shanghai Uniscope Technologies Co.,Ltd", + [3]byte{148, 254, 34}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{148, 254, 244}: "Sagemcom Broadband SAS", + [3]byte{152, 0, 193}: "GuangZhou CREATOR Technology Co.,Ltd.(CHINA)", + [3]byte{152, 1, 167}: "Apple, Inc.", + [3]byte{152, 2, 132}: "Theobroma Systems GmbH", + [3]byte{152, 2, 216}: "IEEE Registration Authority", + [3]byte{152, 3, 160}: "ABB n.v. Power Quality Products", + [3]byte{152, 3, 216}: "Apple, Inc.", + [3]byte{152, 7, 45}: "Texas Instruments", + [3]byte{152, 12, 130}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{152, 12, 165}: "Motorola (Wuhan) Mobility Technologies Communication Co., Ltd.", + [3]byte{152, 13, 46}: "HTC Corporation", + [3]byte{152, 14, 228}: "Private", + [3]byte{152, 16, 148}: "Shenzhen Vsun communication technology Co.,ltd", + [3]byte{152, 16, 232}: "Apple, Inc.", + [3]byte{152, 19, 51}: "zte corporation", + [3]byte{152, 22, 236}: "IC Intracom", + [3]byte{152, 29, 250}: "Samsung Electronics Co.,Ltd", + [3]byte{152, 30, 15}: "Jeelan (Shanghai Jeelan Technology Information Inc", + [3]byte{152, 31, 177}: "Shenzhen Lemon Network Technology Co.,Ltd", + [3]byte{152, 32, 142}: "Definium Technologies", + [3]byte{152, 35, 78}: "Micromedia AG", + [3]byte{152, 38, 42}: "Applied Research Associates, Inc", + [3]byte{152, 41, 29}: "Jaguar de Mexico, SA de CV", + [3]byte{152, 41, 63}: "Fujian Start Computer Equipment Co.,Ltd", + [3]byte{152, 44, 190}: "2Wire Inc", + [3]byte{152, 45, 86}: "Resolution Audio", + [3]byte{152, 47, 60}: "Sichuan Changhong Electric Ltd.", + [3]byte{152, 48, 0}: "Beijing KEMACOM Technologies Co., Ltd.", + [3]byte{152, 48, 113}: "DAIKYUNG VASCOM", + [3]byte{152, 52, 157}: "Krauss Maffei Technologies GmbH", + [3]byte{152, 53, 113}: "Sub10 Systems Ltd", + [3]byte{152, 53, 184}: "Assembled Products Corporation", + [3]byte{152, 55, 19}: "PT.Navicom Indonesia", + [3]byte{152, 57, 142}: "Samsung Electronics Co.,Ltd", + [3]byte{152, 59, 22}: "AMPAK Technology, Inc.", + [3]byte{152, 63, 159}: "China SSJ (Suzhou) Network Technology Inc.", + [3]byte{152, 64, 187}: "Dell Inc.", + [3]byte{152, 66, 70}: "SOL INDUSTRY PTE., LTD", + [3]byte{152, 67, 218}: "INTERTECH", + [3]byte{152, 71, 60}: "SHANGHAI SUNMON COMMUNICATION TECHNOGY CO.,LTD", + [3]byte{152, 74, 71}: "CHG Hospital Beds", + [3]byte{152, 75, 74}: "ARRIS Group, Inc.", + [3]byte{152, 75, 225}: "Hewlett Packard", + [3]byte{152, 76, 4}: "Zhangzhou Keneng Electrical Equipment Co Ltd", + [3]byte{152, 76, 211}: "Mantis Deposition", + [3]byte{152, 78, 151}: "Starlight Marketing (H. K.) Ltd.", + [3]byte{152, 79, 238}: "Intel Corporate", + [3]byte{152, 82, 177}: "Samsung Electronics Co.,Ltd", + [3]byte{152, 84, 27}: "Intel Corporate", + [3]byte{152, 87, 211}: "HON HAI-CCPBG PRECISION IND.CO.,LTD.", + [3]byte{152, 88, 138}: "SYSGRATION Ltd.", + [3]byte{152, 89, 69}: "Texas Instruments", + [3]byte{152, 90, 235}: "Apple, Inc.", + [3]byte{152, 91, 176}: "KMDATA INC.", + [3]byte{152, 92, 147}: "SBG Systems SAS", + [3]byte{152, 93, 70}: "PeopleNet Communication", + [3]byte{152, 93, 173}: "Texas Instruments", + [3]byte{152, 94, 27}: "ConversDigital Co., Ltd.", + [3]byte{152, 95, 211}: "Microsoft Corporation", + [3]byte{152, 96, 34}: "EMW Co., Ltd.", + [3]byte{152, 102, 234}: "Industrial Control Communications, Inc.", + [3]byte{152, 107, 61}: "ARRIS Group, Inc.", + [3]byte{152, 108, 245}: "zte corporation", + [3]byte{152, 109, 53}: "IEEE Registration Authority", + [3]byte{152, 109, 200}: "TOSHIBA MITSUBISHI-ELECTRIC INDUSTRIAL SYSTEMS CORPORATION", + [3]byte{152, 112, 232}: "INNATECH SDN BHD", + [3]byte{152, 115, 196}: "Sage Electronic Engineering LLC", + [3]byte{152, 116, 61}: "Shenzhen Jun Kai Hengye Technology Co. Ltd", + [3]byte{152, 116, 218}: "Infinix mobility limited", + [3]byte{152, 118, 182}: "Adafruit", + [3]byte{152, 119, 112}: "Pep Digital Technology (Guangzhou) Co., Ltd", + [3]byte{152, 123, 243}: "Texas Instruments", + [3]byte{152, 126, 70}: "Emizon Networks Limited", + [3]byte{152, 130, 23}: "Disruptive Ltd", + [3]byte{152, 131, 137}: "Samsung Electronics Co.,Ltd", + [3]byte{152, 132, 227}: "Texas Instruments", + [3]byte{152, 134, 177}: "Flyaudio corporation (China)", + [3]byte{152, 135, 68}: "Wuxi Hongda Science and Technology Co.,LTD", + [3]byte{152, 137, 237}: "Anadem Information Inc.", + [3]byte{152, 139, 93}: "Sagemcom Broadband SAS", + [3]byte{152, 139, 173}: "Corintech Ltd.", + [3]byte{152, 142, 52}: "ZHEJIANG BOXSAM ELECTRONIC CO.,LTD", + [3]byte{152, 142, 74}: "NOXUS(BEIJING) TECHNOLOGY CO.,LTD", + [3]byte{152, 142, 221}: "TE Connectivity Limerick", + [3]byte{152, 144, 128}: "Linkpower Network System Inc Ltd.", + [3]byte{152, 144, 150}: "Dell Inc.", + [3]byte{152, 147, 204}: "LG ELECTRONICS INC", + [3]byte{152, 148, 73}: "Skyworth Wireless Technology Ltd.", + [3]byte{152, 151, 209}: "MitraStar Technology Corp.", + [3]byte{152, 158, 99}: "Apple, Inc.", + [3]byte{152, 164, 14}: "Snap, Inc.", + [3]byte{152, 167, 176}: "MCST ZAO", + [3]byte{152, 170, 60}: "Will i-tech Co., Ltd.", + [3]byte{152, 170, 215}: "BLUE WAVE NETWORKING CO LTD", + [3]byte{152, 170, 252}: "IEEE Registration Authority", + [3]byte{152, 176, 57}: "Nokia", + [3]byte{152, 182, 233}: "Nintendo Co.,Ltd", + [3]byte{152, 184, 227}: "Apple, Inc.", + [3]byte{152, 187, 30}: "BYD Precision Manufacture Company Ltd.", + [3]byte{152, 188, 87}: "SVA TECHNOLOGIES CO.LTD", + [3]byte{152, 188, 153}: "Edeltech Co.,Ltd.", + [3]byte{152, 190, 148}: "IBM", + [3]byte{152, 192, 235}: "Global Regency Ltd", + [3]byte{152, 200, 69}: "PacketAccess", + [3]byte{152, 203, 39}: "Galore Networks Pvt. Ltd.", + [3]byte{152, 205, 180}: "Virident Systems, Inc.", + [3]byte{152, 207, 83}: "BBK EDUCATIONAL ELECTRONICS CORP.,LTD.", + [3]byte{152, 210, 147}: "Google, Inc.", + [3]byte{152, 211, 49}: "Shenzhen Bolutek Technology Co.,Ltd.", + [3]byte{152, 211, 210}: "MEKRA Lang GmbH & Co. KG", + [3]byte{152, 214, 134}: "Chyi Lee industry Co., ltd.", + [3]byte{152, 214, 187}: "Apple, Inc.", + [3]byte{152, 214, 247}: "LG Electronics (Mobile Communications)", + [3]byte{152, 216, 140}: "Nortel Networks", + [3]byte{152, 218, 146}: "Vuzix Corporation", + [3]byte{152, 220, 217}: "UNITEC Co., Ltd.", + [3]byte{152, 221, 234}: "Infinix mobility limited", + [3]byte{152, 222, 208}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{152, 224, 217}: "Apple, Inc.", + [3]byte{152, 225, 101}: "Accutome", + [3]byte{152, 228, 118}: "Zentan", + [3]byte{152, 231, 154}: "Foxconn(NanJing) Communication Co.,Ltd.", + [3]byte{152, 231, 244}: "Hewlett Packard", + [3]byte{152, 231, 245}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{152, 232, 72}: "Axiim", + [3]byte{152, 236, 101}: "Cosesy ApS", + [3]byte{152, 238, 203}: "Wistron Infocomm (Zhongshan) Corporation", + [3]byte{152, 240, 88}: "Lynxspring, Incl.", + [3]byte{152, 240, 171}: "Apple, Inc.", + [3]byte{152, 241, 112}: "Murata Manufacturing Co., Ltd.", + [3]byte{152, 241, 153}: "NEC Platforms, Ltd.", + [3]byte{152, 244, 40}: "zte corporation", + [3]byte{152, 245, 55}: "zte corporation", + [3]byte{152, 245, 169}: "OHSUNG ELECTRONICS CO.,LTD.", + [3]byte{152, 248, 193}: "IDT Technology Limited", + [3]byte{152, 248, 219}: "Marini Impianti Industriali s.r.l.", + [3]byte{152, 250, 227}: "Xiaomi Communications Co Ltd", + [3]byte{152, 251, 18}: "Grand Electronics (HK) Ltd", + [3]byte{152, 252, 17}: "Cisco-Linksys, LLC", + [3]byte{152, 253, 116}: "ACT.CO.LTD", + [3]byte{152, 253, 180}: "Primax Electronics Ltd.", + [3]byte{152, 254, 3}: "Ericsson - North America", + [3]byte{152, 254, 148}: "Apple, Inc.", + [3]byte{152, 255, 106}: "OTEC(Shanghai)Technology Co.,Ltd.", + [3]byte{152, 255, 208}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{156, 1, 17}: "Shenzhen Newabel Electronic Co., Ltd.", + [3]byte{156, 2, 152}: "Samsung Electronics Co.,Ltd", + [3]byte{156, 3, 158}: "Beijing Winchannel Software Technology Co., Ltd", + [3]byte{156, 4, 115}: "Tecmobile (International) Ltd. ", + [3]byte{156, 4, 235}: "Apple, Inc.", + [3]byte{156, 6, 27}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{156, 6, 110}: "Hytera Communications Corporation Limited", + [3]byte{156, 13, 172}: "Tymphany HK Limited", + [3]byte{156, 14, 74}: "Shenzhen Vastking Electronic Co.,Ltd.", + [3]byte{156, 19, 171}: "Chanson Water Co., Ltd.", + [3]byte{156, 20, 101}: "Edata Elektronik San. ve Tic. A.Åž.", + [3]byte{156, 24, 116}: "Nokia Danmark A/S", + [3]byte{156, 28, 18}: "Aruba Networks", + [3]byte{156, 29, 88}: "Texas Instruments", + [3]byte{156, 30, 149}: "Actiontec Electronics, Inc", + [3]byte{156, 31, 221}: "Accupix Inc.", + [3]byte{156, 32, 123}: "Apple, Inc.", + [3]byte{156, 33, 106}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{156, 34, 14}: "TASCAN Systems GmbH", + [3]byte{156, 40, 64}: "Discovery Technology,LTD..", + [3]byte{156, 40, 191}: "Continental Automotive Czech Republic s.r.o.", + [3]byte{156, 40, 239}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 41, 63}: "Apple, Inc.", + [3]byte{156, 42, 112}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{156, 42, 131}: "Samsung Electronics Co.,Ltd", + [3]byte{156, 48, 102}: "RWE Effizienz GmbH", + [3]byte{156, 49, 120}: "Foshan Huadian Intelligent Communications Teachnologies Co.,Ltd", + [3]byte{156, 49, 182}: "Kulite Semiconductor Products Inc", + [3]byte{156, 50, 169}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{156, 52, 38}: "ARRIS Group, Inc.", + [3]byte{156, 53, 131}: "Nipro Diagnostics, Inc", + [3]byte{156, 53, 235}: "Apple, Inc.", + [3]byte{156, 55, 244}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 58, 175}: "Samsung Electronics Co.,Ltd", + [3]byte{156, 61, 207}: "NETGEAR", + [3]byte{156, 62, 170}: "EnvyLogic Co.,Ltd.", + [3]byte{156, 65, 124}: "Hame Technology Co., Limited ", + [3]byte{156, 68, 61}: "CHENGDU XUGUANG TECHNOLOGY CO, LTD", + [3]byte{156, 68, 166}: "SwiftTest, Inc.", + [3]byte{156, 69, 99}: "DIMEP Sistemas", + [3]byte{156, 74, 123}: "Nokia Corporation", + [3]byte{156, 76, 174}: "Mesa Labs", + [3]byte{156, 78, 32}: "Cisco Systems, Inc", + [3]byte{156, 78, 54}: "Intel Corporate", + [3]byte{156, 78, 142}: "ALT Systems Ltd", + [3]byte{156, 78, 191}: "BoxCast", + [3]byte{156, 79, 218}: "Apple, Inc.", + [3]byte{156, 80, 238}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{156, 82, 248}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 83, 205}: "ENGICAM s.r.l.", + [3]byte{156, 84, 28}: "Shenzhen My-power Technology Co.,Ltd", + [3]byte{156, 84, 202}: "Zhengzhou VCOM Science and Technology Co.,Ltd", + [3]byte{156, 85, 180}: "I.S.E. S.r.l.", + [3]byte{156, 87, 17}: "Feitian Xunda(Beijing) Aeronautical Information Technology Co., Ltd.", + [3]byte{156, 87, 173}: "Cisco Systems, Inc", + [3]byte{156, 91, 150}: "NMR Corporation", + [3]byte{156, 92, 141}: "FIREMAX INDÚSTRIA E COMÉRCIO DE PRODUTOS ELETRÔNICOS LTDA", + [3]byte{156, 92, 142}: "ASUSTek COMPUTER INC.", + [3]byte{156, 92, 249}: "Sony Mobile Communications AB", + [3]byte{156, 93, 18}: "Aerohive Networks Inc.", + [3]byte{156, 93, 149}: "VTC Electronics Corp.", + [3]byte{156, 94, 115}: "Calibre UK LTD", + [3]byte{156, 97, 29}: "Omni-ID USA, Inc.", + [3]byte{156, 97, 33}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{156, 98, 171}: "Sumavision Technologies Co.,Ltd", + [3]byte{156, 100, 94}: "Harman Consumer Group", + [3]byte{156, 101, 176}: "Samsung Electronics Co.,Ltd", + [3]byte{156, 101, 249}: "AcSiP Technology Corp.", + [3]byte{156, 102, 80}: "Glodio Technolies Co.,Ltd Tianjin Branch", + [3]byte{156, 104, 91}: "Octonion SA", + [3]byte{156, 106, 190}: "QEES ApS.", + [3]byte{156, 108, 21}: "Microsoft Corporation", + [3]byte{156, 116, 26}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 117, 20}: "Wildix srl", + [3]byte{156, 119, 170}: "NADASNV", + [3]byte{156, 121, 172}: "Suntec Software(Shanghai) Co., Ltd.", + [3]byte{156, 122, 3}: "Ciena Corporation", + [3]byte{156, 123, 210}: "NEOLAB Convergence", + [3]byte{156, 125, 163}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 128, 125}: "SYSCABLE Korea Inc.", + [3]byte{156, 128, 223}: "Arcadyan Technology Corporation", + [3]byte{156, 131, 191}: "PRO-VISION, Inc.", + [3]byte{156, 132, 191}: "Apple, Inc.", + [3]byte{156, 134, 218}: "Phoenix Geophysics Ltd.", + [3]byte{156, 136, 136}: "Simac Techniek NV", + [3]byte{156, 136, 173}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{156, 139, 160}: "Apple, Inc.", + [3]byte{156, 139, 241}: "The Warehouse Limited", + [3]byte{156, 141, 26}: "INTEG process group inc", + [3]byte{156, 141, 124}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{156, 141, 211}: "Leonton Technologies", + [3]byte{156, 142, 153}: "Hewlett Packard", + [3]byte{156, 142, 205}: "Amcrest Technologies", + [3]byte{156, 142, 220}: "Teracom Limited", + [3]byte{156, 147, 78}: "Xerox Corporation", + [3]byte{156, 147, 228}: "Private", + [3]byte{156, 149, 248}: "SmartDoor Systems, LLC", + [3]byte{156, 151, 38}: "Technicolor", + [3]byte{156, 152, 17}: "Guangzhou Sunrise Electronics Development Co., Ltd", + [3]byte{156, 153, 160}: "Xiaomi Communications Co Ltd", + [3]byte{156, 156, 29}: "Starkey Labs Inc.", + [3]byte{156, 157, 93}: "Raden Inc", + [3]byte{156, 161, 10}: "SCLE SFE", + [3]byte{156, 161, 52}: "Nike, Inc.", + [3]byte{156, 163, 169}: "Guangzhou Juan Optical and Electronical Tech Joint Stock Co., Ltd", + [3]byte{156, 163, 186}: "SAKURA Internet Inc.", + [3]byte{156, 165, 119}: "Osorno Enterprises Inc.", + [3]byte{156, 165, 192}: "vivo Mobile Communication Co., Ltd.", + [3]byte{156, 166, 157}: "Whaley Technology Co.Ltd", + [3]byte{156, 169, 228}: "zte corporation", + [3]byte{156, 172, 109}: "Universal Electronics, Inc.", + [3]byte{156, 173, 151}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{156, 173, 239}: "Obihai Technology, Inc.", + [3]byte{156, 174, 211}: "Seiko Epson Corporation", + [3]byte{156, 175, 111}: "ITEL MOBILE LIMITED", + [3]byte{156, 175, 202}: "Cisco Systems, Inc", + [3]byte{156, 176, 8}: "Ubiquitous Computing Technology Corporation", + [3]byte{156, 178, 6}: "PROCENTEC", + [3]byte{156, 178, 178}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 182, 84}: "Hewlett Packard", + [3]byte{156, 182, 208}: "Rivet Networks", + [3]byte{156, 183, 13}: "Liteon Technology Corporation", + [3]byte{156, 183, 147}: "Creatcomm Technology Inc.", + [3]byte{156, 187, 152}: "Shen Zhen RND Electronic Co.,LTD", + [3]byte{156, 189, 157}: "SkyDisk, Inc.", + [3]byte{156, 190, 224}: "Biosoundlab Co., Ltd.", + [3]byte{156, 192, 119}: "PrintCounts, LLC", + [3]byte{156, 192, 210}: "Conductix-Wampfler GmbH", + [3]byte{156, 193, 114}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 199, 166}: "AVM GmbH", + [3]byte{156, 199, 209}: "SHARP Corporation", + [3]byte{156, 200, 174}: "Becton, Dickinson and Company", + [3]byte{156, 202, 217}: "Nokia Corporation", + [3]byte{156, 204, 131}: "Juniper Networks", + [3]byte{156, 205, 130}: "CHENG UEI PRECISION INDUSTRY CO.,LTD", + [3]byte{156, 210, 30}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{156, 210, 75}: "zte corporation", + [3]byte{156, 211, 50}: "PLC Technology Ltd", + [3]byte{156, 211, 91}: "Samsung Electronics Co.,Ltd", + [3]byte{156, 211, 109}: "NETGEAR", + [3]byte{156, 212, 139}: "Innolux Technology Europe BV", + [3]byte{156, 214, 67}: "D-Link International", + [3]byte{156, 217, 23}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{156, 217, 203}: "Lesira Manufacturing Pty Ltd", + [3]byte{156, 218, 62}: "Intel Corporate", + [3]byte{156, 220, 113}: "Hewlett Packard Enterprise", + [3]byte{156, 221, 31}: "Intelligent Steward Co.,Ltd", + [3]byte{156, 223, 3}: "Harman/Becker Automotive Systems GmbH", + [3]byte{156, 223, 177}: "Shenzhen Crave Communication Co., LTD", + [3]byte{156, 225, 14}: "NCTech Ltd", + [3]byte{156, 225, 214}: "Junger Audio-Studiotechnik GmbH", + [3]byte{156, 226, 48}: "JULONG CO,.LTD.", + [3]byte{156, 227, 116}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{156, 230, 53}: "Nintendo Co., Ltd.", + [3]byte{156, 230, 231}: "Samsung Electronics Co.,Ltd", + [3]byte{156, 231, 189}: "Winduskorea co., Ltd", + [3]byte{156, 233, 81}: "Shenzhen Sang Fei Consumer Communications Ltd., Co.", + [3]byte{156, 235, 232}: "BizLink (Kunshan) Co.,Ltd", + [3]byte{156, 239, 213}: "Panda Wireless, Inc.", + [3]byte{156, 243, 135}: "Apple, Inc.", + [3]byte{156, 244, 142}: "Apple, Inc.", + [3]byte{156, 246, 26}: "UTC Fire and Security", + [3]byte{156, 246, 125}: "Ricardo Prague, s.r.o.", + [3]byte{156, 248, 219}: "shenzhen eyunmei technology co,.ltd", + [3]byte{156, 249, 56}: "AREVA NP GmbH", + [3]byte{156, 251, 213}: "vivo Mobile Communication Co., Ltd.", + [3]byte{156, 251, 241}: "MESOMATIC GmbH & Co.KG", + [3]byte{156, 252, 1}: "Apple, Inc.", + [3]byte{156, 252, 209}: "Aetheris Technology (Shanghai) Co., Ltd.", + [3]byte{156, 255, 190}: "OTSL Inc.", + [3]byte{160, 2, 220}: "Amazon Technologies Inc.", + [3]byte{160, 3, 99}: "Robert Bosch Healthcare GmbH", + [3]byte{160, 4, 62}: "Parker Hannifin Manufacturing Germany GmbH & Co. KG", + [3]byte{160, 4, 96}: "NETGEAR", + [3]byte{160, 6, 39}: "NEXPA System", + [3]byte{160, 7, 152}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 7, 182}: "Advanced Technical Support, Inc.", + [3]byte{160, 8, 111}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{160, 9, 237}: "Avaya Inc", + [3]byte{160, 10, 191}: "Wieson Technologies Co., Ltd.", + [3]byte{160, 11, 186}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{160, 12, 161}: "SKTB SKiT", + [3]byte{160, 16, 129}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 18, 144}: "Avaya Inc", + [3]byte{160, 18, 219}: "TABUCHI ELECTRIC CO.,LTD", + [3]byte{160, 19, 59}: "HiTi Digital, Inc.", + [3]byte{160, 19, 203}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{160, 20, 61}: "PARROT SA", + [3]byte{160, 22, 92}: "Triteka LTD", + [3]byte{160, 24, 40}: "Apple, Inc.", + [3]byte{160, 24, 89}: "Shenzhen Yidashi Electronics Co Ltd", + [3]byte{160, 25, 23}: "Bertel S.p.a.", + [3]byte{160, 27, 41}: "Sagemcom Broadband SAS", + [3]byte{160, 28, 5}: "NIMAX TELECOM CO.,LTD.", + [3]byte{160, 29, 72}: "Hewlett Packard", + [3]byte{160, 30, 11}: "MINIX Technology Limited", + [3]byte{160, 32, 166}: "Espressif Inc.", + [3]byte{160, 33, 149}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 33, 183}: "NETGEAR", + [3]byte{160, 35, 27}: "TeleComp R&D Corp.", + [3]byte{160, 43, 184}: "Hewlett Packard", + [3]byte{160, 44, 54}: "FN-LINK TECHNOLOGY LIMITED", + [3]byte{160, 46, 243}: "United Integrated Services Co., Led.", + [3]byte{160, 50, 153}: "Lenovo (Beijing) Co., Ltd.", + [3]byte{160, 54, 159}: "Intel Corporate", + [3]byte{160, 54, 240}: "Comprehensive Power", + [3]byte{160, 54, 250}: "Ettus Research LLC", + [3]byte{160, 57, 247}: "LG Electronics (Mobile Communications)", + [3]byte{160, 58, 117}: "PSS Belgium N.V.", + [3]byte{160, 59, 27}: "Inspire Tech", + [3]byte{160, 59, 227}: "Apple, Inc.", + [3]byte{160, 61, 111}: "Cisco Systems, Inc", + [3]byte{160, 62, 107}: "IEEE Registration Authority", + [3]byte{160, 64, 37}: "Actioncable, Inc.", + [3]byte{160, 64, 65}: "SAMWONFA Co.,Ltd.", + [3]byte{160, 65, 94}: "Opsens Solution Inc.", + [3]byte{160, 65, 167}: "NL Ministry of Defense", + [3]byte{160, 66, 63}: "Tyan Computer Corp", + [3]byte{160, 67, 219}: "Sitael S.p.A.", + [3]byte{160, 72, 28}: "Hewlett Packard", + [3]byte{160, 76, 91}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{160, 76, 193}: "Helixtech Corp.", + [3]byte{160, 78, 1}: "CENTRAL ENGINEERING co.,ltd.", + [3]byte{160, 78, 4}: "Nokia Corporation", + [3]byte{160, 79, 212}: "ADB Broadband Italia", + [3]byte{160, 81, 198}: "Avaya Inc", + [3]byte{160, 85, 79}: "Cisco Systems, Inc", + [3]byte{160, 85, 222}: "ARRIS Group, Inc.", + [3]byte{160, 86, 178}: "Harman/Becker Automotive Systems GmbH", + [3]byte{160, 89, 58}: "V.D.S. Video Display Systems srl", + [3]byte{160, 90, 164}: "Grand Products Nevada, Inc.", + [3]byte{160, 91, 33}: "ENVINET GmbH", + [3]byte{160, 93, 193}: "TMCT Co., LTD.", + [3]byte{160, 93, 231}: "DIRECTV, Inc.", + [3]byte{160, 94, 107}: "MELPER Co., Ltd.", + [3]byte{160, 96, 144}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 99, 145}: "NETGEAR", + [3]byte{160, 101, 24}: "VNPT TECHNOLOGY", + [3]byte{160, 103, 190}: "Sicon srl", + [3]byte{160, 105, 134}: "Wellav Technologies Ltd", + [3]byte{160, 106, 0}: "Verilink Corporation", + [3]byte{160, 108, 236}: "RIM", + [3]byte{160, 109, 9}: "Intelcan Technosystems Inc.", + [3]byte{160, 110, 80}: "Nanotek Elektronik Sistemler Ltd. Sti.", + [3]byte{160, 111, 170}: "LG Innotek", + [3]byte{160, 113, 169}: "Nokia Corporation", + [3]byte{160, 114, 44}: "HUMAX Co., Ltd.", + [3]byte{160, 115, 50}: "Cashmaster International Limited", + [3]byte{160, 115, 252}: "Rancore Technologies Private Limited", + [3]byte{160, 117, 145}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 119, 113}: "Vialis BV", + [3]byte{160, 120, 186}: "Pantech Co., Ltd.", + [3]byte{160, 130, 31}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 130, 172}: "Linear DMS Solutions Sdn. Bhd.", + [3]byte{160, 130, 199}: "P.T.I Co.,LTD", + [3]byte{160, 132, 203}: "SonicSensory,Inc.", + [3]byte{160, 134, 29}: "Chengdu Fuhuaxin Technology co.,Ltd", + [3]byte{160, 134, 198}: "Xiaomi Communications Co Ltd", + [3]byte{160, 134, 236}: "SAEHAN HITEC Co., Ltd", + [3]byte{160, 136, 105}: "Intel Corporate", + [3]byte{160, 136, 180}: "Intel Corporate", + [3]byte{160, 137, 228}: "Skyworth Digital Technology(Shenzhen) Co.,Ltd", + [3]byte{160, 138, 135}: "HuiZhou KaiYue Electronic Co.,Ltd", + [3]byte{160, 140, 21}: "Gerhard D. Wempe KG", + [3]byte{160, 140, 155}: "Xtreme Technologies Corp", + [3]byte{160, 140, 248}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{160, 140, 253}: "Hewlett Packard", + [3]byte{160, 141, 22}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{160, 144, 222}: "VEEDIMS,LLC", + [3]byte{160, 145, 105}: "LG Electronics (Mobile Communications)", + [3]byte{160, 145, 200}: "zte corporation", + [3]byte{160, 147, 71}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{160, 152, 5}: "OpenVox Communication Co Ltd", + [3]byte{160, 152, 237}: "Shandong Intelligent Optical Communication Development Co., Ltd.", + [3]byte{160, 153, 155}: "Apple, Inc.", + [3]byte{160, 154, 90}: "Time Domain", + [3]byte{160, 155, 189}: "Total Aviation Solutions Pty Ltd", + [3]byte{160, 157, 145}: "SoundBridge", + [3]byte{160, 158, 26}: "Polar Electro Oy", + [3]byte{160, 161, 48}: "DLI Taiwan Branch office", + [3]byte{160, 162, 60}: "GPMS", + [3]byte{160, 163, 59}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{160, 163, 226}: "Actiontec Electronics, Inc", + [3]byte{160, 166, 92}: "Supercomputing Systems AG", + [3]byte{160, 167, 99}: "Polytron Vertrieb GmbH", + [3]byte{160, 168, 205}: "Intel Corporate", + [3]byte{160, 170, 253}: "EraThink Technologies Corp.", + [3]byte{160, 171, 27}: "D-Link International", + [3]byte{160, 173, 161}: "JMR Electronics, Inc", + [3]byte{160, 177, 0}: "ShenZhen Cando Electronics Co.,Ltd", + [3]byte{160, 179, 204}: "Hewlett Packard", + [3]byte{160, 180, 55}: "GD Mission Systems", + [3]byte{160, 180, 165}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 181, 218}: "HongKong THTF Co., Ltd", + [3]byte{160, 182, 98}: "Acutvista Innovation Co., Ltd.", + [3]byte{160, 184, 248}: "Amgen U.S.A. Inc.", + [3]byte{160, 185, 237}: "Skytap", + [3]byte{160, 186, 184}: "Pixon Imaging", + [3]byte{160, 187, 62}: "IEEE Registration Authority", + [3]byte{160, 191, 80}: "S.C. ADD-PRODUCTION S.R.L.", + [3]byte{160, 191, 165}: "CORESYS", + [3]byte{160, 194, 222}: "Costar Video Systems", + [3]byte{160, 195, 222}: "Triton Electronic Systems Ltd.", + [3]byte{160, 196, 165}: "SYGN HOUSE CO.,LTD", + [3]byte{160, 197, 98}: "ARRIS Group, Inc.", + [3]byte{160, 197, 137}: "Intel Corporate", + [3]byte{160, 198, 236}: "ShenZhen ANYK Technology Co.,LTD", + [3]byte{160, 203, 253}: "Samsung Electronics Co.,Ltd", + [3]byte{160, 204, 43}: "Murata Manufacturing Co., Ltd.", + [3]byte{160, 206, 200}: "CE LINK LIMITED", + [3]byte{160, 207, 91}: "Cisco Systems, Inc", + [3]byte{160, 209, 42}: "AXPRO Technology Inc.", + [3]byte{160, 211, 122}: "Intel Corporate", + [3]byte{160, 211, 133}: "AUMA Riester GmbH & Co. KG", + [3]byte{160, 211, 193}: "Hewlett Packard", + [3]byte{160, 215, 149}: "Apple, Inc.", + [3]byte{160, 218, 146}: "Nanjing Glarun Atten Technology Co. Ltd.", + [3]byte{160, 220, 4}: "Becker-Antriebe GmbH", + [3]byte{160, 221, 151}: "PolarLink Technologies, Ltd", + [3]byte{160, 221, 229}: "SHARP Corporation", + [3]byte{160, 222, 5}: "JSC Irbis-T", + [3]byte{160, 224, 175}: "Cisco Systems, Inc", + [3]byte{160, 226, 1}: "AVTrace Ltd.(China)", + [3]byte{160, 226, 90}: "Amicus SK, s.r.o.", + [3]byte{160, 226, 149}: "DAT System Co.,Ltd", + [3]byte{160, 228, 83}: "Sony Mobile Communications AB", + [3]byte{160, 228, 203}: "ZyXEL Communications Corporation", + [3]byte{160, 229, 52}: "Stratec Biomedical AG", + [3]byte{160, 229, 233}: "enimai Inc", + [3]byte{160, 230, 248}: "Texas Instruments", + [3]byte{160, 233, 219}: "Ningbo FreeWings Technologies Co.,Ltd", + [3]byte{160, 235, 118}: "AirCUVE Inc.", + [3]byte{160, 236, 128}: "zte corporation", + [3]byte{160, 236, 249}: "Cisco Systems, Inc", + [3]byte{160, 237, 205}: "Apple, Inc.", + [3]byte{160, 239, 132}: "Seine Image Int'l Co., Ltd", + [3]byte{160, 242, 23}: "GE Medical System(China) Co., Ltd. ", + [3]byte{160, 243, 193}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{160, 243, 228}: "Alcatel-Lucent IPD", + [3]byte{160, 244, 25}: "Nokia Corporation", + [3]byte{160, 244, 80}: "HTC Corporation", + [3]byte{160, 244, 89}: "FN-LINK TECHNOLOGY LIMITED", + [3]byte{160, 244, 121}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{160, 246, 253}: "Texas Instruments", + [3]byte{160, 248, 73}: "Cisco Systems, Inc", + [3]byte{160, 248, 149}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{160, 249, 224}: "VIVATEL COMPANY LIMITED", + [3]byte{160, 252, 110}: "Telegrafia a.s.", + [3]byte{160, 254, 145}: "AVAT Automation GmbH", + [3]byte{164, 1, 48}: "ABIsystems Co., LTD", + [3]byte{164, 2, 185}: "Intel Corporate", + [3]byte{164, 5, 158}: "STA Infinity LLP", + [3]byte{164, 8, 234}: "Murata Manufacturing Co., Ltd.", + [3]byte{164, 8, 245}: "Sagemcom Broadband SAS", + [3]byte{164, 9, 203}: "Alfred Kaercher GmbH & Co KG", + [3]byte{164, 11, 237}: "Carry Technology Co.,Ltd", + [3]byte{164, 12, 195}: "Cisco Systems, Inc", + [3]byte{164, 13, 188}: "Xiamen Intretech Inc.", + [3]byte{164, 17, 99}: "IEEE Registration Authority", + [3]byte{164, 18, 66}: "NEC Platforms, Ltd.", + [3]byte{164, 19, 78}: "Luxul ", + [3]byte{164, 20, 55}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{164, 21, 102}: "Wei Fang Goertek Electronics Co.,Ltd", + [3]byte{164, 21, 136}: "ARRIS Group, Inc.", + [3]byte{164, 23, 49}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{164, 24, 117}: "Cisco Systems, Inc", + [3]byte{164, 27, 192}: "Fastec Imaging Corporation", + [3]byte{164, 31, 114}: "Dell Inc.", + [3]byte{164, 33, 138}: "Nortel Networks", + [3]byte{164, 35, 5}: "Open Networking Laboratory", + [3]byte{164, 36, 179}: "FlatFrog Laboratories AB", + [3]byte{164, 36, 221}: "Cambrionix Ltd", + [3]byte{164, 37, 27}: "Avaya Inc", + [3]byte{164, 41, 64}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{164, 41, 131}: "Boeing Defence Australia", + [3]byte{164, 41, 183}: "bluesky", + [3]byte{164, 43, 140}: "NETGEAR", + [3]byte{164, 43, 176}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{164, 44, 8}: "Masterwork Automodules", + [3]byte{164, 49, 17}: "ZIV", + [3]byte{164, 49, 53}: "Apple, Inc.", + [3]byte{164, 51, 209}: "Fibrlink Communications Co.,Ltd.", + [3]byte{164, 52, 217}: "Intel Corporate", + [3]byte{164, 56, 49}: "RF elements s.r.o.", + [3]byte{164, 56, 252}: "Plastic Logic", + [3]byte{164, 58, 105}: "Vers Inc", + [3]byte{164, 59, 250}: "IEEE Registration Authority", + [3]byte{164, 61, 120}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{164, 68, 209}: "Wingtech Group (HongKong)Limited", + [3]byte{164, 70, 107}: "EOC Technology", + [3]byte{164, 70, 250}: "AmTRAN Video Corporation", + [3]byte{164, 74, 211}: "ST Electronics(Shanghai) Co.,Ltd", + [3]byte{164, 75, 21}: "Sun Cupid Technology (HK) LTD", + [3]byte{164, 76, 17}: "Cisco Systems, Inc", + [3]byte{164, 78, 45}: "Adaptive Wireless Solutions, LLC", + [3]byte{164, 78, 49}: "Intel Corporate", + [3]byte{164, 79, 41}: "IEEE Registration Authority", + [3]byte{164, 80, 85}: "busware.de", + [3]byte{164, 81, 111}: "Microsoft Mobile Oy", + [3]byte{164, 82, 111}: "ADB Broadband Italia", + [3]byte{164, 83, 133}: "Weifang GoerTek Electronics Co., Ltd.", + [3]byte{164, 86, 2}: "fenglian Technology Co.,Ltd.", + [3]byte{164, 86, 27}: "MCOT Corporation", + [3]byte{164, 86, 48}: "Cisco Systems, Inc", + [3]byte{164, 88, 15}: "IEEE Registration Authority", + [3]byte{164, 90, 28}: "smart-electronic GmbH", + [3]byte{164, 92, 39}: "Nintendo Co., Ltd.", + [3]byte{164, 93, 54}: "Hewlett Packard", + [3]byte{164, 93, 161}: "ADB Broadband Italia", + [3]byte{164, 94, 96}: "Apple, Inc.", + [3]byte{164, 96, 17}: "Verifone", + [3]byte{164, 96, 50}: "MRV Communications (Networks) LTD", + [3]byte{164, 98, 223}: "DS Global. Co., LTD", + [3]byte{164, 103, 6}: "Apple, Inc.", + [3]byte{164, 104, 188}: "Private", + [3]byte{164, 108, 42}: "Cisco Systems, Inc", + [3]byte{164, 108, 193}: "LTi REEnergy GmbH", + [3]byte{164, 110, 121}: "DFT System Co.Ltd", + [3]byte{164, 112, 214}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{164, 113, 116}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{164, 119, 51}: "Google, Inc.", + [3]byte{164, 119, 96}: "Nokia Corporation", + [3]byte{164, 121, 228}: "KLINFO Corp", + [3]byte{164, 122, 164}: "ARRIS Group, Inc.", + [3]byte{164, 122, 207}: "VIBICOM COMMUNICATIONS INC.", + [3]byte{164, 123, 44}: "Nokia", + [3]byte{164, 123, 133}: "ULTIMEDIA Co Ltd,", + [3]byte{164, 124, 20}: "ChargeStorm AB", + [3]byte{164, 124, 31}: "Cobham plc", + [3]byte{164, 126, 57}: "zte corporation", + [3]byte{164, 129, 238}: "Nokia Corporation", + [3]byte{164, 130, 105}: "Datrium, Inc.", + [3]byte{164, 132, 49}: "Samsung Electronics Co.,Ltd", + [3]byte{164, 133, 107}: "Q Electronics Ltd", + [3]byte{164, 137, 91}: "ARK INFOSOLUTIONS PVT LTD", + [3]byte{164, 140, 219}: "Lenovo", + [3]byte{164, 141, 59}: "Vizio, Inc", + [3]byte{164, 142, 10}: "DeLaval International AB", + [3]byte{164, 144, 5}: "CHINA GREATWALL COMPUTER SHENZHEN CO.,LTD", + [3]byte{164, 147, 76}: "Cisco Systems, Inc", + [3]byte{164, 151, 187}: "Hitachi Industrial Equipment Systems Co.,Ltd", + [3]byte{164, 153, 71}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{164, 153, 129}: "FuJian Elite Power Tech CO.,LTD.", + [3]byte{164, 154, 88}: "Samsung Electronics Co.,Ltd", + [3]byte{164, 155, 19}: "Digital Check", + [3]byte{164, 155, 245}: "Hybridserver Tec GmbH", + [3]byte{164, 157, 73}: "Ketra, Inc.", + [3]byte{164, 158, 219}: "AutoCrib, Inc.", + [3]byte{164, 159, 133}: "Lyve Minds, Inc", + [3]byte{164, 159, 137}: "Shanghai Rui Rui Communication Technology Co.Ltd.", + [3]byte{164, 161, 194}: "Ericsson AB", + [3]byte{164, 161, 228}: "Innotube, Inc.", + [3]byte{164, 162, 74}: "Cisco SPVTG", + [3]byte{164, 164, 211}: "Bluebank Communication Technology Co.Ltd", + [3]byte{164, 166, 169}: "Private", + [3]byte{164, 168, 15}: "Shenzhen Coship Electronics Co., Ltd.", + [3]byte{164, 173, 0}: "Ragsdale Technology", + [3]byte{164, 173, 184}: "Vitec Group, Camera Dynamics Ltd", + [3]byte{164, 174, 154}: "Maestro Wireless Solutions ltd.", + [3]byte{164, 177, 33}: "Arantia 2010 S.L.", + [3]byte{164, 177, 151}: "Apple, Inc.", + [3]byte{164, 177, 233}: "Technicolor", + [3]byte{164, 177, 238}: "H. ZANDER GmbH & Co. KG", + [3]byte{164, 178, 167}: "Adaxys Solutions AG", + [3]byte{164, 179, 106}: "JSC SDO Chromatec", + [3]byte{164, 184, 5}: "Apple, Inc.", + [3]byte{164, 184, 24}: "PENTA Gesellschaft für elektronische Industriedatenverarbeitung mbH", + [3]byte{164, 185, 128}: "Parking BOXX Inc.", + [3]byte{164, 186, 118}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{164, 186, 219}: "Dell Inc.", + [3]byte{164, 187, 175}: "Lime Instruments", + [3]byte{164, 190, 97}: "EutroVision System, Inc.", + [3]byte{164, 191, 1}: "Intel Corporate", + [3]byte{164, 192, 199}: "ShenZhen Hitom Communication Technology Co..LTD", + [3]byte{164, 192, 225}: "Nintendo Co., Ltd.", + [3]byte{164, 193, 56}: "Telink Semiconductor (Taipei) Co. Ltd.", + [3]byte{164, 194, 171}: "Hangzhou LEAD-IT Information & Technology Co.,Ltd", + [3]byte{164, 195, 97}: "Apple, Inc.", + [3]byte{164, 196, 148}: "Intel Corporate", + [3]byte{164, 198, 79}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{164, 199, 222}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{164, 202, 160}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{164, 204, 50}: "Inficomm Co., Ltd", + [3]byte{164, 208, 148}: "Erwin Peters Systemtechnik GmbH", + [3]byte{164, 209, 140}: "Apple, Inc.", + [3]byte{164, 209, 143}: "Shenzhen Skyee Optical Fiber Communication Technology Ltd. ", + [3]byte{164, 209, 209}: "ECOtality North America", + [3]byte{164, 209, 210}: "Apple, Inc.", + [3]byte{164, 211, 181}: "GLITEL Stropkov, s.r.o.", + [3]byte{164, 213, 120}: "Texas Instruments", + [3]byte{164, 216, 86}: "Gimbal, Inc", + [3]byte{164, 216, 202}: "HONG KONG WATER WORLD TECHNOLOGY CO. LIMITED", + [3]byte{164, 217, 164}: "neXus ID Solutions AB", + [3]byte{164, 218, 63}: "Bionics Corp.", + [3]byte{164, 219, 46}: "Kingspan Environmental Ltd", + [3]byte{164, 219, 48}: "Liteon Technology Corporation", + [3]byte{164, 220, 190}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{164, 222, 80}: "Total Walther GmbH", + [3]byte{164, 222, 201}: "QLove Mobile Intelligence Information Technology (W.H.) Co. Ltd.", + [3]byte{164, 224, 230}: "FILIZOLA S.A. PESAGEM E AUTOMACAO", + [3]byte{164, 227, 46}: "Silicon & Software Systems Ltd.", + [3]byte{164, 227, 145}: "DENY FONTAINE", + [3]byte{164, 228, 184}: "BlackBerry RTS", + [3]byte{164, 229, 151}: "Gessler GmbH", + [3]byte{164, 230, 177}: "Shanghai Joindata Technology Co.,Ltd.", + [3]byte{164, 231, 49}: "Nokia Corporation", + [3]byte{164, 231, 228}: "Connex GmbH", + [3]byte{164, 233, 145}: "SISTEMAS AUDIOVISUALES ITELSIS S.L.", + [3]byte{164, 233, 163}: "Honest Technology Co., Ltd", + [3]byte{164, 235, 211}: "Samsung Electronics Co.,Ltd", + [3]byte{164, 237, 78}: "ARRIS Group, Inc.", + [3]byte{164, 238, 87}: "Seiko Epson Corporation", + [3]byte{164, 239, 82}: "Telewave Co., Ltd.", + [3]byte{164, 241, 232}: "Apple, Inc.", + [3]byte{164, 243, 193}: "Open Source Robotics Foundation, Inc.", + [3]byte{164, 245, 34}: "CHOFU SEISAKUSHO CO.,LTD", + [3]byte{164, 247, 208}: "LAN Accessories Co., Ltd.", + [3]byte{164, 251, 141}: "Hangzhou Dunchong Technology Co.Ltd", + [3]byte{164, 252, 206}: "Security Expert Ltd.", + [3]byte{168, 1, 128}: "IMAGO Technologies GmbH", + [3]byte{168, 6, 0}: "Samsung Electronics Co.,Ltd", + [3]byte{168, 12, 13}: "Cisco Systems, Inc", + [3]byte{168, 12, 202}: "Shenzhen Sundray Technologies Company Limited", + [3]byte{168, 17, 252}: "ARRIS Group, Inc.", + [3]byte{168, 19, 116}: "Panasonic Corporation AVC Networks Company", + [3]byte{168, 21, 77}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{168, 21, 89}: "Breathometer, Inc.", + [3]byte{168, 21, 214}: "Shenzhen Meione Technology CO., LTD", + [3]byte{168, 22, 178}: "LG Electronics (Mobile Communications)", + [3]byte{168, 23, 88}: "Elektronik System i UmeÃ¥ AB", + [3]byte{168, 27, 24}: "XTS CORP", + [3]byte{168, 27, 90}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{168, 27, 93}: "Foxtel Management Pty Ltd", + [3]byte{168, 27, 106}: "Texas Instruments", + [3]byte{168, 29, 22}: "AzureWave Technology Inc.", + [3]byte{168, 30, 132}: "QUANTA COMPUTER INC. ", + [3]byte{168, 31, 175}: "KRYPTON POLSKA", + [3]byte{168, 32, 102}: "Apple, Inc.", + [3]byte{168, 36, 235}: "ZAO NPO Introtest", + [3]byte{168, 38, 217}: "HTC Corporation", + [3]byte{168, 41, 76}: "Precision Optical Transceivers, Inc.", + [3]byte{168, 43, 214}: "Shina System Co., Ltd", + [3]byte{168, 48, 173}: "Wei Fang Goertek Electronics Co.,Ltd", + [3]byte{168, 50, 154}: "Digicom Futuristic Technologies Ltd.", + [3]byte{168, 57, 68}: "Actiontec Electronics, Inc", + [3]byte{168, 64, 65}: "Dragino Technology Co., Limited", + [3]byte{168, 68, 129}: "Nokia Corporation", + [3]byte{168, 69, 205}: "Siselectron Technology LTD.", + [3]byte{168, 69, 233}: "Firich Enterprises CO., LTD.", + [3]byte{168, 71, 74}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{168, 73, 165}: "Lisantech Co., Ltd.", + [3]byte{168, 78, 63}: "Hitron Technologies. Inc", + [3]byte{168, 84, 178}: "Wistron Neweb Corporation", + [3]byte{168, 85, 106}: "Pocketnet Technology Inc.", + [3]byte{168, 87, 78}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{168, 88, 64}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{168, 91, 120}: "Apple, Inc.", + [3]byte{168, 91, 176}: "Shenzhen Dehoo Technology Co.,Ltd", + [3]byte{168, 91, 243}: "Audivo GmbH", + [3]byte{168, 94, 228}: "12Sided Technology, LLC", + [3]byte{168, 96, 182}: "Apple, Inc.", + [3]byte{168, 97, 170}: "Cloudview Limited", + [3]byte{168, 98, 162}: "JIWUMEDIA CO., LTD.", + [3]byte{168, 99, 223}: "DISPLAIRE CORPORATION", + [3]byte{168, 99, 242}: "Texas Instruments", + [3]byte{168, 100, 5}: "nimbus 9, Inc", + [3]byte{168, 101, 178}: "DONGGUAN YISHANG ELECTRONIC TECHNOLOGY CO., LIMITED", + [3]byte{168, 102, 127}: "Apple, Inc.", + [3]byte{168, 106, 111}: "RIM", + [3]byte{168, 106, 193}: "HanbitEDS Co., Ltd.", + [3]byte{168, 107, 173}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{168, 112, 165}: "UniComm Inc.", + [3]byte{168, 114, 133}: "IDT, INC.", + [3]byte{168, 116, 29}: "PHOENIX CONTACT Electronics GmbH", + [3]byte{168, 117, 214}: "FreeTek International Co., Ltd.", + [3]byte{168, 117, 226}: "Aventura Technologies, Inc.", + [3]byte{168, 119, 111}: "Zonoff", + [3]byte{168, 123, 57}: "Nokia Corporation", + [3]byte{168, 124, 1}: "Samsung Electronics Co.,Ltd", + [3]byte{168, 126, 51}: "Nokia Danmark A/S", + [3]byte{168, 128, 56}: "ShenZhen MovingComm Technology Co., Limited", + [3]byte{168, 129, 149}: "Samsung Electronics Co.,Ltd", + [3]byte{168, 129, 241}: "BMEYE B.V.", + [3]byte{168, 130, 127}: "CIBN Oriental Network(Beijing) CO.,Ltd", + [3]byte{168, 134, 221}: "Apple, Inc.", + [3]byte{168, 135, 146}: "Broadband Antenna Tracking Systems", + [3]byte{168, 135, 237}: "ARC Wireless LLC", + [3]byte{168, 136, 8}: "Apple, Inc.", + [3]byte{168, 140, 238}: "MicroMade Galka i Drozdz sp.j.", + [3]byte{168, 141, 123}: "SunDroid Global limited.", + [3]byte{168, 142, 36}: "Apple, Inc.", + [3]byte{168, 144, 8}: "Beijing Yuecheng Technology Co. Ltd.", + [3]byte{168, 146, 44}: "LG Electronics (Mobile Communications)", + [3]byte{168, 147, 82}: "SHANGHAI ZHONGMI COMMUNICATION TECHNOLOGY CO.,LTD", + [3]byte{168, 147, 230}: "JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LTD", + [3]byte{168, 149, 176}: "Aker Subsea Ltd", + [3]byte{168, 150, 138}: "Apple, Inc.", + [3]byte{168, 151, 220}: "IBM", + [3]byte{168, 152, 198}: "Shinbo Co., Ltd.", + [3]byte{168, 153, 92}: "aizo ag", + [3]byte{168, 155, 16}: "inMotion Ltd.", + [3]byte{168, 157, 33}: "Cisco Systems, Inc", + [3]byte{168, 157, 210}: "Shanghai DareGlobal Technologies Co.,Ltd", + [3]byte{168, 159, 186}: "Samsung Electronics Co.,Ltd", + [3]byte{168, 160, 137}: "Tactical Communications", + [3]byte{168, 161, 152}: "TCT mobile ltd", + [3]byte{168, 165, 226}: "MSF-Vathauer Antriebstechnik GmbH & Co KG ", + [3]byte{168, 166, 72}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{168, 166, 104}: "zte corporation", + [3]byte{168, 167, 149}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{168, 173, 61}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{168, 176, 174}: "LEONI ", + [3]byte{168, 177, 212}: "Cisco Systems, Inc", + [3]byte{168, 185, 179}: "ESSYS", + [3]byte{168, 187, 80}: "WiZ IoT Company Limited", + [3]byte{168, 187, 207}: "Apple, Inc.", + [3]byte{168, 189, 26}: "Honey Bee (Hong Kong) Limited", + [3]byte{168, 189, 39}: "Hewlett Packard Enterprise", + [3]byte{168, 189, 58}: "UNIONMAN TECHNOLOGY CO.,LTD", + [3]byte{168, 194, 34}: "TM-Research Inc.", + [3]byte{168, 200, 58}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{168, 200, 127}: "Roqos, Inc.", + [3]byte{168, 202, 123}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{168, 203, 149}: "EAST BEST CO., LTD.", + [3]byte{168, 204, 197}: "Saab AB (publ)", + [3]byte{168, 206, 144}: "CVC", + [3]byte{168, 208, 227}: "Systech Electronics Ltd.", + [3]byte{168, 208, 229}: "Juniper Networks", + [3]byte{168, 210, 54}: "Lightware Visual Engineering", + [3]byte{168, 211, 200}: "Wachendorff Elektronik GmbH & Co. KG", + [3]byte{168, 211, 247}: "Arcadyan Technology Corporation", + [3]byte{168, 212, 9}: "USA 111 Inc", + [3]byte{168, 213, 121}: "Beijing Chushang Science and Technology Co.,Ltd", + [3]byte{168, 216, 40}: "Ascensia Diabetes Care", + [3]byte{168, 216, 138}: "Wyconn", + [3]byte{168, 224, 24}: "Nokia Corporation", + [3]byte{168, 227, 238}: "Sony Interactive Entertainment Inc.", + [3]byte{168, 229, 57}: "Moimstone Co.,Ltd", + [3]byte{168, 231, 5}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{168, 239, 38}: "Tritonwave", + [3]byte{168, 240, 56}: "SHEN ZHEN SHI JIN HUA TAI ELECTRONICS CO.,LTD", + [3]byte{168, 242, 116}: "Samsung Electronics Co.,Ltd", + [3]byte{168, 244, 112}: "Fujian Newland Communication Science Technologies Co.,Ltd.", + [3]byte{168, 247, 224}: "PLANET Technology Corporation", + [3]byte{168, 249, 75}: "Eltex Enterprise Ltd.", + [3]byte{168, 250, 216}: "Apple, Inc.", + [3]byte{168, 251, 112}: "WiseSec L.t.d", + [3]byte{168, 252, 183}: "Consolidated Resource Imaging", + [3]byte{170, 0, 0}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{170, 0, 1}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{170, 0, 2}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{170, 0, 3}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{170, 0, 4}: "DIGITAL EQUIPMENT CORPORATION", + [3]byte{172, 1, 66}: "Uriel Technologies SIA", + [3]byte{172, 2, 202}: "HI Solutions, Inc.", + [3]byte{172, 2, 207}: "RW Tecnologia Industria e Comercio Ltda", + [3]byte{172, 2, 239}: "Comsis", + [3]byte{172, 4, 11}: "Peloton Interactive, Inc", + [3]byte{172, 4, 129}: "Jiangsu Huaxing Electronics Co., Ltd.", + [3]byte{172, 6, 19}: "Senselogix Ltd", + [3]byte{172, 6, 199}: "ServerNet S.r.l.", + [3]byte{172, 10, 97}: "Labor S.r.L.", + [3]byte{172, 13, 27}: "LG Electronics (Mobile Communications)", + [3]byte{172, 13, 254}: "Ekon GmbH - myGEKKO", + [3]byte{172, 17, 211}: "Suzhou HOTEK Video Technology Co. Ltd", + [3]byte{172, 20, 97}: "ATAW Co., Ltd.", + [3]byte{172, 20, 210}: "wi-daq, inc.", + [3]byte{172, 22, 45}: "Hewlett Packard", + [3]byte{172, 23, 2}: "Fibar Group sp. z o.o.", + [3]byte{172, 24, 38}: "Seiko Epson Corporation", + [3]byte{172, 25, 159}: "SUNGROW POWER SUPPLY CO.,LTD.", + [3]byte{172, 31, 107}: "Super Micro Computer, Inc.", + [3]byte{172, 31, 215}: "Real Vision Technology Co.,Ltd.", + [3]byte{172, 32, 46}: "Hitron Technologies. Inc", + [3]byte{172, 32, 170}: "DMATEK Co., Ltd.", + [3]byte{172, 34, 11}: "ASUSTek COMPUTER INC.", + [3]byte{172, 35, 63}: "Shenzhen Minew Technologies Co., Ltd.", + [3]byte{172, 41, 58}: "Apple, Inc.", + [3]byte{172, 42, 12}: "CSR ZHUZHOU INSTITUTE CO.,LTD.", + [3]byte{172, 43, 110}: "Intel Corporate", + [3]byte{172, 45, 163}: "TXTR GmbH", + [3]byte{172, 47, 168}: "Humannix Co.,Ltd.", + [3]byte{172, 49, 157}: "Shenzhen TG-NET Botone Technology Co.,Ltd.", + [3]byte{172, 52, 203}: "Shanhai GBCOM Communication Technology Co. Ltd", + [3]byte{172, 54, 19}: "Samsung Electronics Co.,Ltd", + [3]byte{172, 55, 67}: "HTC Corporation", + [3]byte{172, 56, 112}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{172, 58, 122}: "Roku, Inc.", + [3]byte{172, 60, 11}: "Apple, Inc.", + [3]byte{172, 60, 180}: "Nilan A/S", + [3]byte{172, 61, 5}: "Instorescreen Aisa", + [3]byte{172, 61, 117}: "HANGZHOU ZHIWAY TECHNOLOGIES CO.,LTD.", + [3]byte{172, 63, 164}: "TAIYO YUDEN CO.,LTD", + [3]byte{172, 64, 234}: "C&T Solution Inc. ", + [3]byte{172, 65, 34}: "Eclipse Electronic Systems Inc.", + [3]byte{172, 68, 242}: "YAMAHA CORPORATION", + [3]byte{172, 71, 35}: "Genelec", + [3]byte{172, 72, 45}: "Ralinwi Nanjing Electronic Technology Co., Ltd.", + [3]byte{172, 74, 254}: "Hisense Broadband Multimedia Technology Co.,Ltd.", + [3]byte{172, 75, 200}: "Juniper Networks", + [3]byte{172, 78, 145}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{172, 79, 252}: "SVS-VISTEK GmbH", + [3]byte{172, 80, 54}: "Pi-Coral Inc", + [3]byte{172, 81, 53}: "MPI TECH", + [3]byte{172, 81, 238}: "Cambridge Communication Systems Ltd", + [3]byte{172, 84, 236}: "IEEE P1823 Standards Working Group", + [3]byte{172, 86, 44}: "LAVA INTERNATIONAL(H.K) LIMITED", + [3]byte{172, 88, 59}: "Human Assembler, Inc.", + [3]byte{172, 88, 123}: "JCT Healthcare", + [3]byte{172, 90, 20}: "Samsung Electronics Co.,Ltd", + [3]byte{172, 93, 16}: "Pace Americas", + [3]byte{172, 94, 140}: "Utillink", + [3]byte{172, 95, 62}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{172, 96, 182}: "Ericsson AB", + [3]byte{172, 97, 35}: "Drivven, Inc.", + [3]byte{172, 97, 117}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{172, 97, 234}: "Apple, Inc.", + [3]byte{172, 98, 13}: "Jabil Circuit(Wuxi) Co.,Ltd", + [3]byte{172, 99, 190}: "Amazon Technologies Inc.", + [3]byte{172, 100, 98}: "zte corporation", + [3]byte{172, 100, 221}: "IEEE Registration Authority", + [3]byte{172, 103, 6}: "Ruckus Wireless", + [3]byte{172, 103, 111}: "Electrocompaniet A.S.", + [3]byte{172, 107, 15}: "CADENCE DESIGN SYSTEMS INC", + [3]byte{172, 107, 172}: "Jenny Science AG", + [3]byte{172, 110, 26}: "Shenzhen Gongjin Electronics Co.,Ltd", + [3]byte{172, 111, 79}: "Enspert Inc", + [3]byte{172, 111, 187}: "TATUNG Technology Inc.", + [3]byte{172, 111, 217}: "Valueplus Inc.", + [3]byte{172, 114, 54}: "Lexking Technology Co., Ltd.", + [3]byte{172, 114, 137}: "Intel Corporate", + [3]byte{172, 116, 9}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{172, 122, 66}: "iConnectivity", + [3]byte{172, 122, 77}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{172, 123, 161}: "Intel Corporate", + [3]byte{172, 126, 138}: "Cisco Systems, Inc", + [3]byte{172, 127, 62}: "Apple, Inc.", + [3]byte{172, 128, 214}: "Hexatronic AB", + [3]byte{172, 129, 18}: "Gemtek Technology Co., Ltd.", + [3]byte{172, 129, 243}: "Nokia Corporation", + [3]byte{172, 131, 23}: "Shenzhen Furtunetel Communication Co., Ltd", + [3]byte{172, 131, 240}: "ImmediaTV Corporation", + [3]byte{172, 131, 243}: "AMPAK Technology, Inc.", + [3]byte{172, 132, 201}: "Sagemcom Broadband SAS", + [3]byte{172, 133, 61}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{172, 134, 116}: "Open Mesh, Inc.", + [3]byte{172, 134, 126}: "Create New Technology (HK) Limited Company", + [3]byte{172, 135, 163}: "Apple, Inc.", + [3]byte{172, 137, 149}: "AzureWave Technology Inc.", + [3]byte{172, 138, 205}: "ROGER D.Wensker, G.Wensker sp.j.", + [3]byte{172, 141, 20}: "Smartrove Inc", + [3]byte{172, 147, 47}: "Nokia Corporation", + [3]byte{172, 148, 3}: "Envision Peripherals Inc", + [3]byte{172, 154, 34}: "NXP Semiconductors", + [3]byte{172, 154, 150}: "Lantiq Deutschland GmbH", + [3]byte{172, 155, 10}: "Sony Corporation", + [3]byte{172, 155, 132}: "Smak Tecnologia e Automacao", + [3]byte{172, 156, 228}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{172, 158, 23}: "ASUSTek COMPUTER INC.", + [3]byte{172, 160, 22}: "Cisco Systems, Inc", + [3]byte{172, 162, 19}: "Shenzhen Bilian electronic CO.,LTD", + [3]byte{172, 162, 44}: "Baycity Technologies Ltd", + [3]byte{172, 163, 30}: "Aruba Networks", + [3]byte{172, 164, 48}: "Peerless AV", + [3]byte{172, 169, 25}: "TrekStor GmbH", + [3]byte{172, 169, 160}: "Audioengine, Ltd.", + [3]byte{172, 171, 46}: "Beijing LasNubes Technology Co., Ltd.", + [3]byte{172, 171, 141}: "Lyngso Marine A/S", + [3]byte{172, 171, 191}: "AthenTek Inc.", + [3]byte{172, 179, 19}: "ARRIS Group, Inc.", + [3]byte{172, 181, 125}: "Liteon Technology Corporation", + [3]byte{172, 183, 79}: "METEL s.r.o.", + [3]byte{172, 184, 89}: "Uniband Electronic Corp,", + [3]byte{172, 188, 50}: "Apple, Inc.", + [3]byte{172, 189, 11}: "IMAC CO.,LTD", + [3]byte{172, 190, 117}: "Ufine Technologies Co.,Ltd.", + [3]byte{172, 190, 182}: "Visualedge Technology Co., Ltd.", + [3]byte{172, 193, 238}: "Xiaomi Communications Co Ltd", + [3]byte{172, 194, 236}: "CLT INT'L IND. CORP.", + [3]byte{172, 195, 58}: "Samsung Electronics Co.,Ltd", + [3]byte{172, 197, 27}: "Zhuhai Pantum Electronics Co., Ltd.", + [3]byte{172, 197, 149}: "Graphite Systems", + [3]byte{172, 198, 98}: "MitraStar Technology Corp.", + [3]byte{172, 198, 152}: "Kohzu Precision Co., Ltd.", + [3]byte{172, 199, 63}: "VITSMO CO., LTD.", + [3]byte{172, 201, 53}: "Ness Corporation", + [3]byte{172, 202, 84}: "Telldus Technologies AB", + [3]byte{172, 202, 142}: "ODA Technologies", + [3]byte{172, 202, 171}: "Virtual Electric Inc", + [3]byte{172, 202, 186}: "Midokura Co., Ltd. ", + [3]byte{172, 203, 9}: "Hefcom Metering (Pty) Ltd", + [3]byte{172, 204, 142}: "Axis Communications AB", + [3]byte{172, 206, 143}: "HWA YAO TECHNOLOGIES CO., LTD", + [3]byte{172, 207, 35}: "Hi-flying electronics technology Co.,Ltd", + [3]byte{172, 207, 92}: "Apple, Inc.", + [3]byte{172, 207, 133}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{172, 208, 116}: "Espressif Inc.", + [3]byte{172, 209, 128}: "Crexendo Business Solutions, Inc.", + [3]byte{172, 209, 184}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{172, 211, 100}: "ABB SPA, ABB SACE DIV.", + [3]byte{172, 214, 87}: "Shaanxi GuoLian Digital TV Technology Co.,Ltd.", + [3]byte{172, 217, 214}: "tci GmbH", + [3]byte{172, 219, 218}: "Shenzhen Geniatech Inc, Ltd", + [3]byte{172, 220, 229}: "Procter & Gamble Company", + [3]byte{172, 222, 72}: "Private", + [3]byte{172, 224, 16}: "Liteon Technology Corporation", + [3]byte{172, 224, 105}: "ISAAC Instruments", + [3]byte{172, 226, 21}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{172, 227, 72}: "MadgeTech, Inc", + [3]byte{172, 228, 46}: "SK hynix", + [3]byte{172, 229, 240}: "Doppler Labs", + [3]byte{172, 230, 75}: "Shenzhen Baojia Battery Technology Co., Ltd.", + [3]byte{172, 231, 123}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{172, 232, 123}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{172, 232, 126}: "Bytemark Computer Consulting Ltd", + [3]byte{172, 233, 127}: "IoT Tech Limited", + [3]byte{172, 233, 170}: "Hay Systems Ltd", + [3]byte{172, 234, 106}: "GENIX INFOCOMM CO., LTD.", + [3]byte{172, 236, 128}: "ARRIS Group, Inc.", + [3]byte{172, 238, 59}: "6harmonics Inc", + [3]byte{172, 238, 158}: "Samsung Electronics Co.,Ltd", + [3]byte{172, 240, 178}: "Becker Electronics Taiwan Ltd.", + [3]byte{172, 241, 223}: "D-Link International", + [3]byte{172, 242, 197}: "Cisco Systems, Inc", + [3]byte{172, 247, 243}: "Xiaomi Communications Co Ltd", + [3]byte{172, 248, 92}: "Private", + [3]byte{172, 249, 126}: "ELESYS INC.", + [3]byte{172, 253, 147}: "Weifang GoerTek Electronics Co., Ltd.", + [3]byte{172, 253, 206}: "Intel Corporate", + [3]byte{172, 253, 236}: "Apple, Inc.", + [3]byte{176, 0, 180}: "Cisco Systems, Inc", + [3]byte{176, 5, 148}: "Liteon Technology Corporation", + [3]byte{176, 8, 191}: "Vital Connect, Inc.", + [3]byte{176, 9, 211}: "Avizia", + [3]byte{176, 16, 65}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{176, 18, 3}: "Dynamics Hong Kong Limited", + [3]byte{176, 18, 102}: "Futaba-Kikaku", + [3]byte{176, 20, 8}: "LIGHTSPEED INTERNATIONAL CO.", + [3]byte{176, 23, 67}: "EDISON GLOBAL CIRCUITS LLC", + [3]byte{176, 27, 124}: "Ontrol A.S.", + [3]byte{176, 27, 210}: "Le Shi Zhi Xin Electronic Technology (Tianjin) Limited", + [3]byte{176, 28, 145}: "Elim Co", + [3]byte{176, 31, 129}: "IEEE Registration Authority", + [3]byte{176, 36, 243}: "Progeny Systems", + [3]byte{176, 37, 170}: "Private", + [3]byte{176, 38, 40}: "Broadcom Limited", + [3]byte{176, 52, 149}: "Apple, Inc.", + [3]byte{176, 53, 141}: "Nokia Corporation", + [3]byte{176, 53, 159}: "Intel Corporate", + [3]byte{176, 56, 41}: "Siliconware Precision Industries Co., Ltd.", + [3]byte{176, 56, 80}: "Nanjing CAS-ZDC IOT SYSTEM CO.,LTD", + [3]byte{176, 61, 150}: "Vision Valley FZ LLC", + [3]byte{176, 62, 176}: "MICRODIA Ltd.", + [3]byte{176, 64, 137}: "Senient Systems LTD", + [3]byte{176, 65, 29}: "ITTIM Technologies", + [3]byte{176, 67, 93}: "NuLEDs, Inc.", + [3]byte{176, 69, 21}: "mira fitness,LLC.", + [3]byte{176, 69, 25}: "TCT mobile ltd", + [3]byte{176, 69, 69}: "YACOUB Automation GmbH", + [3]byte{176, 70, 252}: "MitraStar Technology Corp.", + [3]byte{176, 71, 191}: "Samsung Electronics Co.,Ltd", + [3]byte{176, 72, 26}: "Apple, Inc.", + [3]byte{176, 72, 122}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{176, 73, 95}: "OMRON HEALTHCARE Co., Ltd.", + [3]byte{176, 75, 191}: "PT HAN SUNG ELECTORONICS INDONESIA", + [3]byte{176, 76, 5}: "Fresenius Medical Care Deutschland GmbH", + [3]byte{176, 80, 188}: "SHENZHEN BASICOM ELECTRONIC CO.,LTD.", + [3]byte{176, 81, 142}: "Holl technology CO.Ltd.", + [3]byte{176, 82, 22}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{176, 87, 6}: "Vallox Oy", + [3]byte{176, 88, 196}: "Broadcast Microwave Services, Inc", + [3]byte{176, 89, 71}: "Shenzhen Qihu Intelligent Technology Company Limited", + [3]byte{176, 90, 218}: "Hewlett Packard", + [3]byte{176, 91, 31}: "THERMO FISHER SCIENTIFIC S.P.A.", + [3]byte{176, 91, 103}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{176, 92, 229}: "Nokia Corporation", + [3]byte{176, 97, 199}: "Ericsson-LG Enterprise", + [3]byte{176, 101, 99}: "Shanghai Railway Communication Factory", + [3]byte{176, 101, 189}: "Apple, Inc.", + [3]byte{176, 104, 182}: "Hangzhou OYE Technology Co. Ltd", + [3]byte{176, 105, 113}: "DEI Sales, Inc.", + [3]byte{176, 108, 191}: "3ality Digital Systems GmbH", + [3]byte{176, 112, 45}: "Apple, Inc.", + [3]byte{176, 114, 191}: "Murata Manufacturing Co., Ltd.", + [3]byte{176, 117, 12}: "QA Cafe", + [3]byte{176, 117, 77}: "Nokia", + [3]byte{176, 117, 213}: "zte corporation", + [3]byte{176, 119, 172}: "ARRIS Group, Inc.", + [3]byte{176, 120, 112}: "Wi-NEXT, Inc.", + [3]byte{176, 120, 240}: "Beijing HuaqinWorld Technology Co.,Ltd.", + [3]byte{176, 121, 8}: "Cummings Engineering", + [3]byte{176, 121, 60}: "Revolv Inc", + [3]byte{176, 121, 148}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{176, 125, 71}: "Cisco Systems, Inc", + [3]byte{176, 125, 98}: "Dipl.-Ing. H. Horstmann GmbH", + [3]byte{176, 126, 112}: "Zadara Storage Ltd.", + [3]byte{176, 127, 185}: "NETGEAR", + [3]byte{176, 128, 140}: "Laser Light Engines", + [3]byte{176, 129, 216}: "I-sys Corp", + [3]byte{176, 131, 254}: "Dell Inc.", + [3]byte{176, 134, 158}: "Chloride S.r.L", + [3]byte{176, 136, 7}: "Strata Worldwide", + [3]byte{176, 137, 0}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{176, 137, 145}: "LGE ", + [3]byte{176, 142, 26}: "URadio Systems Co., Ltd", + [3]byte{176, 144, 116}: "Fulan Electronics Limited", + [3]byte{176, 145, 34}: "Texas Instruments", + [3]byte{176, 145, 52}: "Taleo", + [3]byte{176, 145, 55}: "ISis ImageStream Internet Solutions, Inc", + [3]byte{176, 149, 142}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{176, 150, 108}: "Lanbowan Technology Ltd.", + [3]byte{176, 151, 58}: "E-Fuel Corporation", + [3]byte{176, 152, 159}: "LG CNS", + [3]byte{176, 153, 40}: "FUJITSU LIMITED", + [3]byte{176, 154, 226}: "STEMMER IMAGING GmbH", + [3]byte{176, 155, 212}: "GNH Software India Private Limited", + [3]byte{176, 159, 186}: "Apple, Inc.", + [3]byte{176, 161, 10}: "Pivotal Systems Corporation", + [3]byte{176, 162, 231}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{176, 163, 126}: "Qingdao Haier Telecom Co.,Ltd", + [3]byte{176, 167, 42}: "Ensemble Designs, Inc.", + [3]byte{176, 167, 55}: "Roku, Inc.", + [3]byte{176, 168, 110}: "Juniper Networks", + [3]byte{176, 170, 54}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{176, 170, 119}: "Cisco Systems, Inc", + [3]byte{176, 172, 250}: "FUJITSU LIMITED", + [3]byte{176, 173, 170}: "Avaya Inc", + [3]byte{176, 178, 143}: "Sagemcom Broadband SAS", + [3]byte{176, 178, 220}: "ZyXEL Communications Corporation", + [3]byte{176, 179, 43}: "Slican Sp. z o.o.", + [3]byte{176, 180, 72}: "Texas Instruments", + [3]byte{176, 184, 213}: "Nanjing Nengrui Auto Equipment CO.,Ltd", + [3]byte{176, 185, 138}: "NETGEAR", + [3]byte{176, 189, 109}: "Echostreams Innovative Solutions", + [3]byte{176, 189, 161}: "ZAKLAD ELEKTRONICZNY SIMS", + [3]byte{176, 191, 153}: "WIZITDONGDO", + [3]byte{176, 192, 144}: "Chicony Electronics Co., Ltd.", + [3]byte{176, 193, 40}: "Adler ELREHA GmbH", + [3]byte{176, 194, 5}: "BIONIME", + [3]byte{176, 194, 135}: "Technicolor CH USA Inc.", + [3]byte{176, 196, 108}: "Senseit", + [3]byte{176, 196, 231}: "Samsung Electronics Co.,Ltd", + [3]byte{176, 197, 84}: "D-Link International", + [3]byte{176, 197, 89}: "Samsung Electronics Co.,Ltd", + [3]byte{176, 197, 202}: "IEEE Registration Authority", + [3]byte{176, 198, 154}: "Juniper Networks", + [3]byte{176, 199, 69}: "BUFFALO.INC", + [3]byte{176, 200, 63}: "Jiangsu Cynray IOT Co., Ltd.", + [3]byte{176, 200, 173}: "People Power Company", + [3]byte{176, 201, 91}: "Beijing Symtech CO.,LTD", + [3]byte{176, 206, 24}: "Zhejiang shenghui lighting co.,Ltd", + [3]byte{176, 207, 77}: "MI-Zone Technology Ireland", + [3]byte{176, 208, 156}: "Samsung Electronics Co.,Ltd", + [3]byte{176, 210, 245}: "Vello Systems, Inc.", + [3]byte{176, 213, 157}: "Shenzhen Zowee Technology Co., Ltd", + [3]byte{176, 213, 204}: "Texas Instruments", + [3]byte{176, 215, 197}: "Logipix Ltd", + [3]byte{176, 215, 204}: "Tridonic GmbH & Co KG", + [3]byte{176, 218, 0}: "CERA ELECTRONIQUE", + [3]byte{176, 223, 58}: "Samsung Electronics Co.,Ltd", + [3]byte{176, 224, 60}: "TCT mobile ltd", + [3]byte{176, 226, 53}: "Xiaomi Communications Co Ltd", + [3]byte{176, 226, 229}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{176, 227, 157}: "CAT SYSTEM CO.,LTD.", + [3]byte{176, 229, 14}: "NRG SYSTEMS INC", + [3]byte{176, 229, 237}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{176, 231, 84}: "2Wire Inc", + [3]byte{176, 232, 146}: "Seiko Epson Corporation", + [3]byte{176, 233, 126}: "Advanced Micro Peripherals", + [3]byte{176, 236, 113}: "Samsung Electronics Co.,Ltd", + [3]byte{176, 236, 143}: "GMX SAS", + [3]byte{176, 236, 225}: "Private", + [3]byte{176, 238, 69}: "AzureWave Technology Inc.", + [3]byte{176, 238, 123}: "Roku, Inc", + [3]byte{176, 241, 163}: "Fengfan (BeiJing) Technology Co., Ltd. ", + [3]byte{176, 241, 188}: "Dhemax Ingenieros Ltda", + [3]byte{176, 241, 236}: "AMPAK Technology, Inc.", + [3]byte{176, 248, 147}: "Shanghai MXCHIP Information Technology Co., Ltd.", + [3]byte{176, 249, 99}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{176, 250, 235}: "Cisco Systems, Inc", + [3]byte{176, 254, 189}: "Private", + [3]byte{180, 0, 156}: "CableWorld Ltd.", + [3]byte{180, 1, 66}: "GCI Science & Technology Co.,LTD", + [3]byte{180, 4, 24}: "Smartchip Integrated Inc.", + [3]byte{180, 5, 102}: "SP Best Corporation Co., LTD.", + [3]byte{180, 7, 249}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{180, 8, 50}: "TC Communications", + [3]byte{180, 10, 198}: "DEXON Systems Ltd.", + [3]byte{180, 11, 68}: "Smartisan Technology Co., Ltd.", + [3]byte{180, 11, 122}: "Brusa Elektronik AG", + [3]byte{180, 12, 37}: "Palo Alto Networks", + [3]byte{180, 14, 150}: "HERAN ", + [3]byte{180, 14, 220}: "LG-Ericsson Co.,Ltd.", + [3]byte{180, 20, 137}: "Cisco Systems, Inc", + [3]byte{180, 21, 19}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{180, 23, 128}: "DTI Group Ltd", + [3]byte{180, 24, 209}: "Apple, Inc.", + [3]byte{180, 29, 239}: "Internet Laboratories, Inc.", + [3]byte{180, 33, 29}: "Beijing GuangXin Technology Co., Ltd", + [3]byte{180, 33, 138}: "Dog Hunter LLC", + [3]byte{180, 36, 231}: "Codetek Technology Co.,Ltd", + [3]byte{180, 40, 241}: "E-Prime Co., Ltd.", + [3]byte{180, 41, 61}: "Shenzhen Urovo Technology Co.,Ltd.", + [3]byte{180, 42, 14}: "Technicolor CH USA Inc.", + [3]byte{180, 42, 57}: "ORBIT MERRET, spol. s r. o.", + [3]byte{180, 44, 146}: "Zhejiang Weirong Electronic Co., Ltd", + [3]byte{180, 44, 190}: "Direct Payment Solutions Limited", + [3]byte{180, 48, 82}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{180, 49, 184}: "Aviwest", + [3]byte{180, 52, 108}: "MATSUNICHI DIGITAL TECHNOLOGY (HONG KONG) LIMITED", + [3]byte{180, 53, 100}: "Fujian Tian Cheng Electron Science & Technical Development Co.,Ltd.", + [3]byte{180, 53, 247}: "Zhejiang Pearmain Electronics Co.ltd.", + [3]byte{180, 54, 169}: "Fibocom Wireless Inc. ", + [3]byte{180, 54, 227}: "KBVISION GROUP", + [3]byte{180, 55, 65}: "Consert, Inc.", + [3]byte{180, 55, 209}: "IEEE Registration Authority", + [3]byte{180, 57, 52}: "Pen Generations, Inc.", + [3]byte{180, 57, 214}: "ProCurve Networking by HP", + [3]byte{180, 58, 40}: "Samsung Electronics Co.,Ltd", + [3]byte{180, 61, 178}: "Degreane Horizon", + [3]byte{180, 62, 59}: "Viableware, Inc", + [3]byte{180, 65, 122}: "SHENZHEN GONGJIN ELECTRONICS CO.,LT", + [3]byte{180, 67, 13}: "Broadlink Pty Ltd", + [3]byte{180, 71, 94}: "Avaya Inc", + [3]byte{180, 75, 210}: "Apple, Inc.", + [3]byte{180, 76, 194}: "NR ELECTRIC CO., LTD", + [3]byte{180, 81, 249}: "NB Software", + [3]byte{180, 82, 83}: "Seagate Technology", + [3]byte{180, 82, 125}: "Sony Mobile Communications AB", + [3]byte{180, 82, 126}: "Sony Mobile Communications AB", + [3]byte{180, 85, 112}: "Borea", + [3]byte{180, 86, 185}: "Teraspek Technologies Co.,Ltd", + [3]byte{180, 88, 97}: "CRemote, LLC", + [3]byte{180, 92, 164}: "Thing-talk Wireless Communication Technologies Corporation Limited", + [3]byte{180, 93, 80}: "Aruba Networks", + [3]byte{180, 97, 255}: "Lumigon A/S", + [3]byte{180, 98, 56}: "Exablox", + [3]byte{180, 98, 147}: "Samsung Electronics Co.,Ltd", + [3]byte{180, 98, 173}: "Elysia Germany GmbH", + [3]byte{180, 102, 152}: "Zealabs srl", + [3]byte{180, 103, 233}: "Qingdao GoerTek Technology Co., Ltd.", + [3]byte{180, 109, 53}: "Dalian Seasky Automation Co;Ltd", + [3]byte{180, 109, 131}: "Intel Corporate", + [3]byte{180, 115, 86}: "Hangzhou Treebear Networking Co., Ltd.", + [3]byte{180, 116, 67}: "Samsung Electronics Co.,Ltd", + [3]byte{180, 116, 71}: "CoreOS", + [3]byte{180, 116, 159}: "ASKEY COMPUTER CORP", + [3]byte{180, 117, 14}: "Belkin International Inc.", + [3]byte{180, 121, 167}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{180, 124, 41}: "Shenzhen Guzidi Technology Co.,Ltd", + [3]byte{180, 124, 156}: "Amazon Technologies Inc.", + [3]byte{180, 127, 94}: "Foresight Manufacture (S) Pte Ltd", + [3]byte{180, 130, 85}: "Research Products Corporation", + [3]byte{180, 130, 123}: "AKG Acoustics GmbH", + [3]byte{180, 130, 197}: "Relay2, Inc.", + [3]byte{180, 130, 254}: "ASKEY COMPUTER CORP", + [3]byte{180, 133, 71}: "Amptown System Company GmbH", + [3]byte{180, 137, 16}: "Coster T.E. S.P.A.", + [3]byte{180, 139, 25}: "Apple, Inc.", + [3]byte{180, 148, 78}: "WeTelecom Co., Ltd.", + [3]byte{180, 150, 145}: "Intel Corporate", + [3]byte{180, 152, 66}: "zte corporation", + [3]byte{180, 153, 76}: "Texas Instruments", + [3]byte{180, 153, 186}: "Hewlett Packard", + [3]byte{180, 156, 223}: "Apple, Inc.", + [3]byte{180, 157, 11}: "BQ", + [3]byte{180, 157, 180}: "Axion Technologies Inc.", + [3]byte{180, 158, 172}: "Imagik Int'l Corp", + [3]byte{180, 158, 230}: "SHENZHEN TECHNOLOGY CO LTD", + [3]byte{180, 164, 181}: "Zen Eye Co.,Ltd", + [3]byte{180, 164, 227}: "Cisco Systems, Inc", + [3]byte{180, 165, 169}: "MODI GmbH", + [3]byte{180, 165, 239}: "Sercomm Corporation.", + [3]byte{180, 168, 40}: "Shenzhen Concox Information Technology Co., Ltd", + [3]byte{180, 168, 43}: "Histar Digital Electronics Co., Ltd.", + [3]byte{180, 169, 90}: "Avaya Inc", + [3]byte{180, 169, 132}: "Symantec Corporation", + [3]byte{180, 169, 254}: "GHIA Technology (Shenzhen) LTD", + [3]byte{180, 170, 77}: "Ensequence, Inc.", + [3]byte{180, 171, 44}: "MtM Technology Corporation", + [3]byte{180, 174, 43}: "Microsoft", + [3]byte{180, 174, 111}: "Circle Reliance, Inc DBA Cranberry Networks", + [3]byte{180, 176, 23}: "Avaya Inc", + [3]byte{180, 177, 90}: "Siemens AG Energy Management Division", + [3]byte{180, 178, 101}: "DAEHO I&T", + [3]byte{180, 179, 98}: "zte corporation", + [3]byte{180, 179, 132}: "ShenZhen Figigantic Electronic Co.,Ltd", + [3]byte{180, 181, 47}: "Hewlett Packard", + [3]byte{180, 181, 66}: "Hubbell Power Systems, Inc.", + [3]byte{180, 181, 175}: "Minsung Electronics", + [3]byte{180, 182, 118}: "Intel Corporate", + [3]byte{180, 184, 89}: "Texa Spa", + [3]byte{180, 184, 141}: "Thuh Company", + [3]byte{180, 196, 78}: "VXL eTech Pvt Ltd", + [3]byte{180, 198, 248}: "Axilspot Communication", + [3]byte{180, 199, 153}: "Extreme Networks", + [3]byte{180, 200, 16}: "UMPI Elettronica", + [3]byte{180, 204, 233}: "PROSYST", + [3]byte{180, 206, 246}: "HTC Corporation", + [3]byte{180, 207, 219}: "Shenzhen Jiuzhou Electric Co.,LTD", + [3]byte{180, 209, 53}: "Cloudistics", + [3]byte{180, 213, 189}: "Intel Corporate", + [3]byte{180, 216, 169}: "BetterBots", + [3]byte{180, 216, 222}: "iota Computing, Inc.", + [3]byte{180, 221, 21}: "ControlThings Oy Ab", + [3]byte{180, 223, 59}: "Chromlech", + [3]byte{180, 223, 250}: "Litemax Electronics Inc.", + [3]byte{180, 224, 205}: "Fusion-io, Inc", + [3]byte{180, 225, 15}: "Dell Inc.", + [3]byte{180, 225, 196}: "Microsoft Mobile Oy", + [3]byte{180, 225, 235}: "Private", + [3]byte{180, 231, 130}: "Vivalnk", + [3]byte{180, 233, 176}: "Cisco Systems, Inc", + [3]byte{180, 237, 25}: "Pie Digital, Inc.", + [3]byte{180, 237, 84}: "Wohler Technologies", + [3]byte{180, 238, 180}: "ASKEY COMPUTER CORP", + [3]byte{180, 238, 212}: "Texas Instruments", + [3]byte{180, 239, 4}: "DAIHAN Scientific Co., Ltd.", + [3]byte{180, 239, 57}: "Samsung Electronics Co.,Ltd", + [3]byte{180, 239, 250}: "Lemobile Information Technology (Beijing) Co., Ltd.", + [3]byte{180, 240, 171}: "Apple, Inc.", + [3]byte{180, 242, 232}: "ARRIS Group, Inc.", + [3]byte{180, 243, 35}: "PETATEL INC.", + [3]byte{180, 248, 30}: "Kinova", + [3]byte{180, 251, 228}: "Ubiquiti Networks Inc.", + [3]byte{180, 252, 117}: "SEMA Electronics(HK) CO.,LTD", + [3]byte{180, 254, 140}: "Centro Sicurezza Italia SpA", + [3]byte{184, 0, 24}: "Htel", + [3]byte{184, 3, 5}: "Intel Corporate", + [3]byte{184, 4, 21}: "Bayan Audio", + [3]byte{184, 5, 171}: "zte corporation", + [3]byte{184, 8, 207}: "Intel Corporate", + [3]byte{184, 8, 215}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{184, 9, 138}: "Apple, Inc.", + [3]byte{184, 11, 157}: "ROPEX Industrie-Elektronik GmbH", + [3]byte{184, 19, 233}: "Trace Live Network", + [3]byte{184, 20, 19}: "Keen High Holding(HK) Ltd.", + [3]byte{184, 22, 25}: "ARRIS Group, Inc.", + [3]byte{184, 22, 219}: "CHANT SINCERE CO.,LTD", + [3]byte{184, 23, 194}: "Apple, Inc.", + [3]byte{184, 24, 111}: "ORIENTAL MOTOR CO., LTD.", + [3]byte{184, 25, 153}: "Nesys", + [3]byte{184, 29, 170}: "LG Electronics (Mobile Communications)", + [3]byte{184, 32, 231}: "Guangzhou Horizontal Information & Network Integration Co. Ltd", + [3]byte{184, 34, 79}: "SICHUAN TIANYI COMHEART TELECOMCO., LTD", + [3]byte{184, 36, 16}: "Magneti Marelli Slovakia s.r.o.", + [3]byte{184, 36, 26}: "SWEDA INFORMATICA LTDA", + [3]byte{184, 36, 240}: "SOYO Technology Development Co., Ltd.", + [3]byte{184, 38, 108}: "ANOV France", + [3]byte{184, 38, 212}: "Furukawa Industrial S.A. Produtos Elétricos", + [3]byte{184, 39, 235}: "Raspberry Pi Foundation", + [3]byte{184, 40, 139}: "Parker Hannifin Manufacturing (UK) Ltd", + [3]byte{184, 41, 247}: "Blaster Tech", + [3]byte{184, 42, 114}: "Dell Inc.", + [3]byte{184, 42, 220}: "EFR Europäische Funk-Rundsteuerung GmbH", + [3]byte{184, 44, 160}: "Honeywell HomMed", + [3]byte{184, 48, 168}: "Road-Track Telematics Development", + [3]byte{184, 50, 65}: "Wuhan Tianyu Information Industry Co., Ltd.", + [3]byte{184, 54, 216}: "Videoswitch", + [3]byte{184, 55, 101}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{184, 56, 97}: "Cisco Systems, Inc", + [3]byte{184, 56, 202}: "Kyokko Tsushin System CO.,LTD", + [3]byte{184, 58, 8}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{184, 58, 123}: "Worldplay (Canada) Inc.", + [3]byte{184, 58, 157}: "Alarm.com", + [3]byte{184, 61, 78}: "Shenzhen Cultraview Digital Technology Co.,Ltd Shanghai Branch", + [3]byte{184, 62, 89}: "Roku, Inc.", + [3]byte{184, 65, 95}: "ASP AG", + [3]byte{184, 67, 228}: "Vlatacom", + [3]byte{184, 68, 217}: "Apple, Inc.", + [3]byte{184, 71, 198}: "SanJet Technology Corp.", + [3]byte{184, 79, 213}: "Microsoft Corporation", + [3]byte{184, 80, 1}: "Extreme Networks", + [3]byte{184, 83, 172}: "Apple, Inc.", + [3]byte{184, 85, 16}: "Zioncom Electronics (Shenzhen) Ltd.", + [3]byte{184, 86, 189}: "ITT LLC", + [3]byte{184, 87, 216}: "Samsung Electronics Co.,Ltd", + [3]byte{184, 88, 16}: "NUMERA, INC.", + [3]byte{184, 90, 115}: "Samsung Electronics Co.,Ltd", + [3]byte{184, 90, 247}: "Ouya, Inc", + [3]byte{184, 90, 254}: "Handaer Communication Technology (Beijing) Co., Ltd", + [3]byte{184, 94, 123}: "Samsung Electronics Co.,Ltd", + [3]byte{184, 96, 145}: "Onnet Technologies and Innovations LLC", + [3]byte{184, 97, 111}: "Accton Technology Corp", + [3]byte{184, 98, 31}: "Cisco Systems, Inc", + [3]byte{184, 99, 188}: "ROBOTIS, Co, Ltd", + [3]byte{184, 100, 145}: "CK Telecom Ltd", + [3]byte{184, 101, 59}: "Bolymin, Inc.", + [3]byte{184, 105, 194}: "Sunitec Enterprise Co., Ltd.", + [3]byte{184, 107, 35}: "Toshiba", + [3]byte{184, 108, 232}: "Samsung Electronics Co.,Ltd", + [3]byte{184, 112, 244}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{184, 116, 36}: "Viessmann Elektronik GmbH", + [3]byte{184, 116, 71}: "Convergence Technologies", + [3]byte{184, 117, 192}: "PayPal, Inc.", + [3]byte{184, 118, 63}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{184, 119, 195}: "Decagon Devices, Inc.", + [3]byte{184, 120, 46}: "Apple, Inc.", + [3]byte{184, 120, 121}: "Roche Diagnostics GmbH", + [3]byte{184, 121, 126}: "Secure Meters (UK) Limited", + [3]byte{184, 122, 201}: "Siemens Ltd.", + [3]byte{184, 124, 242}: "Aerohive Networks Inc.", + [3]byte{184, 129, 152}: "Intel Corporate", + [3]byte{184, 134, 135}: "Liteon Technology Corporation", + [3]byte{184, 135, 30}: "Good Mind Industries Co., Ltd.", + [3]byte{184, 135, 168}: "Step Ahead Innovations Inc.", + [3]byte{184, 136, 227}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{184, 137, 129}: "Chengdu InnoThings Technology Co., Ltd.", + [3]byte{184, 137, 202}: "ILJIN ELECTRIC Co., Ltd.", + [3]byte{184, 138, 96}: "Intel Corporate", + [3]byte{184, 141, 18}: "Apple, Inc.", + [3]byte{184, 142, 58}: "Infinite Technologies JLT", + [3]byte{184, 142, 198}: "Stateless Networks", + [3]byte{184, 142, 223}: "Zencheer Communication Technology Co., Ltd.", + [3]byte{184, 143, 20}: "Analytica GmbH", + [3]byte{184, 146, 29}: "BG T&A", + [3]byte{184, 148, 210}: "Retail Innovation HTT AB", + [3]byte{184, 150, 116}: "AllDSP GmbH & Co. KG", + [3]byte{184, 151, 90}: "BIOSTAR Microtech Int'l Corp.", + [3]byte{184, 152, 176}: "Atlona Inc.", + [3]byte{184, 152, 247}: "Gionee Communication Equipment Co,Ltd.ShenZhen", + [3]byte{184, 153, 25}: "7signal Solutions, Inc", + [3]byte{184, 153, 176}: "Cohere Technologies", + [3]byte{184, 154, 205}: "ELITE OPTOELECTRONIC(ASIA)CO.,LTD", + [3]byte{184, 154, 237}: "OceanServer Technology, Inc", + [3]byte{184, 155, 201}: "SMC Networks Inc", + [3]byte{184, 155, 228}: "ABB Power Systems Power Generation", + [3]byte{184, 161, 117}: "Roku, Inc.", + [3]byte{184, 163, 134}: "D-Link International", + [3]byte{184, 163, 224}: "BenRui Technology Co.,Ltd", + [3]byte{184, 168, 175}: "Logic S.p.A.", + [3]byte{184, 172, 111}: "Dell Inc.", + [3]byte{184, 173, 62}: "BLUECOM", + [3]byte{184, 174, 110}: "Nintendo Co., Ltd.", + [3]byte{184, 174, 237}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{184, 175, 103}: "Hewlett Packard", + [3]byte{184, 177, 199}: "BT&COM CO.,LTD", + [3]byte{184, 178, 235}: "Googol Technology (HK) Limited", + [3]byte{184, 179, 220}: "DEREK (SHAOGUAN) LIMITED", + [3]byte{184, 180, 46}: "Gionee Communication Equipment Co,Ltd.ShenZhen", + [3]byte{184, 183, 215}: "2GIG Technologies", + [3]byte{184, 184, 30}: "Intel Corporate", + [3]byte{184, 185, 78}: "Shenzhen iBaby Labs, Inc.", + [3]byte{184, 186, 104}: "Xi'an Jizhong Digital Communication Co.,Ltd", + [3]byte{184, 186, 114}: "Cynove", + [3]byte{184, 187, 35}: "Guangdong Nufront CSC Co., Ltd", + [3]byte{184, 187, 109}: "ENERES Co.,Ltd.", + [3]byte{184, 187, 175}: "Samsung Electronics Co.,Ltd", + [3]byte{184, 188, 27}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{184, 189, 121}: "TrendPoint Systems", + [3]byte{184, 190, 191}: "Cisco Systems, Inc", + [3]byte{184, 191, 131}: "Intel Corporate", + [3]byte{184, 193, 162}: "Dragon Path Technologies Co., Limited", + [3]byte{184, 195, 191}: "Henan Chengshi NetWork Technology Co.,Ltd", + [3]byte{184, 196, 111}: "PRIMMCON INDUSTRIES INC", + [3]byte{184, 198, 142}: "Samsung Electronics Co.,Ltd", + [3]byte{184, 199, 22}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{184, 199, 93}: "Apple, Inc.", + [3]byte{184, 200, 85}: "Shanghai GBCOM Communication Technology Co.,Ltd.", + [3]byte{184, 202, 58}: "Dell Inc.", + [3]byte{184, 205, 147}: "Penetek, Inc", + [3]byte{184, 205, 167}: "Maxeler Technologies Ltd.", + [3]byte{184, 208, 111}: "GUANGZHOU HKUST FOK YING TUNG RESEARCH INSTITUTE", + [3]byte{184, 212, 157}: "M Seven System Ltd.", + [3]byte{184, 213, 11}: "Sunitec Enterprise Co.,Ltd", + [3]byte{184, 215, 175}: "Murata Manufacturing Co., Ltd.", + [3]byte{184, 216, 18}: "IEEE Registration Authority", + [3]byte{184, 217, 206}: "Samsung Electronics Co.,Ltd", + [3]byte{184, 218, 241}: "Strahlenschutz- Entwicklungs- und Ausruestungsgesellschaft mbH", + [3]byte{184, 218, 247}: "Advanced Photonics, Inc.", + [3]byte{184, 220, 135}: "IAI Corporation", + [3]byte{184, 223, 107}: "SpotCam Co., Ltd.", + [3]byte{184, 229, 137}: "Payter BV", + [3]byte{184, 230, 37}: "2Wire Inc", + [3]byte{184, 231, 121}: "9Solutions Oy", + [3]byte{184, 232, 86}: "Apple, Inc.", + [3]byte{184, 233, 55}: "Sonos, Inc.", + [3]byte{184, 234, 170}: "ICG NETWORKS CO.,ltd", + [3]byte{184, 236, 163}: "ZyXEL Communications Corporation", + [3]byte{184, 238, 101}: "Liteon Technology Corporation", + [3]byte{184, 238, 121}: "YWire Technologies, Inc.", + [3]byte{184, 240, 128}: "SPS, INC.", + [3]byte{184, 243, 23}: "iSun Smasher Communications Private Limited", + [3]byte{184, 244, 208}: "Herrmann Ultraschalltechnik GmbH & Co. Kg", + [3]byte{184, 245, 231}: "WayTools, LLC", + [3]byte{184, 246, 177}: "Apple, Inc.", + [3]byte{184, 247, 50}: "Aryaka Networks Inc", + [3]byte{184, 248, 40}: "Changshu Gaoshida Optoelectronic Technology Co. Ltd.", + [3]byte{184, 248, 131}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{184, 248, 190}: "BLUECOM", + [3]byte{184, 249, 52}: "Sony Mobile Communications AB", + [3]byte{184, 252, 154}: "Le Shi Zhi Xin Electronic Technology (Tianjin) Limited", + [3]byte{184, 253, 50}: "Zhejiang ROICX Microelectronics", + [3]byte{184, 255, 97}: "Apple, Inc.", + [3]byte{184, 255, 111}: "Shanghai Typrotech Technology Co.Ltd", + [3]byte{184, 255, 254}: "Texas Instruments", + [3]byte{188, 2, 0}: "Stewart Audio", + [3]byte{188, 2, 74}: "HMD Global Oy", + [3]byte{188, 5, 67}: "AVM GmbH", + [3]byte{188, 13, 165}: "Texas Instruments", + [3]byte{188, 15, 43}: "FORTUNE TECHGROUP CO.,LTD", + [3]byte{188, 15, 100}: "Intel Corporate", + [3]byte{188, 18, 94}: "Beijing WisVideo INC.", + [3]byte{188, 20, 1}: "Hitron Technologies. Inc", + [3]byte{188, 20, 133}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 20, 239}: "ITON Technology Limited", + [3]byte{188, 21, 166}: "Taiwan Jantek Electronics,Ltd.", + [3]byte{188, 21, 172}: "Vodafone Italia S.p.A.", + [3]byte{188, 22, 101}: "Cisco Systems, Inc", + [3]byte{188, 22, 245}: "Cisco Systems, Inc", + [3]byte{188, 26, 103}: "YF Technology Co., Ltd", + [3]byte{188, 32, 164}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 32, 186}: "Inspur (Shandong) Electronic Information Co., Ltd", + [3]byte{188, 37, 224}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{188, 37, 240}: "3D Display Technologies Co., Ltd.", + [3]byte{188, 38, 29}: "HONG KONG TECON TECHNOLOGY", + [3]byte{188, 40, 44}: "e-Smart Systems Pvt. Ltd", + [3]byte{188, 40, 70}: "NextBIT Computing Pvt. Ltd.", + [3]byte{188, 40, 214}: "Rowley Associates Limited", + [3]byte{188, 43, 107}: "Beijing Haier IC Design Co.,Ltd", + [3]byte{188, 43, 215}: "Revogi Innovation Co., Ltd.", + [3]byte{188, 44, 85}: "Bear Flag Design, Inc.", + [3]byte{188, 45, 152}: "ThinGlobal LLC", + [3]byte{188, 47, 61}: "vivo Mobile Communication Co., Ltd.", + [3]byte{188, 48, 91}: "Dell Inc.", + [3]byte{188, 48, 125}: "Wistron Neweb Corporation", + [3]byte{188, 48, 126}: "Wistron Neweb Corporation", + [3]byte{188, 52, 0}: "IEEE Registration Authority", + [3]byte{188, 53, 229}: "Hydro Systems Company", + [3]byte{188, 56, 210}: "Pandachip Limited", + [3]byte{188, 57, 166}: "CSUN System Technology Co.,LTD", + [3]byte{188, 57, 217}: "Z-TEC", + [3]byte{188, 58, 234}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{188, 59, 175}: "Apple, Inc.", + [3]byte{188, 62, 19}: "Accordance Systems Inc.", + [3]byte{188, 63, 143}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{188, 65, 0}: "CODACO ELECTRONIC s.r.o.", + [3]byte{188, 67, 119}: "Hang Zhou Huite Technology Co.,ltd.", + [3]byte{188, 68, 52}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{188, 68, 134}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 68, 176}: "Elastifile", + [3]byte{188, 69, 46}: "Knowledge Development for POF S.L.", + [3]byte{188, 70, 153}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{188, 71, 96}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 75, 121}: "SensingTek", + [3]byte{188, 76, 196}: "Apple, Inc.", + [3]byte{188, 77, 251}: "Hitron Technologies. Inc", + [3]byte{188, 78, 60}: "CORE STAFF CO., LTD.", + [3]byte{188, 78, 93}: "ZhongMiao Technology Co., Ltd.", + [3]byte{188, 81, 254}: "Swann communications Pty Ltd", + [3]byte{188, 82, 180}: "Nokia", + [3]byte{188, 82, 183}: "Apple, Inc.", + [3]byte{188, 84, 54}: "Apple, Inc.", + [3]byte{188, 84, 249}: "Drogoo Technology Co., Ltd.", + [3]byte{188, 92, 76}: "ELECOM CO.,LTD.", + [3]byte{188, 95, 244}: "ASRock Incorporation", + [3]byte{188, 95, 246}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{188, 96, 16}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{188, 96, 167}: "Sony Interactive Entertainment Inc.", + [3]byte{188, 98, 14}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{188, 98, 159}: "Telenet Systems P. Ltd.", + [3]byte{188, 100, 75}: "ARRIS Group, Inc.", + [3]byte{188, 102, 65}: "IEEE Registration Authority", + [3]byte{188, 102, 222}: "Shadow Creator Information Technology Co.,Ltd.", + [3]byte{188, 103, 28}: "Cisco Systems, Inc", + [3]byte{188, 103, 120}: "Apple, Inc.", + [3]byte{188, 103, 132}: "Environics Oy", + [3]byte{188, 106, 22}: "tdvine", + [3]byte{188, 106, 41}: "Texas Instruments", + [3]byte{188, 106, 47}: "Henge Docks LLC", + [3]byte{188, 106, 68}: "Commend International GmbH", + [3]byte{188, 107, 77}: "Nokia", + [3]byte{188, 108, 33}: "Apple, Inc.", + [3]byte{188, 110, 100}: "Sony Mobile Communications AB", + [3]byte{188, 110, 118}: "Green Energy Options Ltd", + [3]byte{188, 113, 193}: "XTrillion, Inc.", + [3]byte{188, 114, 177}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 116, 215}: "HangZhou JuRu Technology CO.,LTD", + [3]byte{188, 117, 116}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{188, 118, 78}: "Rackspace US, Inc.", + [3]byte{188, 118, 94}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 118, 112}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{188, 119, 55}: "Intel Corporate", + [3]byte{188, 119, 159}: "SBM Co., Ltd.", + [3]byte{188, 121, 173}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 125, 209}: "Radio Data Comms", + [3]byte{188, 129, 31}: "Ingate Systems", + [3]byte{188, 129, 153}: "BASIC Co.,Ltd.", + [3]byte{188, 131, 133}: "Microsoft Corporation", + [3]byte{188, 131, 167}: "SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD", + [3]byte{188, 133, 31}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 133, 86}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{188, 136, 147}: "VILLBAU Ltd.", + [3]byte{188, 138, 163}: "NHN Entertainment", + [3]byte{188, 138, 232}: "QING DAO HAIER TELECOM CO.,LTD.", + [3]byte{188, 139, 85}: "NPP ELIKS America Inc. DBA T&M Atlantic", + [3]byte{188, 140, 205}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{188, 141, 14}: "Nokia", + [3]byte{188, 146, 107}: "Apple, Inc.", + [3]byte{188, 150, 128}: "Shenzhen Gongjin Electronics Co.,Ltd", + [3]byte{188, 152, 137}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{188, 153, 188}: "FonSee Technology Inc.", + [3]byte{188, 156, 49}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{188, 156, 197}: "Beijing Huafei Technology Co., Ltd.", + [3]byte{188, 157, 165}: "DASCOM Europe GmbH", + [3]byte{188, 159, 239}: "Apple, Inc.", + [3]byte{188, 160, 66}: "SHANGHAI FLYCO ELECTRICAL APPLIANCE CO.,LTD", + [3]byte{188, 164, 225}: "Nabto", + [3]byte{188, 168, 166}: "Intel Corporate", + [3]byte{188, 169, 32}: "Apple, Inc.", + [3]byte{188, 169, 214}: "Cyber-Rain, Inc.", + [3]byte{188, 173, 40}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{188, 173, 171}: "Avaya Inc", + [3]byte{188, 174, 197}: "ASUSTek COMPUTER INC.", + [3]byte{188, 177, 129}: "SHARP CORPORATION", + [3]byte{188, 177, 243}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 179, 8}: "HONGKONG RAGENTEK COMMUNICATION TECHNOLOGY CO.,LIMITED", + [3]byte{188, 184, 82}: "Cybera, Inc.", + [3]byte{188, 186, 225}: "AREC Inc.", + [3]byte{188, 187, 201}: "Kellendonk Elektronik GmbH", + [3]byte{188, 188, 70}: "SKS Welding Systems GmbH", + [3]byte{188, 192, 15}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{188, 193, 104}: "DinBox Sverige AB", + [3]byte{188, 194, 58}: "Thomson Video Networks", + [3]byte{188, 195, 66}: "Panasonic System Networks Co., Ltd.", + [3]byte{188, 196, 147}: "Cisco Systems, Inc", + [3]byte{188, 198, 26}: "SPECTRA EMBEDDED SYSTEMS", + [3]byte{188, 198, 219}: "Nokia Corporation", + [3]byte{188, 200, 16}: "Cisco SPVTG", + [3]byte{188, 202, 181}: "ARRIS Group, Inc.", + [3]byte{188, 205, 69}: "VOISMART", + [3]byte{188, 207, 204}: "HTC Corporation", + [3]byte{188, 209, 31}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 209, 101}: "Cisco SPVTG", + [3]byte{188, 209, 119}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{188, 209, 211}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{188, 213, 182}: "d2d technologies", + [3]byte{188, 217, 64}: "ASR Co,.Ltd.", + [3]byte{188, 224, 157}: "Eoslink", + [3]byte{188, 229, 159}: "WATERWORLD Technology Co.,LTD", + [3]byte{188, 230, 63}: "Samsung Electronics Co.,Ltd", + [3]byte{188, 231, 103}: "Quanzhou TDX Electronics Co., Ltd", + [3]byte{188, 234, 43}: "CityCom GmbH", + [3]byte{188, 234, 250}: "Hewlett Packard", + [3]byte{188, 235, 95}: "Fujian Beifeng Telecom Technology Co., Ltd.", + [3]byte{188, 236, 35}: "SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD", + [3]byte{188, 236, 93}: "Apple, Inc.", + [3]byte{188, 238, 123}: "ASUSTek COMPUTER INC.", + [3]byte{188, 241, 242}: "Cisco Systems, Inc", + [3]byte{188, 242, 175}: "devolo AG", + [3]byte{188, 245, 172}: "LG Electronics (Mobile Communications)", + [3]byte{188, 246, 28}: "Geomodeling Wuxi Technology Co. Ltd.", + [3]byte{188, 246, 133}: "D-Link International", + [3]byte{188, 248, 17}: "Xiamen DNAKE Technology Co.,Ltd", + [3]byte{188, 254, 140}: "Altronic, LLC", + [3]byte{188, 255, 172}: "TOPCON CORPORATION", + [3]byte{192, 2, 141}: "WINSTAR Display CO.,Ltd", + [3]byte{192, 5, 194}: "ARRIS Group, Inc.", + [3]byte{192, 13, 126}: "Additech, Inc.", + [3]byte{192, 17, 115}: "Samsung Electronics Co.,Ltd", + [3]byte{192, 17, 166}: "Fort-Telecom ltd.", + [3]byte{192, 18, 66}: "Alpha Security Products", + [3]byte{192, 20, 61}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{192, 24, 133}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{192, 26, 218}: "Apple, Inc.", + [3]byte{192, 30, 155}: "Pixavi AS", + [3]byte{192, 33, 13}: "SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.", + [3]byte{192, 34, 80}: "Private", + [3]byte{192, 37, 6}: "AVM GmbH", + [3]byte{192, 37, 92}: "Cisco Systems, Inc", + [3]byte{192, 37, 103}: "Nexxt Solutions", + [3]byte{192, 37, 162}: "NEC Platforms, Ltd.", + [3]byte{192, 37, 233}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{192, 39, 185}: "Beijing National Railway Research & Design Institute of Signal & Communication Co., Ltd.", + [3]byte{192, 40, 141}: "Logitech, Inc", + [3]byte{192, 41, 115}: "Audyssey Laboratories Inc.", + [3]byte{192, 41, 243}: "XySystem", + [3]byte{192, 43, 252}: "iNES. applied informatics GmbH", + [3]byte{192, 44, 122}: "Shenzhen Horn Audio Co.,Ltd.", + [3]byte{192, 45, 238}: "Cuff", + [3]byte{192, 47, 241}: "Volta Networks", + [3]byte{192, 51, 94}: "Microsoft", + [3]byte{192, 52, 180}: "Gigastone Corporation", + [3]byte{192, 53, 128}: "A&R TECH", + [3]byte{192, 53, 189}: "Velocytech Aps", + [3]byte{192, 53, 197}: "Prosoft Systems LTD", + [3]byte{192, 56, 150}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{192, 56, 249}: "Nokia Danmark A/S", + [3]byte{192, 59, 143}: "Minicom Digital Signage", + [3]byte{192, 61, 70}: "Shanghai Sango Network Technology Co.,Ltd", + [3]byte{192, 62, 15}: "BSkyB Ltd", + [3]byte{192, 63, 14}: "NETGEAR", + [3]byte{192, 63, 42}: "Biscotti, Inc.", + [3]byte{192, 63, 213}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{192, 65, 246}: "LG ELECTRONICS INC", + [3]byte{192, 67, 1}: "Epec Oy", + [3]byte{192, 68, 227}: "Shenzhen Sinkna Electronics Co., LTD", + [3]byte{192, 73, 61}: "MAITRISE TECHNOLOGIQUE", + [3]byte{192, 74, 0}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{192, 74, 9}: "Zhejiang Everbright Communication Equip. Co,. Ltd", + [3]byte{192, 77, 247}: "SERELEC", + [3]byte{192, 86, 39}: "Belkin International Inc.", + [3]byte{192, 86, 227}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{192, 87, 188}: "Avaya Inc", + [3]byte{192, 88, 167}: "Pico Systems Co., Ltd.", + [3]byte{192, 94, 111}: "V. Stonkaus firma Kodinis Raktas", + [3]byte{192, 94, 121}: "SHENZHEN HUAXUN ARK TECHNOLOGIES CO.,LTD", + [3]byte{192, 97, 24}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{192, 98, 107}: "Cisco Systems, Inc", + [3]byte{192, 99, 148}: "Apple, Inc.", + [3]byte{192, 100, 198}: "Nokia Corporation", + [3]byte{192, 101, 153}: "Samsung Electronics Co.,Ltd", + [3]byte{192, 103, 175}: "Cisco Systems, Inc", + [3]byte{192, 108, 15}: "Dobbs Stanford", + [3]byte{192, 108, 109}: "MagneMotion, Inc.", + [3]byte{192, 112, 9}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{192, 123, 188}: "Cisco Systems, Inc", + [3]byte{192, 124, 209}: "PEGATRON CORPORATION", + [3]byte{192, 126, 64}: "SHENZHEN XDK COMMUNICATION EQUIPMENT CO.,LTD", + [3]byte{192, 129, 112}: "Effigis GeoSolutions", + [3]byte{192, 131, 10}: "2Wire Inc", + [3]byte{192, 132, 122}: "Apple, Inc.", + [3]byte{192, 132, 136}: "Finis Inc", + [3]byte{192, 133, 76}: "Ragentek Technology Group", + [3]byte{192, 136, 91}: "SnD Tech Co., Ltd.", + [3]byte{192, 137, 151}: "Samsung Electronics Co.,Ltd", + [3]byte{192, 138, 222}: "Ruckus Wireless", + [3]byte{192, 139, 111}: "S I Sistemas Inteligentes Eletrônicos Ltda", + [3]byte{192, 140, 96}: "Cisco Systems, Inc", + [3]byte{192, 145, 50}: "Patriot Memory", + [3]byte{192, 145, 52}: "ProCurve Networking by HP", + [3]byte{192, 151, 39}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{192, 152, 121}: "Acer Inc.", + [3]byte{192, 152, 229}: "University of Michigan", + [3]byte{192, 154, 113}: "XIAMEN MEITU MOBILE TECHNOLOGY CO.LTD", + [3]byte{192, 156, 4}: "Shaanxi GuoLian Digital TV Technology Co.,Ltd.", + [3]byte{192, 156, 146}: "COBY", + [3]byte{192, 157, 38}: "Topicon HK Lmd.", + [3]byte{192, 159, 5}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{192, 159, 66}: "Apple, Inc.", + [3]byte{192, 160, 187}: "D-Link International", + [3]byte{192, 160, 199}: "FAIRFIELD INDUSTRIES", + [3]byte{192, 160, 222}: "Multi Touch Oy", + [3]byte{192, 160, 226}: "Eden Innovations", + [3]byte{192, 161, 162}: "MarqMetrix", + [3]byte{192, 162, 109}: "Abbott Point of Care", + [3]byte{192, 163, 100}: "3D Systems Massachusetts", + [3]byte{192, 163, 158}: "EarthCam, Inc.", + [3]byte{192, 170, 104}: "OSASI Technos Inc.", + [3]byte{192, 172, 84}: "Sagemcom Broadband SAS", + [3]byte{192, 179, 57}: "Comigo Ltd.", + [3]byte{192, 179, 87}: "Yoshiki Electronics Industry Ltd.", + [3]byte{192, 183, 19}: "Beijing Xiaoyuer Technology Co. Ltd.", + [3]byte{192, 184, 177}: "BitBox Ltd", + [3]byte{192, 186, 230}: "Application Solutions (Electronics and Vision) Ltd", + [3]byte{192, 189, 66}: "ZPA Smart Energy a.s.", + [3]byte{192, 189, 209}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{192, 191, 192}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{192, 193, 192}: "Cisco-Linksys, LLC", + [3]byte{192, 195, 182}: "Automatic Systems", + [3]byte{192, 197, 32}: "Ruckus Wireless", + [3]byte{192, 197, 34}: "ARRIS Group, Inc.", + [3]byte{192, 197, 105}: "SHANGHAI LYNUC CNC TECHNOLOGY CO.,LTD", + [3]byte{192, 198, 135}: "Cisco SPVTG", + [3]byte{192, 201, 70}: "MITSUYA LABORATORIES INC.", + [3]byte{192, 201, 118}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{192, 203, 56}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{192, 204, 248}: "Apple, Inc.", + [3]byte{192, 206, 205}: "Apple, Inc.", + [3]byte{192, 207, 163}: "Creative Electronics & Software, Inc.", + [3]byte{192, 208, 18}: "Apple, Inc.", + [3]byte{192, 208, 68}: "Sagemcom Broadband SAS", + [3]byte{192, 211, 145}: "IEEE Registration Authority", + [3]byte{192, 211, 192}: "Samsung Electronics Co.,Ltd", + [3]byte{192, 217, 98}: "ASKEY COMPUTER CORP", + [3]byte{192, 217, 247}: "ShanDong Domor Intelligent S&T CO.,Ltd", + [3]byte{192, 218, 116}: "Hangzhou Sunyard Technology Co., Ltd.", + [3]byte{192, 220, 106}: "Qingdao Eastsoft Communication Technology Co.,LTD", + [3]byte{192, 223, 119}: "Conrad Electronic SE", + [3]byte{192, 228, 34}: "Texas Instruments", + [3]byte{192, 228, 45}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{192, 229, 78}: "ARIES Embedded GmbH", + [3]byte{192, 234, 228}: "Sonicwall", + [3]byte{192, 238, 64}: "Laird Technologies", + [3]byte{192, 238, 251}: "OnePlus Tech (Shenzhen) Ltd", + [3]byte{192, 241, 196}: "Pacidal Corporation Ltd.", + [3]byte{192, 242, 251}: "Apple, Inc.", + [3]byte{192, 246, 54}: "Hangzhou Kuaiyue Technologies, Ltd.", + [3]byte{192, 247, 157}: "Powercode", + [3]byte{192, 248, 218}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{192, 249, 69}: "Toshiba Toko Meter Systems Co., LTD.", + [3]byte{192, 249, 145}: "GME Standard Communications P/L", + [3]byte{192, 255, 212}: "NETGEAR", + [3]byte{196, 0, 6}: "Lipi Data Systems Ltd.", + [3]byte{196, 0, 73}: "Kamama", + [3]byte{196, 1, 66}: "MaxMedia Technology Limited", + [3]byte{196, 1, 124}: "Ruckus Wireless", + [3]byte{196, 1, 177}: "SeekTech INC", + [3]byte{196, 1, 206}: "PRESITION (2000) CO., LTD.", + [3]byte{196, 4, 21}: "NETGEAR", + [3]byte{196, 4, 123}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{196, 5, 40}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{196, 7, 47}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{196, 8, 74}: "Nokia", + [3]byte{196, 8, 128}: "Shenzhen UTEPO Tech Co., Ltd.", + [3]byte{196, 9, 56}: "FUJIAN STAR-NET COMMUNICATION CO.,LTD", + [3]byte{196, 10, 203}: "Cisco Systems, Inc", + [3]byte{196, 11, 203}: "Xiaomi Communications Co Ltd", + [3]byte{196, 14, 69}: "ACK Networks,Inc.", + [3]byte{196, 15, 9}: "Hermes electronic GmbH", + [3]byte{196, 16, 138}: "Ruckus Wireless", + [3]byte{196, 17, 224}: "Bull Group Co., Ltd", + [3]byte{196, 18, 245}: "D-Link International", + [3]byte{196, 19, 226}: "Aerohive Networks Inc.", + [3]byte{196, 20, 60}: "Cisco Systems, Inc", + [3]byte{196, 22, 250}: "Prysm Inc", + [3]byte{196, 23, 254}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{196, 25, 139}: "Dominion Voting Systems Corporation", + [3]byte{196, 25, 236}: "Qualisys AB", + [3]byte{196, 28, 255}: "Vizio, Inc", + [3]byte{196, 30, 206}: "HMI Sources Ltd.", + [3]byte{196, 33, 200}: "KYOCERA Corporation", + [3]byte{196, 35, 122}: "WhizNets Inc.", + [3]byte{196, 36, 46}: "Galvanic Applied Sciences Inc", + [3]byte{196, 38, 40}: "Airo Wireless", + [3]byte{196, 39, 149}: "Technicolor CH USA Inc.", + [3]byte{196, 40, 45}: "Embedded Intellect Pty Ltd", + [3]byte{196, 41, 29}: "KLEMSAN ELEKTRIK ELEKTRONIK SAN.VE TIC.AS.", + [3]byte{196, 44, 3}: "Apple, Inc.", + [3]byte{196, 47, 144}: "Hangzhou Hikvision Digital Technology Co.,Ltd.", + [3]byte{196, 48, 24}: "MCS Logic Inc.", + [3]byte{196, 52, 107}: "Hewlett Packard", + [3]byte{196, 54, 85}: "Shenzhen Fenglian Technology Co., Ltd.", + [3]byte{196, 54, 108}: "LG Innotek", + [3]byte{196, 54, 218}: "Rusteletech Ltd.", + [3]byte{196, 56, 211}: "TAGATEC CO.,LTD", + [3]byte{196, 57, 58}: "SMC Networks Inc", + [3]byte{196, 58, 159}: "Siconix Inc.", + [3]byte{196, 58, 190}: "Sony Mobile Communications AB", + [3]byte{196, 60, 60}: "CYBELEC SA", + [3]byte{196, 61, 199}: "NETGEAR", + [3]byte{196, 64, 68}: "RackTop Systems Inc.", + [3]byte{196, 66, 2}: "Samsung Electronics Co.,Ltd", + [3]byte{196, 67, 143}: "LG Electronics (Mobile Communications)", + [3]byte{196, 69, 103}: "SAMBON PRECISON and ELECTRONICS", + [3]byte{196, 69, 236}: "Shanghai Yali Electron Co.,LTD", + [3]byte{196, 70, 25}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{196, 71, 63}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{196, 72, 56}: "Satcom Direct, Inc.", + [3]byte{196, 73, 187}: "MITSUMI ELECTRIC CO.,LTD.", + [3]byte{196, 74, 208}: "FIREFLIES SYSTEMS", + [3]byte{196, 75, 68}: "Omniprint Inc.", + [3]byte{196, 75, 209}: "Wallys Communications Teachnologies Co.,Ltd.", + [3]byte{196, 78, 31}: "BlueN", + [3]byte{196, 78, 172}: "Shenzhen Shiningworth Technology Co., Ltd.", + [3]byte{196, 80, 6}: "Samsung Electronics Co.,Ltd", + [3]byte{196, 84, 68}: "QUANTA COMPUTER INC.", + [3]byte{196, 85, 166}: "Cadac Holdings Ltd", + [3]byte{196, 85, 194}: "Bach-Simpson", + [3]byte{196, 86, 0}: "Galleon Embedded Computing", + [3]byte{196, 86, 254}: "Lava International Ltd.", + [3]byte{196, 87, 110}: "Samsung Electronics Co.,Ltd", + [3]byte{196, 88, 194}: "Shenzhen TATFOOK Technology Co., Ltd.", + [3]byte{196, 89, 118}: "Fugoo Coorporation", + [3]byte{196, 93, 216}: "HDMI Forum", + [3]byte{196, 96, 68}: "Everex Electronics Limited", + [3]byte{196, 98, 107}: "ZPT Vigantice", + [3]byte{196, 98, 234}: "Samsung Electronics Co.,Ltd", + [3]byte{196, 99, 84}: "U-Raku, Inc.", + [3]byte{196, 100, 19}: "Cisco Systems, Inc", + [3]byte{196, 102, 153}: "vivo Mobile Communication Co., Ltd.", + [3]byte{196, 103, 181}: "Libratone A/S", + [3]byte{196, 105, 62}: "Turbulence Design Inc.", + [3]byte{196, 106, 183}: "Xiaomi Communications Co Ltd", + [3]byte{196, 107, 180}: "myIDkey", + [3]byte{196, 109, 241}: "DataGravity", + [3]byte{196, 110, 31}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{196, 112, 11}: "GUANGZHOU CHIP TECHNOLOGIES CO.,LTD", + [3]byte{196, 113, 48}: "Fon Technology S.L.", + [3]byte{196, 113, 254}: "Cisco Systems, Inc", + [3]byte{196, 114, 149}: "Cisco Systems, Inc", + [3]byte{196, 115, 30}: "Samsung Electronics Co.,Ltd", + [3]byte{196, 119, 171}: "Beijing ASU Tech Co.,Ltd", + [3]byte{196, 123, 47}: "Beijing JoinHope Image Technology Ltd.", + [3]byte{196, 123, 163}: "NAVIS Inc.", + [3]byte{196, 124, 141}: "IEEE Registration Authority", + [3]byte{196, 125, 70}: "FUJITSU LIMITED", + [3]byte{196, 125, 79}: "Cisco Systems, Inc", + [3]byte{196, 125, 204}: "Zebra Technologies Inc", + [3]byte{196, 125, 254}: "A.N. Solutions GmbH", + [3]byte{196, 127, 81}: "Inventek Systems", + [3]byte{196, 130, 63}: "Fujian Newland Auto-ID Tech. Co,.Ltd.", + [3]byte{196, 130, 78}: "Changzhou Uchip Electronics Co., LTD.", + [3]byte{196, 131, 111}: "Ciena Corporation", + [3]byte{196, 133, 8}: "Intel Corporate", + [3]byte{196, 134, 233}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{196, 136, 229}: "Samsung Electronics Co.,Ltd", + [3]byte{196, 142, 143}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{196, 143, 7}: "Shenzhen Yihao Hulian Science and Technology Co., Ltd.", + [3]byte{196, 145, 58}: "Shenzhen Sanland Electronic Co., ltd.", + [3]byte{196, 146, 76}: "KEISOKUKI CENTER CO.,LTD.", + [3]byte{196, 147, 0}: "8Devices", + [3]byte{196, 147, 19}: "100fio networks technology llc", + [3]byte{196, 147, 128}: "Speedytel technology", + [3]byte{196, 149, 162}: "SHENZHEN WEIJIU INDUSTRY AND TRADE DEVELOPMENT CO., LTD", + [3]byte{196, 152, 5}: "Minieum Networks, Inc", + [3]byte{196, 154, 2}: "LG Electronics (Mobile Communications)", + [3]byte{196, 157, 237}: "Microsoft Corporation", + [3]byte{196, 158, 65}: "G24 Power Limited", + [3]byte{196, 159, 243}: "Mciao Technologies, Inc.", + [3]byte{196, 163, 102}: "zte corporation", + [3]byte{196, 168, 29}: "D-Link International", + [3]byte{196, 170, 161}: "SUMMIT DEVELOPMENT, spol.s r.o.", + [3]byte{196, 173, 33}: "MEDIAEDGE Corporation", + [3]byte{196, 173, 241}: "GOPEACE Inc.", + [3]byte{196, 174, 18}: "Samsung Electronics Co.,Ltd", + [3]byte{196, 179, 1}: "Apple, Inc.", + [3]byte{196, 181, 18}: "General Electric Digital Energy", + [3]byte{196, 185, 205}: "Cisco Systems, Inc", + [3]byte{196, 186, 153}: "I+ME Actia Informatik und Mikro-Elektronik GmbH", + [3]byte{196, 186, 163}: "Beijing Winicssec Technologies Co., Ltd.", + [3]byte{196, 187, 76}: "Zebra Information Tech Co. Ltd", + [3]byte{196, 187, 234}: "Pakedge Device and Software Inc", + [3]byte{196, 189, 106}: "SKF GmbH", + [3]byte{196, 190, 132}: "Texas Instruments", + [3]byte{196, 190, 212}: "Avaya Inc", + [3]byte{196, 192, 174}: "MIDORI ELECTRONIC CO., LTD.", + [3]byte{196, 193, 159}: "National Oilwell Varco Instrumentation, Monitoring, and Optimization (NOV IMO)", + [3]byte{196, 199, 85}: "Beijing HuaqinWorld Technology Co.,Ltd", + [3]byte{196, 201, 25}: "Energy Imports Ltd", + [3]byte{196, 201, 236}: "Gugaoo HK Limited", + [3]byte{196, 202, 217}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{196, 205, 69}: "Beijing Boomsense Technology CO.,LTD.", + [3]byte{196, 209, 151}: "Ventia Utility Services", + [3]byte{196, 212, 137}: "JiangSu Joyque Information Industry Co.,Ltd", + [3]byte{196, 214, 85}: "Tercel technology co.,ltd", + [3]byte{196, 217, 135}: "Intel Corporate", + [3]byte{196, 218, 38}: "NOBLEX SA", + [3]byte{196, 218, 125}: "Ivium Technologies B.V.", + [3]byte{196, 224, 50}: "IEEE 1904.1 Working Group", + [3]byte{196, 225, 124}: "U2S co.", + [3]byte{196, 229, 16}: "Mechatro, Inc.", + [3]byte{196, 231, 190}: "SCSpro Co.,Ltd", + [3]byte{196, 233, 47}: "AB Sciex", + [3]byte{196, 233, 132}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{196, 234, 29}: "Technicolor", + [3]byte{196, 235, 227}: "RRCN SAS", + [3]byte{196, 237, 186}: "Texas Instruments", + [3]byte{196, 238, 174}: "VSS Monitoring", + [3]byte{196, 238, 245}: "II-VI Incorporated", + [3]byte{196, 239, 112}: "Home Skinovations", + [3]byte{196, 240, 129}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{196, 241, 209}: "BEIJING SOGOU TECHNOLOGY DEVELOPMENT CO., LTD.", + [3]byte{196, 244, 100}: "Spica international", + [3]byte{196, 245, 124}: "Brocade Communications Systems, Inc.", + [3]byte{196, 245, 165}: "Kumalift Co., Ltd.", + [3]byte{196, 252, 228}: "DishTV NZ Ltd", + [3]byte{196, 255, 31}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 0, 132}: "Cisco Systems, Inc", + [3]byte{200, 2, 16}: "LG Innotek", + [3]byte{200, 2, 88}: "ITW GSE ApS", + [3]byte{200, 2, 143}: "Nova Electronics (Shanghai) Co., Ltd.", + [3]byte{200, 2, 166}: "Beijing Newmine Technology", + [3]byte{200, 7, 24}: "TDSi", + [3]byte{200, 8, 233}: "LG Electronics", + [3]byte{200, 10, 169}: "QUANTA COMPUTER INC.", + [3]byte{200, 12, 200}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 14, 20}: "AVM Audiovisuelles Marketing und Computersysteme GmbH", + [3]byte{200, 14, 119}: "Le Shi Zhi Xin Electronic Technology (Tianjin) Limited", + [3]byte{200, 14, 149}: "OmniLync Inc.", + [3]byte{200, 16, 115}: "CENTURY OPTICOMM CO.,LTD", + [3]byte{200, 20, 81}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 20, 121}: "Samsung Electronics Co.,Ltd", + [3]byte{200, 22, 165}: "Masimo Corporation", + [3]byte{200, 22, 189}: "Qingdao Hisense Communications Co.,Ltd.", + [3]byte{200, 25, 247}: "Samsung Electronics Co.,Ltd", + [3]byte{200, 26, 254}: "DLOGIC GmbH", + [3]byte{200, 27, 92}: "BCTech", + [3]byte{200, 27, 107}: "Innova Security", + [3]byte{200, 30, 142}: "ADV Security (S) Pte Ltd", + [3]byte{200, 30, 231}: "Apple, Inc.", + [3]byte{200, 31, 102}: "Dell Inc.", + [3]byte{200, 31, 190}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 32, 142}: "Storagedata", + [3]byte{200, 33, 88}: "Intel Corporate", + [3]byte{200, 37, 225}: "Lemobile Information Technology (Beijing) Co., Ltd", + [3]byte{200, 41, 42}: "Barun Electronics", + [3]byte{200, 42, 20}: "Apple, Inc.", + [3]byte{200, 46, 148}: "Halfa Enterprise Co., Ltd.", + [3]byte{200, 49, 104}: "eZEX corporation", + [3]byte{200, 50, 50}: "Hunting Innova", + [3]byte{200, 51, 75}: "Apple, Inc.", + [3]byte{200, 52, 142}: "Intel Corporate", + [3]byte{200, 53, 184}: "Ericsson, EAB/RWI/K", + [3]byte{200, 56, 112}: "Samsung Electronics Co.,Ltd", + [3]byte{200, 58, 53}: "Tenda Technology Co., Ltd.", + [3]byte{200, 58, 107}: "Roku, Inc", + [3]byte{200, 59, 69}: "JRI", + [3]byte{200, 61, 151}: "Nokia Corporation", + [3]byte{200, 61, 212}: "CyberTAN Technology Inc.", + [3]byte{200, 61, 252}: "Pioneer DJ Corporation", + [3]byte{200, 62, 153}: "Texas Instruments", + [3]byte{200, 62, 167}: "KUNBUS GmbH", + [3]byte{200, 63, 38}: "Microsoft Corporation", + [3]byte{200, 63, 180}: "ARRIS Group, Inc.", + [3]byte{200, 69, 41}: "IMK Networks Co.,Ltd", + [3]byte{200, 69, 68}: "Asia Pacific CIS (Wuxi) Co, Ltd", + [3]byte{200, 69, 143}: "Wyler AG", + [3]byte{200, 71, 140}: "Beken Corporation", + [3]byte{200, 72, 245}: "MEDISON Xray Co., Ltd", + [3]byte{200, 76, 117}: "Cisco Systems, Inc", + [3]byte{200, 81, 149}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 86, 69}: "Intermas France", + [3]byte{200, 86, 99}: "Sunflex Europe GmbH", + [3]byte{200, 91, 118}: "LCFC(HeFei) Electronics Technology co., ltd", + [3]byte{200, 96, 0}: "ASUSTek COMPUTER INC.", + [3]byte{200, 100, 199}: "zte corporation", + [3]byte{200, 102, 44}: "Beijing Haitai Fangyuan High Technology Co,.Ltd.", + [3]byte{200, 102, 93}: "Aerohive Networks Inc.", + [3]byte{200, 103, 94}: "Aerohive Networks Inc.", + [3]byte{200, 105, 205}: "Apple, Inc.", + [3]byte{200, 108, 30}: "Display Systems Ltd", + [3]byte{200, 108, 135}: "ZyXEL Communications Corporation", + [3]byte{200, 108, 182}: "Optcom Co., Ltd.", + [3]byte{200, 111, 29}: "Apple, Inc.", + [3]byte{200, 114, 72}: "Aplicom Oy", + [3]byte{200, 115, 36}: "Sow Cheng Technology Co. Ltd.", + [3]byte{200, 117, 91}: "Quantify Technology Pty. Ltd.", + [3]byte{200, 119, 139}: "Themis Computer", + [3]byte{200, 123, 91}: "zte corporation", + [3]byte{200, 124, 188}: "Valink Co., Ltd. ", + [3]byte{200, 125, 119}: "Shenzhen Kingtech Communication Equipment Co.,Ltd", + [3]byte{200, 126, 117}: "Samsung Electronics Co.,Ltd", + [3]byte{200, 132, 57}: "Sunrise Technologies", + [3]byte{200, 132, 71}: "Beautiful Enterprise Co., Ltd", + [3]byte{200, 133, 80}: "Apple, Inc.", + [3]byte{200, 135, 34}: "Lumenpulse", + [3]byte{200, 135, 59}: "Net Optics", + [3]byte{200, 138, 131}: "Dongguan HuaHong Electronics Co.,Ltd", + [3]byte{200, 139, 71}: "Nolangroup S.P.A con Socio Unico", + [3]byte{200, 141, 131}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 142, 209}: "IEEE Registration Authority", + [3]byte{200, 144, 62}: "Pakton Technologies", + [3]byte{200, 145, 249}: "Sagemcom Broadband SAS", + [3]byte{200, 147, 70}: "MXCHIP Company Limited", + [3]byte{200, 147, 131}: "Embedded Automation, Inc.", + [3]byte{200, 148, 187}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 148, 210}: "Jiangsu Datang Electronic Products Co., Ltd", + [3]byte{200, 151, 159}: "Nokia Corporation", + [3]byte{200, 156, 29}: "Cisco Systems, Inc", + [3]byte{200, 156, 220}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{200, 159, 29}: "SHENZHEN COMMUNICATION TECHNOLOGIES CO.,LTD", + [3]byte{200, 159, 66}: "VDII Innovation AB", + [3]byte{200, 160, 48}: "Texas Instruments", + [3]byte{200, 161, 182}: "Shenzhen Longway Technologies Co., Ltd", + [3]byte{200, 161, 186}: "Neul Ltd", + [3]byte{200, 162, 206}: "Oasis Media Systems LLC", + [3]byte{200, 166, 32}: "Nebula, Inc", + [3]byte{200, 167, 10}: "Verizon Business", + [3]byte{200, 167, 41}: "SYStronics Co., Ltd.", + [3]byte{200, 168, 35}: "Samsung Electronics Co.,Ltd", + [3]byte{200, 169, 252}: "Goyoo Networks Inc.", + [3]byte{200, 170, 33}: "ARRIS Group, Inc.", + [3]byte{200, 170, 85}: "Hunan Comtom Electronic Incorporated Co.,Ltd", + [3]byte{200, 170, 204}: "Private", + [3]byte{200, 174, 156}: "Shanghai TYD Elecronic Technology Co. Ltd", + [3]byte{200, 175, 64}: "marco Systemanalyse und Entwicklung GmbH", + [3]byte{200, 175, 227}: "Hefei Radio Communication Technology Co., Ltd ", + [3]byte{200, 178, 30}: "CHIPSEA TECHNOLOGIES (SHENZHEN) CORP.", + [3]byte{200, 179, 115}: "Cisco-Linksys, LLC", + [3]byte{200, 181, 173}: "Hewlett Packard Enterprise", + [3]byte{200, 181, 183}: "Apple, Inc.", + [3]byte{200, 186, 148}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{200, 187, 211}: "Embrane", + [3]byte{200, 188, 200}: "Apple, Inc.", + [3]byte{200, 190, 25}: "D-Link International", + [3]byte{200, 193, 38}: "ZPM Industria e Comercio Ltda", + [3]byte{200, 193, 60}: "RuggedTek Hangzhou Co., Ltd", + [3]byte{200, 194, 198}: "Shanghai Airm2m Communication Technology Co., Ltd", + [3]byte{200, 197, 14}: "Shenzhen Primestone Network Technologies.Co., Ltd.", + [3]byte{200, 199, 145}: "Zero1.tv GmbH", + [3]byte{200, 203, 184}: "Hewlett Packard", + [3]byte{200, 205, 114}: "Sagemcom Broadband SAS", + [3]byte{200, 208, 25}: "Shanghai Tigercel Communication Technology Co.,Ltd", + [3]byte{200, 209, 11}: "Nokia Corporation", + [3]byte{200, 209, 94}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{200, 209, 209}: "AGAiT Technology Corporation", + [3]byte{200, 210, 193}: "Jetlun (Shenzhen) Corporation", + [3]byte{200, 211, 163}: "D-Link International", + [3]byte{200, 211, 255}: "Hewlett Packard", + [3]byte{200, 212, 41}: "Muehlbauer AG", + [3]byte{200, 213, 144}: "FLIGHT DATA SYSTEMS", + [3]byte{200, 213, 254}: "Shenzhen Zowee Technology Co., Ltd", + [3]byte{200, 215, 25}: "Cisco-Linksys, LLC", + [3]byte{200, 215, 121}: "Qingdao Haier Telecom Co.,Ltd", + [3]byte{200, 221, 201}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{200, 222, 81}: "Integra Networks, Inc.", + [3]byte{200, 223, 124}: "Nokia Corporation", + [3]byte{200, 224, 235}: "Apple, Inc.", + [3]byte{200, 225, 48}: "Milkyway Group Ltd", + [3]byte{200, 225, 167}: "Vertu Corporation Limited", + [3]byte{200, 228, 47}: "Technical Research Design and Development", + [3]byte{200, 231, 118}: "PTCOM Technology", + [3]byte{200, 231, 216}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{200, 238, 8}: "TANGTOP TECHNOLOGY CO.,LTD", + [3]byte{200, 238, 117}: "Pishion International Co. Ltd", + [3]byte{200, 238, 166}: "Shenzhen SHX Technology Co., Ltd", + [3]byte{200, 239, 46}: "Beijing Gefei Tech. Co., Ltd ", + [3]byte{200, 242, 48}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{200, 243, 107}: "Yamato Scale Co.,Ltd.", + [3]byte{200, 243, 134}: "Shenzhen Xiaoniao Technology Co.,Ltd", + [3]byte{200, 244, 6}: "Avaya Inc", + [3]byte{200, 246, 80}: "Apple, Inc.", + [3]byte{200, 246, 141}: "S.E.TECHNOLOGIES LIMITED", + [3]byte{200, 247, 4}: "Building Block Video", + [3]byte{200, 247, 51}: "Intel Corporate", + [3]byte{200, 248, 109}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{200, 249, 70}: "LOCOSYS Technology Inc.", + [3]byte{200, 249, 129}: "Seneca s.r.l.", + [3]byte{200, 249, 200}: "NewSharp Technology(SuZhou)Co,Ltd", + [3]byte{200, 249, 249}: "Cisco Systems, Inc", + [3]byte{200, 251, 38}: "Cisco SPVTG", + [3]byte{200, 253, 25}: "Texas Instruments", + [3]byte{200, 254, 48}: "Bejing DAYO Mobile Communication Technology Ltd.", + [3]byte{200, 255, 40}: "Liteon Technology Corporation", + [3]byte{200, 255, 119}: "Dyson Limited", + [3]byte{204, 0, 128}: "BETTINI SRL", + [3]byte{204, 3, 250}: "Technicolor CH USA Inc.", + [3]byte{204, 4, 124}: "G-WAY Microwave", + [3]byte{204, 4, 180}: "Select Comfort", + [3]byte{204, 5, 27}: "Samsung Electronics Co.,Ltd", + [3]byte{204, 7, 171}: "Samsung Electronics Co.,Ltd", + [3]byte{204, 7, 228}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{204, 8, 141}: "Apple, Inc.", + [3]byte{204, 8, 224}: "Apple, Inc.", + [3]byte{204, 9, 200}: "IMAQLIQ LTD", + [3]byte{204, 12, 218}: "Miljovakt AS", + [3]byte{204, 13, 236}: "Cisco SPVTG", + [3]byte{204, 16, 163}: "Beijing Nan Bao Technology Co., Ltd.", + [3]byte{204, 20, 166}: "Yichun MyEnergy Domain, Inc", + [3]byte{204, 22, 126}: "Cisco Systems, Inc", + [3]byte{204, 24, 123}: "Manzanita Systems, Inc.", + [3]byte{204, 25, 168}: "PT Inovação e Sistemas SA", + [3]byte{204, 26, 250}: "zte corporation", + [3]byte{204, 27, 224}: "IEEE Registration Authority", + [3]byte{204, 30, 255}: "Metrological Group BV", + [3]byte{204, 31, 196}: "InVue", + [3]byte{204, 32, 232}: "Apple, Inc.", + [3]byte{204, 34, 24}: "InnoDigital Co., Ltd.", + [3]byte{204, 37, 239}: "Apple, Inc.", + [3]byte{204, 38, 45}: "Verifi, LLC", + [3]byte{204, 41, 245}: "Apple, Inc.", + [3]byte{204, 42, 128}: "Micro-Biz intelligence solutions Co.,Ltd", + [3]byte{204, 45, 33}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{204, 45, 131}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{204, 45, 140}: "LG ELECTRONICS INC", + [3]byte{204, 48, 128}: "VAIO Corporation", + [3]byte{204, 51, 187}: "Sagemcom Broadband SAS", + [3]byte{204, 52, 41}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{204, 52, 215}: "GEWISS S.P.A.", + [3]byte{204, 53, 64}: "Technicolor CH USA Inc.", + [3]byte{204, 55, 171}: "Edgecore Networks Corportation", + [3]byte{204, 57, 140}: "Shiningtek", + [3]byte{204, 58, 97}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{204, 58, 223}: "Private", + [3]byte{204, 59, 62}: "Lester Electrical", + [3]byte{204, 60, 63}: "SA.S.S. Datentechnik AG", + [3]byte{204, 61, 130}: "Intel Corporate", + [3]byte{204, 62, 95}: "Hewlett Packard", + [3]byte{204, 63, 29}: "Intesis Software SL", + [3]byte{204, 67, 227}: "Trump s.a.", + [3]byte{204, 68, 99}: "Apple, Inc.", + [3]byte{204, 70, 214}: "Cisco Systems, Inc", + [3]byte{204, 71, 3}: "Intercon Systems Co., Ltd.", + [3]byte{204, 74, 225}: "fourtec -Fourier Technologies", + [3]byte{204, 75, 251}: "Hellberg Safety AB", + [3]byte{204, 78, 36}: "Brocade Communications Systems, Inc.", + [3]byte{204, 78, 236}: "HUMAX Co., Ltd.", + [3]byte{204, 80, 10}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{204, 80, 28}: "KVH Industries, Inc.", + [3]byte{204, 80, 118}: "Ocom Communications, Inc.", + [3]byte{204, 82, 175}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{204, 83, 181}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{204, 84, 89}: "OnTime Networks AS", + [3]byte{204, 85, 173}: "RIM", + [3]byte{204, 89, 62}: "TOUMAZ LTD", + [3]byte{204, 92, 117}: "Weightech Com. Imp. Exp. Equip. Pesagem Ltda", + [3]byte{204, 93, 78}: "ZyXEL Communications Corporation", + [3]byte{204, 93, 87}: "Information System Research Institute,Inc.", + [3]byte{204, 95, 191}: "Topwise 3G Communication Co., Ltd.", + [3]byte{204, 96, 187}: "Empower RF Systems", + [3]byte{204, 97, 229}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{204, 101, 173}: "ARRIS Group, Inc.", + [3]byte{204, 105, 176}: "Global Traffic Technologies, LLC", + [3]byte{204, 107, 152}: "Minetec Wireless Technologies", + [3]byte{204, 107, 241}: "Sound Masking Inc.", + [3]byte{204, 109, 160}: "Roku, Inc.", + [3]byte{204, 109, 239}: "TJK Tietolaite Oy", + [3]byte{204, 114, 15}: "Viscount Systems Inc.", + [3]byte{204, 115, 20}: "HONG KONG WHEATEK TECHNOLOGY LIMITED", + [3]byte{204, 116, 152}: "Filmetrics Inc.", + [3]byte{204, 118, 105}: "SEETECH", + [3]byte{204, 120, 95}: "Apple, Inc.", + [3]byte{204, 120, 171}: "Texas Instruments", + [3]byte{204, 121, 74}: "BLU Products Inc.", + [3]byte{204, 121, 207}: "SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.", + [3]byte{204, 122, 48}: "CMAX Wireless Co., Ltd.", + [3]byte{204, 123, 53}: "zte corporation", + [3]byte{204, 125, 55}: "ARRIS Group, Inc.", + [3]byte{204, 126, 231}: "Panasonic AVC Networks Company", + [3]byte{204, 129, 218}: "SHANGHAI PHICOMM COMMUNICATION CO.,LTD", + [3]byte{204, 130, 235}: "KYOCERA CORPORATION ", + [3]byte{204, 133, 108}: "SHENZHEN MDK DIGITAL TECHNOLOGY CO.,LTD", + [3]byte{204, 137, 253}: "Nokia Corporation", + [3]byte{204, 140, 218}: "Shenzhen Wei Da Intelligent Technology Go.,Ltd", + [3]byte{204, 140, 227}: "Texas Instruments", + [3]byte{204, 144, 147}: "Hansong Tehnologies", + [3]byte{204, 144, 232}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{204, 145, 43}: "TE Connectivity Touch Solutions", + [3]byte{204, 148, 74}: "Pfeiffer Vacuum GmbH", + [3]byte{204, 148, 112}: "Kinestral Technologies, Inc.", + [3]byte{204, 149, 215}: "Vizio, Inc", + [3]byte{204, 150, 53}: "LVS Co.,Ltd.", + [3]byte{204, 150, 160}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{204, 158, 0}: "Nintendo Co., Ltd.", + [3]byte{204, 159, 53}: "Transbit Sp. z o.o.", + [3]byte{204, 159, 122}: "Chiun Mai Communication Systems, Inc", + [3]byte{204, 160, 229}: "DZG Metering GmbH", + [3]byte{204, 162, 25}: "SHENZHEN ALONG INVESTMENT CO.,LTD", + [3]byte{204, 162, 35}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{204, 162, 96}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{204, 163, 116}: "Guangdong Guanglian Electronic Technology Co.Ltd", + [3]byte{204, 164, 98}: "ARRIS Group, Inc.", + [3]byte{204, 164, 175}: "Shenzhen Sowell Technology Co., LTD", + [3]byte{204, 166, 20}: "AIFA TECHNOLOGY CORP.", + [3]byte{204, 175, 120}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{204, 176, 218}: "Liteon Technology Corporation", + [3]byte{204, 177, 26}: "Samsung Electronics Co.,Ltd", + [3]byte{204, 178, 85}: "D-Link International", + [3]byte{204, 179, 171}: "shenzhen Biocare Bio-Medical Equipment Co.,Ltd.", + [3]byte{204, 179, 248}: "FUJITSU ISOTEC LIMITED", + [3]byte{204, 181, 90}: "Fraunhofer ITWM", + [3]byte{204, 182, 145}: "NECMagnusCommunications", + [3]byte{204, 184, 136}: "AnB Securite s.a.", + [3]byte{204, 184, 168}: "AMPAK Technology, Inc.", + [3]byte{204, 184, 241}: "EAGLE KINGDOM TECHNOLOGIES LIMITED", + [3]byte{204, 189, 53}: "Steinel GmbH", + [3]byte{204, 189, 211}: "Ultimaker B.V.", + [3]byte{204, 190, 89}: "Calix Inc.", + [3]byte{204, 190, 113}: "OptiLogix BV", + [3]byte{204, 193, 4}: "Applied Technical Systems", + [3]byte{204, 195, 234}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{204, 197, 10}: "SHENZHEN DAJIAHAO TECHNOLOGY CO.,LTD", + [3]byte{204, 197, 239}: "Co-Comm Servicios Telecomunicaciones S.L.", + [3]byte{204, 198, 43}: "Tri-Systems Corporation", + [3]byte{204, 199, 96}: "Apple, Inc.", + [3]byte{204, 200, 215}: "CIAS Elettronica srl", + [3]byte{204, 204, 78}: "Sun Fountainhead USA. Corp ", + [3]byte{204, 204, 129}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{204, 205, 100}: "SM-Electronic GmbH", + [3]byte{204, 206, 30}: "AVM Audiovisuelles Marketing und Computersysteme GmbH", + [3]byte{204, 206, 64}: "Janteq Corp", + [3]byte{204, 210, 155}: "Shenzhen Bopengfa Elec&Technology CO.,Ltd", + [3]byte{204, 211, 30}: "IEEE Registration Authority", + [3]byte{204, 211, 226}: "Jiangsu Yinhe Electronics Co.,Ltd.", + [3]byte{204, 213, 57}: "Cisco Systems, Inc", + [3]byte{204, 216, 17}: "Aiconn Technology Corporation", + [3]byte{204, 216, 193}: "Cisco Systems, Inc", + [3]byte{204, 217, 233}: "SCR Engineers Ltd.", + [3]byte{204, 224, 195}: "Mangstor, Inc.", + [3]byte{204, 225, 127}: "Juniper Networks", + [3]byte{204, 225, 213}: "BUFFALO.INC", + [3]byte{204, 231, 152}: "My Social Stuff", + [3]byte{204, 231, 223}: "American Magnetics, Inc.", + [3]byte{204, 232, 172}: "SOYEA Technology Co.,Ltd.", + [3]byte{204, 234, 28}: "DCONWORKS Co., Ltd", + [3]byte{204, 238, 217}: "VAHLE DETO GmbH", + [3]byte{204, 239, 72}: "Cisco Systems, Inc", + [3]byte{204, 243, 165}: "Chi Mei Communication Systems, Inc", + [3]byte{204, 244, 7}: "EUKREA ELECTROMATIQUE SARL", + [3]byte{204, 245, 56}: "3isysnetworks", + [3]byte{204, 246, 122}: "Ayecka Communication Systems LTD", + [3]byte{204, 248, 65}: "Lumewave", + [3]byte{204, 248, 240}: "Xi'an HISU Multimedia Technology Co.,Ltd.", + [3]byte{204, 249, 84}: "Avaya Inc", + [3]byte{204, 249, 232}: "Samsung Electronics Co.,Ltd", + [3]byte{204, 250, 0}: "LG Electronics (Mobile Communications)", + [3]byte{204, 251, 101}: "Nintendo Co., Ltd.", + [3]byte{204, 252, 109}: "RIZ TRANSMITTERS", + [3]byte{204, 252, 177}: "Wireless Technology, Inc.", + [3]byte{204, 253, 23}: "TCT mobile ltd", + [3]byte{204, 254, 60}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 3, 75}: "Apple, Inc.", + [3]byte{208, 4, 146}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{208, 5, 42}: "Arcadyan Corporation", + [3]byte{208, 7, 144}: "Texas Instruments", + [3]byte{208, 10, 171}: "Yokogawa Digital Computer Corporation", + [3]byte{208, 14, 164}: "Porsche Cars North America", + [3]byte{208, 14, 217}: "Taicang T&W Electronics", + [3]byte{208, 15, 109}: "T&W Electronics Company", + [3]byte{208, 18, 66}: "BIOS Corporation", + [3]byte{208, 19, 30}: "Sunrex Technology Corp", + [3]byte{208, 19, 253}: "LG Electronics (Mobile Communications)", + [3]byte{208, 21, 74}: "zte corporation", + [3]byte{208, 23, 106}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 23, 194}: "ASUSTek COMPUTER INC.", + [3]byte{208, 26, 167}: "UniPrint", + [3]byte{208, 28, 187}: "Beijing Ctimes Digital Technology Co., Ltd.", + [3]byte{208, 34, 18}: "IEEE Registration Authority", + [3]byte{208, 34, 190}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{208, 35, 219}: "Apple, Inc.", + [3]byte{208, 37, 22}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{208, 37, 68}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{208, 37, 152}: "Apple, Inc.", + [3]byte{208, 39, 136}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{208, 44, 69}: "littleBits Electronics, Inc.", + [3]byte{208, 45, 179}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{208, 49, 16}: "Ingenic Semiconductor Co.,Ltd", + [3]byte{208, 51, 17}: "Apple, Inc.", + [3]byte{208, 55, 66}: "Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd", + [3]byte{208, 55, 97}: "Texas Instruments", + [3]byte{208, 57, 114}: "Texas Instruments", + [3]byte{208, 57, 179}: "ARRIS Group, Inc.", + [3]byte{208, 61, 195}: "AQ Corporation", + [3]byte{208, 62, 92}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{208, 67, 30}: "Dell Inc.", + [3]byte{208, 70, 220}: "Southwest Research Institute", + [3]byte{208, 72, 243}: "DATTUS Inc", + [3]byte{208, 73, 139}: "ZOOM SERVER", + [3]byte{208, 76, 193}: "SINTRONES Technology Corp.", + [3]byte{208, 77, 44}: "Roku, Inc.", + [3]byte{208, 79, 126}: "Apple, Inc.", + [3]byte{208, 80, 153}: "ASRock Incorporation", + [3]byte{208, 81, 98}: "Sony Mobile Communications AB", + [3]byte{208, 82, 168}: "Physical Graph Corporation", + [3]byte{208, 83, 73}: "Liteon Technology Corporation", + [3]byte{208, 84, 45}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{208, 85, 178}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{208, 87, 76}: "Cisco Systems, Inc", + [3]byte{208, 87, 123}: "Intel Corporate", + [3]byte{208, 87, 133}: "Pantech Co., Ltd.", + [3]byte{208, 87, 161}: "Werma Signaltechnik GmbH & Co. KG", + [3]byte{208, 88, 117}: "Active Control Technology Inc.", + [3]byte{208, 88, 168}: "zte corporation", + [3]byte{208, 89, 195}: "CeraMicro Technology Corporation", + [3]byte{208, 89, 228}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 90, 15}: "I-BT DIGITAL CO.,LTD", + [3]byte{208, 90, 241}: "Shenzhen Pulier Tech CO.,Ltd", + [3]byte{208, 91, 168}: "zte corporation", + [3]byte{208, 92, 122}: "Sartura d.o.o.", + [3]byte{208, 95, 184}: "Texas Instruments", + [3]byte{208, 95, 206}: "Hitachi Data Systems", + [3]byte{208, 96, 140}: "zte corporation", + [3]byte{208, 98, 160}: "China Essence Technology (Zhumadian) Co., Ltd.", + [3]byte{208, 99, 77}: "Meiko Maschinenbau GmbH & Co. KG", + [3]byte{208, 99, 180}: "SolidRun Ltd.", + [3]byte{208, 101, 202}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{208, 102, 123}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 103, 229}: "Dell Inc.", + [3]byte{208, 105, 158}: "LUMINEX Lighting Control Equipment", + [3]byte{208, 105, 208}: "Verto Medical Solutions, LLC", + [3]byte{208, 106, 31}: "BSE CO.,LTD.", + [3]byte{208, 111, 74}: "TOPWELL INTERNATIONAL HOLDINGS LIMITED", + [3]byte{208, 111, 130}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{208, 113, 196}: "zte corporation", + [3]byte{208, 114, 220}: "Cisco Systems, Inc", + [3]byte{208, 115, 127}: "Mini-Circuits", + [3]byte{208, 115, 142}: "DONG OH PRECISION CO., LTD. ", + [3]byte{208, 115, 213}: "LIFI LABS MANAGEMENT PTY LTD", + [3]byte{208, 117, 190}: "Reno A&E", + [3]byte{208, 118, 80}: "IEEE Registration Authority", + [3]byte{208, 122, 181}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{208, 124, 45}: "Leie IOT technology Co., Ltd", + [3]byte{208, 125, 229}: "Forward Pay Systems, Inc.", + [3]byte{208, 126, 40}: "Hewlett Packard", + [3]byte{208, 126, 53}: "Intel Corporate", + [3]byte{208, 131, 212}: "XTel ApS", + [3]byte{208, 132, 176}: "Sagemcom Broadband SAS", + [3]byte{208, 135, 226}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 137, 153}: "APCON, Inc.", + [3]byte{208, 138, 85}: "Skullcandy", + [3]byte{208, 139, 126}: "Passif Semiconductor", + [3]byte{208, 140, 181}: "Texas Instruments", + [3]byte{208, 140, 255}: "UPWIS AB", + [3]byte{208, 146, 158}: "Microsoft Corporation", + [3]byte{208, 147, 128}: "Ducere Technologies Pvt. Ltd.", + [3]byte{208, 147, 248}: "Stonestreet One LLC", + [3]byte{208, 149, 199}: "Pantech Co., Ltd.", + [3]byte{208, 153, 213}: "Alcatel-Lucent", + [3]byte{208, 155, 5}: "Emtronix", + [3]byte{208, 156, 48}: "Foster Electric Company, Limited", + [3]byte{208, 157, 10}: "LINKCOM", + [3]byte{208, 157, 171}: "TCT mobile ltd", + [3]byte{208, 160, 214}: "Chengdu TD Tech Ltd.", + [3]byte{208, 163, 17}: "Neuberger Gebäudeautomation GmbH", + [3]byte{208, 164, 177}: "Sonifex Ltd.", + [3]byte{208, 165, 166}: "Cisco Systems, Inc", + [3]byte{208, 166, 55}: "Apple, Inc.", + [3]byte{208, 174, 236}: "Alpha Networks Inc.", + [3]byte{208, 175, 182}: "Linktop Technology Co., LTD", + [3]byte{208, 176, 205}: "Moen", + [3]byte{208, 178, 196}: "Technicolor CH USA Inc.", + [3]byte{208, 179, 63}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{208, 180, 152}: "Robert Bosch LLC Automotive Electronics", + [3]byte{208, 181, 35}: "Bestcare Cloucal Corp.", + [3]byte{208, 181, 61}: "SEPRO ROBOTIQUE", + [3]byte{208, 181, 194}: "Texas Instruments", + [3]byte{208, 186, 228}: "Shanghai MXCHIP Information Technology Co., Ltd.", + [3]byte{208, 187, 128}: "SHL Telemedicine International Ltd.", + [3]byte{208, 189, 1}: "DS International", + [3]byte{208, 190, 44}: "CNSLink Co., Ltd.", + [3]byte{208, 191, 156}: "Hewlett Packard", + [3]byte{208, 192, 191}: "Actions Microelectronics Co., Ltd", + [3]byte{208, 193, 147}: "SKYBELL, INC", + [3]byte{208, 193, 177}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 194, 130}: "Cisco Systems, Inc", + [3]byte{208, 196, 47}: "Tamagawa Seiki Co.,Ltd.", + [3]byte{208, 197, 243}: "Apple, Inc.", + [3]byte{208, 199, 137}: "Cisco Systems, Inc", + [3]byte{208, 199, 192}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{208, 205, 225}: "Scientech Electronics", + [3]byte{208, 207, 94}: "Energy Micro AS", + [3]byte{208, 208, 75}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{208, 208, 253}: "Cisco Systems, Inc", + [3]byte{208, 210, 18}: "K2NET Co.,Ltd.", + [3]byte{208, 210, 134}: "Beckman Coulter K.K.", + [3]byte{208, 211, 252}: "Mios, Ltd.", + [3]byte{208, 212, 18}: "ADB Broadband Italia", + [3]byte{208, 212, 113}: "MVTECH co., Ltd", + [3]byte{208, 214, 204}: "Wintop", + [3]byte{208, 217, 79}: "IEEE Registration Authority", + [3]byte{208, 219, 50}: "Nokia Corporation", + [3]byte{208, 223, 154}: "Liteon Technology Corporation", + [3]byte{208, 223, 178}: "Genie Networks Limited", + [3]byte{208, 223, 199}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 225, 64}: "Apple, Inc.", + [3]byte{208, 227, 71}: "Yoga", + [3]byte{208, 228, 11}: "Wearable Inc.", + [3]byte{208, 228, 74}: "Murata Manufacturing Co., Ltd.", + [3]byte{208, 229, 77}: "ARRIS Group, Inc.", + [3]byte{208, 231, 130}: "AzureWave Technology Inc.", + [3]byte{208, 235, 3}: "Zhehua technology limited", + [3]byte{208, 235, 158}: "Seowoo Inc.", + [3]byte{208, 240, 219}: "Ericsson", + [3]byte{208, 242, 127}: "SteadyServ Technoligies, LLC", + [3]byte{208, 247, 59}: "Helmut Mauell GmbH Werk Weida", + [3]byte{208, 250, 29}: "Qihoo 360 Technology Co.,Ltd", + [3]byte{208, 252, 204}: "Samsung Electronics Co.,Ltd", + [3]byte{208, 255, 80}: "Texas Instruments", + [3]byte{208, 255, 152}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 0, 13}: "Phoenix Broadband Technologies, LLC.", + [3]byte{212, 0, 87}: "MC Technologies GmbH", + [3]byte{212, 1, 41}: "Broadcom", + [3]byte{212, 1, 109}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{212, 2, 74}: "Delphian Systems LLC", + [3]byte{212, 4, 205}: "ARRIS Group, Inc.", + [3]byte{212, 4, 255}: "Juniper Networks", + [3]byte{212, 5, 152}: "ARRIS Group, Inc.", + [3]byte{212, 10, 169}: "ARRIS Group, Inc.", + [3]byte{212, 11, 26}: "HTC Corporation", + [3]byte{212, 11, 185}: "Solid Semecs bv.", + [3]byte{212, 15, 178}: "Applied Micro Electronics AME bv", + [3]byte{212, 16, 144}: "iNFORM Systems AG", + [3]byte{212, 16, 207}: "Huanshun Network Science and Technology Co., Ltd.", + [3]byte{212, 17, 214}: "ShotSpotter, Inc.", + [3]byte{212, 18, 150}: "Anobit Technologies Ltd.", + [3]byte{212, 18, 187}: "Quadrant Components Inc. Ltd", + [3]byte{212, 19, 111}: "Asia Pacific Brands", + [3]byte{212, 28, 28}: "RCF S.P.A.", + [3]byte{212, 29, 113}: "Palo Alto Networks", + [3]byte{212, 30, 53}: "TOHO Electronics INC.", + [3]byte{212, 31, 12}: "JAI Oy", + [3]byte{212, 32, 109}: "HTC Corporation", + [3]byte{212, 33, 34}: "Sercomm Corporation", + [3]byte{212, 34, 63}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{212, 34, 78}: "Alcatel Lucent", + [3]byte{212, 39, 81}: "Infopia Co., Ltd", + [3]byte{212, 40, 178}: "ioBridge, Inc.", + [3]byte{212, 41, 234}: "Zimory GmbH", + [3]byte{212, 44, 15}: "ARRIS Group, Inc.", + [3]byte{212, 44, 61}: "Sky Light Digital Limited", + [3]byte{212, 44, 68}: "Cisco Systems, Inc", + [3]byte{212, 47, 35}: "Akenori PTE Ltd", + [3]byte{212, 49, 157}: "Sinwatec", + [3]byte{212, 50, 102}: "Fike Corporation", + [3]byte{212, 54, 57}: "Texas Instruments", + [3]byte{212, 54, 219}: "Jiangsu Toppower Automotive Electronics Co., Ltd", + [3]byte{212, 55, 215}: "zte corporation", + [3]byte{212, 58, 101}: "IGRS Engineering Lab Ltd.", + [3]byte{212, 58, 233}: "DONGGUAN ipt INDUSTRIAL CO., LTD", + [3]byte{212, 61, 103}: "Carma Industries Inc.", + [3]byte{212, 61, 126}: "Micro-Star Int'l Co, Ltd", + [3]byte{212, 64, 240}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 65, 101}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{212, 67, 168}: "Changzhou Haojie Electric Co., Ltd.", + [3]byte{212, 69, 232}: "Jiangxi Hongpai Technology Co., Ltd.", + [3]byte{212, 75, 94}: "TAIYO YUDEN CO., LTD.", + [3]byte{212, 76, 36}: "Vuppalamritha Magnetic Components LTD", + [3]byte{212, 76, 156}: "Shenzhen YOOBAO Technology Co.Ltd", + [3]byte{212, 76, 167}: "Informtekhnika & Communication, LLC", + [3]byte{212, 79, 128}: "Kemper Digital GmbH", + [3]byte{212, 80, 63}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{212, 80, 122}: "CEIVA Logic, Inc", + [3]byte{212, 82, 42}: "TangoWiFi.com", + [3]byte{212, 82, 81}: "IBT Ingenieurbureau Broennimann Thun", + [3]byte{212, 82, 151}: "nSTREAMS Technologies, Inc.", + [3]byte{212, 83, 175}: "VIGO System S.A.", + [3]byte{212, 85, 86}: "Fiber Mountain Inc.", + [3]byte{212, 85, 190}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{212, 90, 178}: "Galleon Systems", + [3]byte{212, 92, 112}: "Wi-Fi Alliance", + [3]byte{212, 93, 66}: "Nokia Corporation", + [3]byte{212, 95, 37}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{212, 97, 46}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 97, 50}: "Pro Concept Manufacturer Co.,Ltd.", + [3]byte{212, 97, 157}: "Apple, Inc.", + [3]byte{212, 97, 254}: "Hangzhou H3C Technologies Co., Limited", + [3]byte{212, 99, 254}: "Arcadyan Corporation", + [3]byte{212, 100, 247}: "CHENGDU USEE DIGITAL TECHNOLOGY CO., LTD", + [3]byte{212, 102, 168}: "Riedo Networks GmbH", + [3]byte{212, 103, 97}: "SAHAB TECHNOLOGY", + [3]byte{212, 103, 231}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{212, 104, 77}: "Ruckus Wireless", + [3]byte{212, 104, 103}: "Neoventus Design Group", + [3]byte{212, 104, 186}: "Shenzhen Sundray Technologies Company Limited", + [3]byte{212, 106, 106}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{212, 106, 145}: "Snap AV", + [3]byte{212, 106, 168}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 108, 191}: "Goodrich ISR", + [3]byte{212, 108, 218}: "CSM GmbH", + [3]byte{212, 109, 80}: "Cisco Systems, Inc", + [3]byte{212, 110, 14}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{212, 110, 92}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 111, 66}: "WAXESS USA Inc", + [3]byte{212, 114, 8}: "Bragi GmbH", + [3]byte{212, 118, 234}: "zte corporation", + [3]byte{212, 120, 86}: "Avaya Inc", + [3]byte{212, 121, 195}: "Cameronet GmbH & Co. KG", + [3]byte{212, 122, 226}: "Samsung Electronics Co.,Ltd", + [3]byte{212, 123, 53}: "NEO Monitors AS", + [3]byte{212, 123, 117}: "HARTING Electronics GmbH", + [3]byte{212, 123, 176}: "ASKEY COMPUTER CORP", + [3]byte{212, 125, 252}: "TECNO MOBILE LIMITED", + [3]byte{212, 129, 202}: "iDevices, LLC", + [3]byte{212, 129, 215}: "Dell Inc.", + [3]byte{212, 130, 62}: "Argosy Technologies, Ltd.", + [3]byte{212, 131, 4}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{212, 133, 100}: "Hewlett Packard", + [3]byte{212, 135, 216}: "Samsung Electronics Co.,Ltd", + [3]byte{212, 136, 63}: "HDPRO CO., LTD.", + [3]byte{212, 136, 144}: "Samsung Electronics Co.,Ltd", + [3]byte{212, 140, 181}: "Cisco Systems, Inc", + [3]byte{212, 141, 217}: "Meld Technology, Inc", + [3]byte{212, 143, 51}: "Microsoft Corporation", + [3]byte{212, 143, 170}: "Sogecam Industrial, S.A.", + [3]byte{212, 144, 224}: "Wachendorff Automation GmbH & Co KG ", + [3]byte{212, 145, 175}: "Electroacustica General Iberica, S.A.", + [3]byte{212, 147, 152}: "Nokia Corporation", + [3]byte{212, 147, 160}: "Fidelix Oy", + [3]byte{212, 148, 90}: "COSMO CO., LTD", + [3]byte{212, 148, 161}: "Texas Instruments", + [3]byte{212, 148, 232}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 149, 36}: "Clover Network, Inc.", + [3]byte{212, 150, 223}: "SUNGJIN C&T CO.,LTD", + [3]byte{212, 151, 11}: "Xiaomi Communications Co Ltd", + [3]byte{212, 154, 32}: "Apple, Inc.", + [3]byte{212, 155, 92}: "Chongqing Miedu Technology Co., Ltd.", + [3]byte{212, 156, 40}: "JayBird LLC", + [3]byte{212, 156, 142}: "University of FUKUI", + [3]byte{212, 158, 109}: "Wuhan Zhongyuan Huadian Science & Technology Co.,", + [3]byte{212, 160, 42}: "Cisco Systems, Inc", + [3]byte{212, 161, 72}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 164, 37}: "SMAX Technology Co., Ltd.", + [3]byte{212, 164, 153}: "InView Technology Corporation", + [3]byte{212, 169, 40}: "GreenWave Reality Inc", + [3]byte{212, 170, 255}: "MICRO WORLD ", + [3]byte{212, 172, 78}: "BODi rS, LLC", + [3]byte{212, 173, 45}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{212, 174, 5}: "Samsung Electronics Co.,Ltd", + [3]byte{212, 174, 82}: "Dell Inc.", + [3]byte{212, 177, 16}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{212, 177, 105}: "Le Shi Zhi Xin Electronic Technology (Tianjin) Limited", + [3]byte{212, 180, 62}: "Messcomp Datentechnik GmbH", + [3]byte{212, 184, 255}: "Home Control Singapore Pte Ltd", + [3]byte{212, 190, 217}: "Dell Inc.", + [3]byte{212, 191, 45}: "SE Controls Asia Pacific Ltd", + [3]byte{212, 191, 127}: "UPVEL", + [3]byte{212, 193, 252}: "Nokia Corporation", + [3]byte{212, 199, 102}: "Acentic GmbH", + [3]byte{212, 200, 176}: "Prime Electronics & Satellitics Inc.", + [3]byte{212, 201, 178}: "Quanergy Systems Inc", + [3]byte{212, 201, 239}: "Hewlett Packard", + [3]byte{212, 202, 109}: "Routerboard.com", + [3]byte{212, 202, 110}: "u-blox AG", + [3]byte{212, 203, 175}: "Nokia Corporation", + [3]byte{212, 206, 184}: "Enatel LTD", + [3]byte{212, 207, 55}: "Symbolic IO", + [3]byte{212, 207, 249}: "Shenzhen Sen5 Technology Co., Ltd.", + [3]byte{212, 209, 132}: "ADB Broadband Italia", + [3]byte{212, 210, 73}: "Power Ethernet", + [3]byte{212, 213, 13}: "Southwest Microwave, Inc", + [3]byte{212, 215, 72}: "Cisco Systems, Inc", + [3]byte{212, 215, 169}: "Shanghai Kaixiang Info Tech LTD", + [3]byte{212, 216, 152}: "Korea CNO Tech Co., Ltd", + [3]byte{212, 217, 25}: "GoPro", + [3]byte{212, 220, 205}: "Apple, Inc.", + [3]byte{212, 223, 87}: "Alpinion Medical Systems", + [3]byte{212, 224, 142}: "ValueHD Corporation", + [3]byte{212, 227, 44}: "S. Siedle & Sohne", + [3]byte{212, 227, 63}: "Nokia", + [3]byte{212, 232, 178}: "Samsung Electronics Co.,Ltd", + [3]byte{212, 233, 11}: "CVT CO.,LTD", + [3]byte{212, 234, 14}: "Avaya Inc", + [3]byte{212, 236, 12}: "Harley-Davidson Motor Company", + [3]byte{212, 236, 134}: "LinkedHope Intelligent Technologies Co., Ltd", + [3]byte{212, 238, 7}: "HIWIFI Co., Ltd.", + [3]byte{212, 240, 39}: "Navetas Energy Management", + [3]byte{212, 240, 180}: "Napco Security Technologies", + [3]byte{212, 241, 67}: "IPROAD.,Inc", + [3]byte{212, 242, 7}: "DIAODIAO(Beijing)Technology CO.,Ltd", + [3]byte{212, 244, 111}: "Apple, Inc.", + [3]byte{212, 244, 190}: "Palo Alto Networks", + [3]byte{212, 245, 19}: "Texas Instruments", + [3]byte{212, 246, 63}: "IEA S.R.L.", + [3]byte{212, 249, 161}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{216, 0, 77}: "Apple, Inc.", + [3]byte{216, 5, 46}: "Skyviia Corporation", + [3]byte{216, 6, 209}: "Honeywell Fire System (Shanghai) Co,. Ltd.", + [3]byte{216, 8, 245}: "Arcadia Networks Co. Ltd. ", + [3]byte{216, 9, 195}: "Cercacor Labs", + [3]byte{216, 12, 207}: "C.G.V. S.A.S.", + [3]byte{216, 13, 227}: "FXI TECHNOLOGIES AS", + [3]byte{216, 15, 153}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{216, 20, 214}: "SURE SYSTEM Co Ltd", + [3]byte{216, 21, 13}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{216, 22, 10}: "Nippon Electro-Sensory Devices", + [3]byte{216, 22, 193}: "DEWAV (HK) ELECTRONICS LIMITED", + [3]byte{216, 24, 43}: "Conti Temic Microelectronic GmbH", + [3]byte{216, 25, 122}: "Nuheara Ltd", + [3]byte{216, 25, 206}: "Telesquare", + [3]byte{216, 27, 254}: "TWINLINX CORPORATION", + [3]byte{216, 28, 20}: "Compacta International, Ltd.", + [3]byte{216, 29, 114}: "Apple, Inc.", + [3]byte{216, 30, 222}: "B&W Group Ltd", + [3]byte{216, 31, 204}: "Brocade Communications Systems, Inc.", + [3]byte{216, 32, 159}: "Cubro Acronet GesmbH", + [3]byte{216, 36, 189}: "Cisco Systems, Inc", + [3]byte{216, 37, 34}: "ARRIS Group, Inc.", + [3]byte{216, 37, 176}: "Rockeetech Systems Co.,Ltd.", + [3]byte{216, 38, 185}: "Guangdong Coagent Electronics S&T Co.,Ltd.", + [3]byte{216, 39, 12}: "MaxTronic International Co., Ltd.", + [3]byte{216, 40, 201}: "General Electric Consumer and Industrial", + [3]byte{216, 41, 22}: "Ascent Communication Technology", + [3]byte{216, 41, 134}: "Best Wish Technology LTD", + [3]byte{216, 42, 21}: "Leitner SpA", + [3]byte{216, 42, 126}: "Nokia Corporation", + [3]byte{216, 45, 155}: "Shenzhen G.Credit Communication Technology Co., Ltd", + [3]byte{216, 45, 225}: "Tricascade Inc.", + [3]byte{216, 48, 98}: "Apple, Inc.", + [3]byte{216, 49, 207}: "Samsung Electronics Co.,Ltd", + [3]byte{216, 50, 20}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{216, 50, 90}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{216, 51, 127}: "Office FA.com Co.,Ltd.", + [3]byte{216, 55, 190}: "Shanghai Gongjing Telecom Technology Co,LTD", + [3]byte{216, 56, 13}: "SHENZHEN IP-COM Network Co.,Ltd", + [3]byte{216, 56, 252}: "Ruckus Wireless", + [3]byte{216, 60, 105}: "Shenzhen TINNO Mobile Technology Corp.", + [3]byte{216, 66, 172}: "Shanghai Feixun Communication Co.,Ltd.", + [3]byte{216, 66, 226}: "Canary Connect, Inc.", + [3]byte{216, 69, 43}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{216, 70, 6}: "Silicon Valley Global Marketing", + [3]byte{216, 71, 16}: "Sichuan Changhong Electric Ltd.", + [3]byte{216, 72, 238}: "Hangzhou Xueji Technology Co., Ltd.", + [3]byte{216, 73, 11}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{216, 73, 47}: "CANON INC.", + [3]byte{216, 74, 135}: "OI ELECTRIC CO.,LTD", + [3]byte{216, 75, 42}: "Cognitas Technologies, Inc.", + [3]byte{216, 79, 184}: "LG ELECTRONICS", + [3]byte{216, 80, 230}: "ASUSTek COMPUTER INC.", + [3]byte{216, 84, 58}: "Texas Instruments", + [3]byte{216, 84, 162}: "Aerohive Networks Inc.", + [3]byte{216, 85, 163}: "zte corporation", + [3]byte{216, 87, 239}: "Samsung Electronics Co.,Ltd", + [3]byte{216, 88, 215}: "CZ.NIC, z.s.p.o.", + [3]byte{216, 91, 42}: "Samsung Electronics Co.,Ltd", + [3]byte{216, 93, 76}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{216, 93, 132}: "CAx soft GmbH", + [3]byte{216, 93, 226}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{216, 93, 239}: "Busch-Jaeger Elektro GmbH", + [3]byte{216, 93, 251}: "Private", + [3]byte{216, 96, 176}: "bioMérieux Italia S.p.A.", + [3]byte{216, 96, 179}: "Guangdong Global Electronic Technology CO.,LTD", + [3]byte{216, 97, 148}: "Objetivos y Sevicios de Valor Añadido", + [3]byte{216, 98, 219}: "Eno Inc.", + [3]byte{216, 101, 149}: "Toy's Myth Inc.", + [3]byte{216, 102, 198}: "Shenzhen Daystar Technology Co.,ltd", + [3]byte{216, 102, 238}: "BOXIN COMMUNICATION CO.,LTD.", + [3]byte{216, 103, 217}: "Cisco Systems, Inc", + [3]byte{216, 105, 96}: "Steinsvik", + [3]byte{216, 107, 247}: "Nintendo Co., Ltd.", + [3]byte{216, 108, 2}: "Huaqin Telecom Technology Co.,Ltd", + [3]byte{216, 108, 233}: "Sagemcom Broadband SAS", + [3]byte{216, 113, 87}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{216, 116, 149}: "zte corporation", + [3]byte{216, 117, 51}: "Nokia Corporation", + [3]byte{216, 118, 10}: "Escort, Inc.", + [3]byte{216, 120, 229}: "KUHN SA", + [3]byte{216, 121, 136}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{216, 124, 221}: "SANIX INCORPORATED", + [3]byte{216, 126, 177}: "x.o.ware, inc.", + [3]byte{216, 128, 57}: "Microchip Technology Inc.", + [3]byte{216, 128, 60}: "Anhui Huami Information Technology Company Limited", + [3]byte{216, 129, 206}: "AHN INC.", + [3]byte{216, 132, 102}: "Extreme Networks", + [3]byte{216, 135, 213}: "Leadcore Technology CO.,LTD", + [3]byte{216, 136, 206}: "RF Technology Pty Ltd", + [3]byte{216, 138, 59}: "UNIT-EM", + [3]byte{216, 139, 76}: "KingTing Tech.", + [3]byte{216, 141, 92}: "Elentec", + [3]byte{216, 144, 232}: "Samsung Electronics Co.,Ltd", + [3]byte{216, 147, 65}: "General Electric Global Research", + [3]byte{216, 148, 3}: "Hewlett Packard Enterprise", + [3]byte{216, 149, 47}: "Texas Instruments", + [3]byte{216, 150, 133}: "GoPro", + [3]byte{216, 150, 149}: "Apple, Inc.", + [3]byte{216, 150, 224}: "Alibaba Cloud Computing Ltd.", + [3]byte{216, 151, 59}: "Artesyn Embedded Technologies", + [3]byte{216, 151, 96}: "C2 Development, Inc.", + [3]byte{216, 151, 124}: "Grey Innovation", + [3]byte{216, 151, 186}: "PEGATRON CORPORATION", + [3]byte{216, 154, 52}: "Beijing SHENQI Technology Co., Ltd.", + [3]byte{216, 157, 103}: "Hewlett Packard", + [3]byte{216, 157, 185}: "eMegatech International Corp.", + [3]byte{216, 158, 63}: "Apple, Inc.", + [3]byte{216, 161, 5}: "Syslane, Co., Ltd.", + [3]byte{216, 162, 94}: "Apple, Inc.", + [3]byte{216, 173, 221}: "Sonavation, Inc.", + [3]byte{216, 174, 144}: "Itibia Technologies", + [3]byte{216, 175, 59}: "Hangzhou Bigbright Integrated communications system Co.,Ltd", + [3]byte{216, 175, 241}: "Panasonic Appliances Company", + [3]byte{216, 176, 46}: "Guangzhou Zonerich Business Machine Co., LTD.", + [3]byte{216, 176, 76}: "Jinan USR IOT Technology Co., Ltd.", + [3]byte{216, 177, 42}: "Panasonic Mobile Communications Co., Ltd.", + [3]byte{216, 177, 144}: "Cisco Systems, Inc", + [3]byte{216, 179, 119}: "HTC Corporation", + [3]byte{216, 182, 183}: "Comtrend Corporation", + [3]byte{216, 182, 193}: "NetworkAccountant, Inc.", + [3]byte{216, 182, 214}: "Blu Tether Limited", + [3]byte{216, 184, 246}: "Nantworks", + [3]byte{216, 185, 14}: "Triple Domain Vision Co.,Ltd.", + [3]byte{216, 187, 44}: "Apple, Inc.", + [3]byte{216, 191, 76}: "Victory Concept Electronics Limited", + [3]byte{216, 192, 104}: "Netgenetech.co.,ltd.", + [3]byte{216, 192, 106}: "Hunantv.com Interactive Entertainment Media Co.,Ltd.", + [3]byte{216, 195, 251}: "DETRACOM", + [3]byte{216, 196, 106}: "Murata Manufacturing Co., Ltd.", + [3]byte{216, 196, 233}: "Samsung Electronics Co.,Ltd", + [3]byte{216, 198, 145}: "Hichan Technology Corp.", + [3]byte{216, 199, 113}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{216, 199, 200}: "Aruba Networks", + [3]byte{216, 200, 233}: "Phicomm (Shanghai) Co., Ltd.", + [3]byte{216, 201, 157}: "EA DISPLAY LIMITED", + [3]byte{216, 203, 138}: "Micro-Star INTL CO., LTD.", + [3]byte{216, 207, 156}: "Apple, Inc.", + [3]byte{216, 209, 203}: "Apple, Inc.", + [3]byte{216, 210, 124}: "JEMA ENERGY, SA", + [3]byte{216, 211, 133}: "Hewlett Packard", + [3]byte{216, 212, 60}: "Sony Corporation", + [3]byte{216, 213, 185}: "Rainforest Automation, Inc.", + [3]byte{216, 214, 126}: "GSK CNC EQUIPMENT CO.,LTD", + [3]byte{216, 215, 35}: "IDS, Inc", + [3]byte{216, 216, 102}: "SHENZHEN TOZED TECHNOLOGIES CO.,LTD.", + [3]byte{216, 218, 82}: "APATOR S.A.", + [3]byte{216, 220, 233}: "Kunshan Erlab ductless filtration system Co.,Ltd", + [3]byte{216, 221, 95}: "BALMUDA Inc.", + [3]byte{216, 221, 253}: "Texas Instruments", + [3]byte{216, 222, 206}: "ISUNG CO.,LTD", + [3]byte{216, 223, 13}: "beroNet GmbH", + [3]byte{216, 224, 184}: "BULAT LLC", + [3]byte{216, 224, 225}: "Samsung Electronics Co.,Ltd", + [3]byte{216, 227, 174}: "CIRTEC MEDICAL SYSTEMS", + [3]byte{216, 229, 109}: "TCT mobile ltd", + [3]byte{216, 231, 43}: "NetScout Systems, Inc.", + [3]byte{216, 231, 67}: "Wush, Inc", + [3]byte{216, 233, 82}: "KEOPSYS", + [3]byte{216, 235, 151}: "TRENDnet, Inc.", + [3]byte{216, 238, 120}: "Moog Protokraft", + [3]byte{216, 239, 205}: "Nokia", + [3]byte{216, 240, 242}: "Zeebo Inc", + [3]byte{216, 247, 16}: "Libre Wireless Technologies Inc.", + [3]byte{216, 251, 17}: "AXACORE", + [3]byte{216, 251, 94}: "ASKEY COMPUTER CORP", + [3]byte{216, 251, 104}: "Cloud Corner Ltd.", + [3]byte{216, 252, 56}: "Giantec Semiconductor Inc", + [3]byte{216, 252, 147}: "Intel Corporate", + [3]byte{216, 254, 143}: "IDFone Co., Ltd.", + [3]byte{216, 254, 227}: "D-Link International", + [3]byte{220, 0, 119}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{220, 2, 101}: "Meditech Kft", + [3]byte{220, 2, 142}: "zte corporation", + [3]byte{220, 5, 47}: "National Products Inc.", + [3]byte{220, 5, 117}: "SIEMENS ENERGY AUTOMATION", + [3]byte{220, 5, 237}: "Nabtesco Corporation", + [3]byte{220, 7, 193}: "HangZhou QiYang Technology Co.,Ltd.", + [3]byte{220, 8, 86}: "Alcatel-Lucent Enterprise", + [3]byte{220, 9, 20}: "Talk-A-Phone Co.", + [3]byte{220, 9, 76}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{220, 11, 26}: "ADB Broadband Italia", + [3]byte{220, 11, 52}: "LG Electronics (Mobile Communications)", + [3]byte{220, 12, 92}: "Apple, Inc.", + [3]byte{220, 13, 48}: "Shenzhen Feasycom Technology Co., Ltd.", + [3]byte{220, 14, 161}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{220, 21, 219}: "Ge Ruili Intelligent Technology ( Beijing ) Co., Ltd.", + [3]byte{220, 22, 162}: "Medtronic Diabetes", + [3]byte{220, 23, 90}: "Hitachi High-Technologies Corporation", + [3]byte{220, 23, 146}: "Captivate Network", + [3]byte{220, 26, 1}: "Ecoliv Technology ( Shenzhen ) Ltd.", + [3]byte{220, 26, 197}: "vivo Mobile Communication Co., Ltd.", + [3]byte{220, 29, 159}: "U & B tech", + [3]byte{220, 29, 212}: "Microstep-MIS spol. s r.o.", + [3]byte{220, 30, 163}: "Accensus LLC", + [3]byte{220, 32, 8}: "ASD Electronics Ltd ", + [3]byte{220, 41, 58}: "Shenzhen Nuoshi Technology Co., LTD.", + [3]byte{220, 42, 20}: "Shanghai Longjing Technology Co.", + [3]byte{220, 43, 42}: "Apple, Inc.", + [3]byte{220, 43, 97}: "Apple, Inc.", + [3]byte{220, 43, 102}: "InfoBLOCK S.A. de C.V.", + [3]byte{220, 43, 202}: "Zera GmbH", + [3]byte{220, 44, 38}: "Iton Technology Limited", + [3]byte{220, 45, 203}: "Beijing Unis HengYue Technology Co., Ltd.", + [3]byte{220, 46, 106}: "HCT. Co., Ltd.", + [3]byte{220, 47, 3}: "Step forward Group Co., Ltd.", + [3]byte{220, 48, 156}: "Heyrex Limited", + [3]byte{220, 51, 13}: "Qingdao Haier Telecom Co.,Ltd", + [3]byte{220, 51, 80}: "TechSAT GmbH", + [3]byte{220, 53, 241}: "Positivo Informática SA.", + [3]byte{220, 55, 20}: "Apple, Inc.", + [3]byte{220, 55, 82}: "GE", + [3]byte{220, 55, 210}: "Hunan HKT Electronic Technology Co., Ltd", + [3]byte{220, 56, 225}: "Juniper Networks", + [3]byte{220, 57, 121}: "Skyport Systems", + [3]byte{220, 58, 94}: "Roku, Inc.", + [3]byte{220, 60, 46}: "Manufacturing System Insights, Inc.", + [3]byte{220, 60, 132}: "Ticom Geomatics, Inc.", + [3]byte{220, 60, 246}: "Atomic Rules LLC", + [3]byte{220, 62, 81}: "Solberg & Andersen AS", + [3]byte{220, 62, 248}: "Nokia Corporation", + [3]byte{220, 65, 95}: "Apple, Inc.", + [3]byte{220, 68, 39}: "IEEE Registration Authority", + [3]byte{220, 68, 109}: "Allwinner Technology Co., Ltd", + [3]byte{220, 69, 23}: "ARRIS Group, Inc.", + [3]byte{220, 73, 201}: "CASCO SIGNAL LTD", + [3]byte{220, 74, 62}: "Hewlett Packard", + [3]byte{220, 77, 35}: "MRV Comunications", + [3]byte{220, 78, 222}: "SHINYEI TECHNOLOGY CO., LTD.", + [3]byte{220, 83, 96}: "Intel Corporate", + [3]byte{220, 83, 124}: "Compal Broadband Networks, Inc.", + [3]byte{220, 86, 230}: "Shenzhen Bococom Technology Co.,LTD", + [3]byte{220, 87, 38}: "Power-One", + [3]byte{220, 94, 54}: "Paterson Technology", + [3]byte{220, 96, 161}: "Teledyne DALSA Professional Imaging", + [3]byte{220, 100, 124}: "C.R.S. iiMotion GmbH", + [3]byte{220, 100, 184}: "Shenzhen JingHanDa Electronics Co.Ltd", + [3]byte{220, 102, 58}: "Apacer Technology Inc.", + [3]byte{220, 102, 114}: "Samsung Electronics Co.,Ltd", + [3]byte{220, 109, 205}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{220, 111, 0}: "Livescribe, Inc.", + [3]byte{220, 111, 8}: "Bay Storage Technology", + [3]byte{220, 112, 20}: "Private", + [3]byte{220, 113, 68}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{220, 120, 52}: "LOGICOM SA", + [3]byte{220, 123, 148}: "Cisco Systems, Inc", + [3]byte{220, 127, 164}: "2Wire Inc", + [3]byte{220, 130, 91}: "JANUS, spol. s r.o.", + [3]byte{220, 130, 246}: "iPort", + [3]byte{220, 133, 222}: "AzureWave Technology Inc.", + [3]byte{220, 134, 216}: "Apple, Inc.", + [3]byte{220, 154, 142}: "Nanjing Cocomm electronics co., LTD", + [3]byte{220, 155, 30}: "Intercom, Inc.", + [3]byte{220, 155, 156}: "Apple, Inc.", + [3]byte{220, 156, 82}: "Sapphire Technology Limited.", + [3]byte{220, 156, 159}: "Shenzhen YOUHUA Technology Co., Ltd", + [3]byte{220, 159, 164}: "Nokia Corporation", + [3]byte{220, 159, 219}: "Ubiquiti Networks Inc.", + [3]byte{220, 163, 172}: "RBcloudtech", + [3]byte{220, 164, 202}: "Apple, Inc.", + [3]byte{220, 165, 244}: "Cisco Systems, Inc", + [3]byte{220, 166, 189}: "Beijing Lanbo Technology Co., Ltd.", + [3]byte{220, 167, 217}: "Compressor Controls Corp", + [3]byte{220, 168, 207}: "New Spin Golf, LLC.", + [3]byte{220, 169, 4}: "Apple, Inc.", + [3]byte{220, 169, 113}: "Intel Corporate", + [3]byte{220, 169, 137}: "MACANDC", + [3]byte{220, 173, 158}: "GreenPriz", + [3]byte{220, 174, 4}: "CELOXICA Ltd", + [3]byte{220, 176, 88}: "Bürkert Werke GmbH", + [3]byte{220, 179, 180}: "Honeywell Environmental & Combustion Controls (Tianjin) Co., Ltd.", + [3]byte{220, 180, 196}: "Microsoft XCG", + [3]byte{220, 191, 144}: "HUIZHOU QIAOXING TELECOMMUNICATION INDUSTRY CO.,LTD.", + [3]byte{220, 192, 219}: "Shenzhen Kaiboer Technology Co., Ltd.", + [3]byte{220, 192, 235}: "ASSA ABLOY CÔTE PICARDE", + [3]byte{220, 193, 1}: "SOLiD Technologies, Inc.", + [3]byte{220, 196, 34}: "Systembase Limited", + [3]byte{220, 198, 34}: "BUHEUNG SYSTEM", + [3]byte{220, 198, 75}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{220, 199, 147}: "Nokia Corporation", + [3]byte{220, 203, 168}: "Explora Technologies Inc", + [3]byte{220, 206, 65}: "FE GLOBAL HONG KONG LIMITED", + [3]byte{220, 206, 188}: "Shenzhen JSR Technology Co.,Ltd.", + [3]byte{220, 206, 193}: "Cisco Systems, Inc", + [3]byte{220, 207, 148}: "Beijing Rongcheng Hutong Technology Co., Ltd.", + [3]byte{220, 207, 150}: "Samsung Electronics Co.,Ltd", + [3]byte{220, 208, 247}: "Bentek Systems Ltd.", + [3]byte{220, 210, 85}: "Kinpo Electronics, Inc.", + [3]byte{220, 210, 252}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{220, 211, 33}: "HUMAX Co., Ltd.", + [3]byte{220, 213, 42}: "Sunny Heart Limited", + [3]byte{220, 216, 124}: "Beijing Jingdong Century Trading Co., LTD.", + [3]byte{220, 216, 127}: "Shenzhen JoinCyber Telecom Equipment Ltd", + [3]byte{220, 217, 22}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{220, 218, 79}: "GETCK TECHNOLOGY, INC", + [3]byte{220, 219, 112}: "Tonfunk Systementwicklung und Service GmbH", + [3]byte{220, 220, 7}: "TRP Systems BV", + [3]byte{220, 222, 202}: "Akyllor", + [3]byte{220, 224, 38}: "Patrol Tag, Inc", + [3]byte{220, 225, 173}: "Shenzhen Wintop Photoelectric Technology Co., Ltd", + [3]byte{220, 226, 172}: "Lumens Digital Optics Inc.", + [3]byte{220, 229, 120}: "Experimental Factory of Scientific Engineering and Special Design Department", + [3]byte{220, 231, 28}: "AUG Elektronik GmbH", + [3]byte{220, 232, 56}: "CK Telecom (Shenzhen) Limited", + [3]byte{220, 235, 148}: "Cisco Systems, Inc", + [3]byte{220, 236, 6}: "Heimi Network Technology Co., Ltd.", + [3]byte{220, 238, 6}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{220, 239, 9}: "NETGEAR", + [3]byte{220, 239, 202}: "Murata Manufacturing Co., Ltd.", + [3]byte{220, 240, 93}: "Letta Teknoloji", + [3]byte{220, 240, 144}: "Private", + [3]byte{220, 241, 16}: "Nokia Corporation", + [3]byte{220, 247, 85}: "SITRONIK", + [3]byte{220, 248, 88}: "Lorent Networks, Inc.", + [3]byte{220, 250, 213}: "STRONG Ges.m.b.H.", + [3]byte{220, 251, 2}: "BUFFALO.INC", + [3]byte{220, 254, 7}: "PEGATRON CORPORATION", + [3]byte{220, 254, 24}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{224, 3, 112}: "ShenZhen Continental Wireless Technology Co., Ltd.", + [3]byte{224, 5, 197}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{224, 6, 230}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{224, 7, 27}: "Hewlett Packard Enterprise", + [3]byte{224, 11, 40}: "Inovonics", + [3]byte{224, 12, 127}: "Nintendo Co., Ltd.", + [3]byte{224, 13, 185}: "Cree, Inc.", + [3]byte{224, 14, 218}: "Cisco Systems, Inc", + [3]byte{224, 16, 127}: "Ruckus Wireless", + [3]byte{224, 20, 62}: "Modoosis Inc.", + [3]byte{224, 24, 119}: "FUJITSU LIMITED", + [3]byte{224, 25, 29}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{224, 26, 234}: "Allied Telesis, Inc.", + [3]byte{224, 28, 65}: "Aerohive Networks Inc.", + [3]byte{224, 28, 238}: "Bravo Tech, Inc.", + [3]byte{224, 29, 56}: "Beijing HuaqinWorld Technology Co.,Ltd", + [3]byte{224, 29, 59}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{224, 30, 7}: "Anite Telecoms US. Inc", + [3]byte{224, 31, 10}: "Xslent Energy Technologies. LLC", + [3]byte{224, 34, 2}: "ARRIS Group, Inc.", + [3]byte{224, 36, 127}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{224, 37, 56}: "Titan Pet Products", + [3]byte{224, 38, 48}: "Intrigue Technologies, Inc.", + [3]byte{224, 38, 54}: "Nortel Networks", + [3]byte{224, 39, 26}: "TTC Next-generation Home Network System WG", + [3]byte{224, 40, 97}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{224, 40, 109}: "AVM Audiovisuelles Marketing und Computersysteme GmbH", + [3]byte{224, 42, 130}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{224, 44, 178}: "Lenovo Mobile Communication (Wuhan) Company Limited", + [3]byte{224, 44, 243}: "MRS Electronic GmbH", + [3]byte{224, 47, 109}: "Cisco Systems, Inc", + [3]byte{224, 48, 5}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{224, 49, 158}: "Valve Corporation", + [3]byte{224, 49, 208}: "SZ Telstar CO., LTD", + [3]byte{224, 52, 228}: "Feit Electric Company, Inc.", + [3]byte{224, 53, 96}: "Challenger Supply Holdings, LLC", + [3]byte{224, 54, 118}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{224, 54, 227}: "Stage One International Co., Ltd.", + [3]byte{224, 55, 191}: "Wistron Neweb Corporation", + [3]byte{224, 57, 215}: "Plexxi, Inc.", + [3]byte{224, 60, 91}: "SHENZHEN JIAXINJIE ELECTRON CO.,LTD", + [3]byte{224, 62, 68}: "Broadcom", + [3]byte{224, 62, 74}: "Cavanagh Group International", + [3]byte{224, 62, 125}: "data-complex GmbH", + [3]byte{224, 63, 73}: "ASUSTek COMPUTER INC.", + [3]byte{224, 65, 54}: "MitraStar Technology Corp.", + [3]byte{224, 67, 219}: "Shenzhen ViewAt Technology Co.,Ltd. ", + [3]byte{224, 70, 154}: "NETGEAR", + [3]byte{224, 72, 175}: "Premietech Limited", + [3]byte{224, 75, 69}: "Hi-P Electronics Pte Ltd", + [3]byte{224, 79, 67}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{224, 79, 189}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{224, 80, 139}: "Zhejiang Dahua Technology Co., Ltd.", + [3]byte{224, 81, 36}: "NXP Semiconductors", + [3]byte{224, 81, 99}: "Arcadyan Corporation", + [3]byte{224, 85, 61}: "Cisco Meraki", + [3]byte{224, 85, 151}: "Emergent Vision Technologies Inc.", + [3]byte{224, 86, 244}: "AxesNetwork Solutions inc.", + [3]byte{224, 88, 158}: "Laerdal Medical", + [3]byte{224, 91, 112}: "Innovid, Co., Ltd.", + [3]byte{224, 93, 166}: "Detlef Fink Elektronik & Softwareentwicklung", + [3]byte{224, 95, 69}: "Apple, Inc.", + [3]byte{224, 95, 185}: "Cisco Systems, Inc", + [3]byte{224, 96, 102}: "Sercomm Corporation", + [3]byte{224, 97, 178}: "HANGZHOU ZENOINTEL TECHNOLOGY CO., LTD", + [3]byte{224, 98, 144}: "Jinan Jovision Science & Technology Co., Ltd.", + [3]byte{224, 99, 229}: "Sony Mobile Communications AB", + [3]byte{224, 100, 187}: "DigiView S.r.l.", + [3]byte{224, 102, 120}: "Apple, Inc.", + [3]byte{224, 103, 179}: "C-Data Technology Co., Ltd", + [3]byte{224, 104, 109}: "Raybased AB", + [3]byte{224, 105, 149}: "PEGATRON CORPORATION", + [3]byte{224, 117, 10}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{224, 117, 125}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{224, 118, 208}: "AMPAK Technology, Inc.", + [3]byte{224, 120, 163}: "Shanghai Winner Information Technology Co.,Inc", + [3]byte{224, 124, 19}: "zte corporation", + [3]byte{224, 124, 98}: "Whistle Labs, Inc.", + [3]byte{224, 127, 83}: "TECHBOARD SRL", + [3]byte{224, 127, 136}: "EVIDENCE Network SIA", + [3]byte{224, 129, 119}: "GreenBytes, Inc.", + [3]byte{224, 135, 177}: "Nata-Info Ltd.", + [3]byte{224, 136, 93}: "Technicolor CH USA Inc.", + [3]byte{224, 137, 157}: "Cisco Systems, Inc", + [3]byte{224, 138, 126}: "Exponent", + [3]byte{224, 142, 60}: "Aztech Electronics Pte Ltd", + [3]byte{224, 143, 236}: "REPOTEC CO., LTD.", + [3]byte{224, 145, 83}: "XAVi Technologies Corp.", + [3]byte{224, 145, 245}: "NETGEAR", + [3]byte{224, 148, 103}: "Intel Corporate", + [3]byte{224, 149, 121}: "ORTHOsoft inc, d/b/a Zimmer CAS", + [3]byte{224, 151, 150}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{224, 151, 242}: "Atomax Inc.", + [3]byte{224, 152, 97}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{224, 153, 113}: "Samsung Electronics Co.,Ltd", + [3]byte{224, 157, 49}: "Intel Corporate", + [3]byte{224, 157, 184}: "PLANEX COMMUNICATIONS INC.", + [3]byte{224, 157, 250}: "Wanan Hongsheng Electronic Co.Ltd", + [3]byte{224, 161, 152}: "NOJA Power Switchgear Pty Ltd", + [3]byte{224, 161, 215}: "SFR", + [3]byte{224, 163, 15}: "Pevco", + [3]byte{224, 163, 172}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{224, 166, 112}: "Nokia Corporation", + [3]byte{224, 167, 0}: "Verkada Inc", + [3]byte{224, 168, 184}: "Le Shi Zhi Xin Electronic Technology (Tianjin) Limited", + [3]byte{224, 170, 176}: "GENERAL VISION ELECTRONICS CO. LTD.", + [3]byte{224, 171, 254}: "Orb Networks, Inc.", + [3]byte{224, 172, 203}: "Apple, Inc.", + [3]byte{224, 172, 241}: "Cisco Systems, Inc", + [3]byte{224, 174, 94}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{224, 174, 178}: "Bender GmbH & Co.KG", + [3]byte{224, 174, 237}: "LOENK", + [3]byte{224, 175, 75}: "Pluribus Networks, Inc.", + [3]byte{224, 178, 241}: "FN-LINK TECHNOLOGY LIMITED", + [3]byte{224, 181, 45}: "Apple, Inc.", + [3]byte{224, 182, 245}: "IEEE Registration Authority", + [3]byte{224, 183, 10}: "ARRIS Group, Inc.", + [3]byte{224, 183, 177}: "ARRIS Group, Inc.", + [3]byte{224, 185, 77}: "SHENZHEN BILIAN ELECTRONIC CO.,LTD", + [3]byte{224, 185, 165}: "AzureWave Technology Inc.", + [3]byte{224, 185, 186}: "Apple, Inc.", + [3]byte{224, 185, 229}: "Technicolor", + [3]byte{224, 188, 67}: "C2 Microsystems, Inc.", + [3]byte{224, 192, 209}: "CK Telecom (Shenzhen) Limited", + [3]byte{224, 194, 134}: "Aisai Communication Technology Co., Ltd.", + [3]byte{224, 194, 183}: "Masimo Corporation", + [3]byte{224, 195, 243}: "zte corporation", + [3]byte{224, 198, 179}: "MilDef AB", + [3]byte{224, 199, 103}: "Apple, Inc.", + [3]byte{224, 199, 157}: "Texas Instruments", + [3]byte{224, 200, 106}: "SHENZHEN TW-SCIE Co., Ltd", + [3]byte{224, 201, 34}: "Jireh Energy Tech., Ltd.", + [3]byte{224, 201, 122}: "Apple, Inc.", + [3]byte{224, 202, 77}: "Shenzhen Unistar Communication Co.,LTD", + [3]byte{224, 202, 148}: "ASKEY COMPUTER CORP", + [3]byte{224, 203, 29}: "Private", + [3]byte{224, 203, 78}: "ASUSTek COMPUTER INC.", + [3]byte{224, 203, 238}: "Samsung Electronics Co.,Ltd", + [3]byte{224, 205, 253}: "Beijing E3Control Technology Co, LTD", + [3]byte{224, 206, 195}: "ASKEY COMPUTER CORP", + [3]byte{224, 207, 45}: "Gemintek Corporation", + [3]byte{224, 209, 10}: "Katoudenkikougyousyo co ltd", + [3]byte{224, 209, 115}: "Cisco Systems, Inc", + [3]byte{224, 209, 230}: "Aliph dba Jawbone", + [3]byte{224, 211, 26}: "EQUES Technology Co., Limited", + [3]byte{224, 215, 186}: "Texas Instruments", + [3]byte{224, 217, 162}: "Hippih aps", + [3]byte{224, 217, 227}: "Eltex Enterprise Ltd.", + [3]byte{224, 218, 220}: "JVC KENWOOD Corporation", + [3]byte{224, 219, 16}: "Samsung Electronics Co.,Ltd", + [3]byte{224, 219, 85}: "Dell Inc.", + [3]byte{224, 219, 136}: "Open Standard Digital-IF Interface for SATCOM Systems", + [3]byte{224, 220, 160}: "Siemens Industrial Automation Products Ltd Chengdu", + [3]byte{224, 221, 192}: "vivo Mobile Communication Co., Ltd.", + [3]byte{224, 229, 207}: "Texas Instruments", + [3]byte{224, 230, 49}: "SNB TECHNOLOGIES LIMITED", + [3]byte{224, 231, 81}: "Nintendo Co., Ltd.", + [3]byte{224, 231, 187}: "Nureva, Inc.", + [3]byte{224, 232, 232}: "Olive Telecommunication Pvt. Ltd", + [3]byte{224, 237, 26}: "vastriver Technology Co., Ltd", + [3]byte{224, 237, 199}: "Shenzhen Friendcom Technology Development Co., Ltd", + [3]byte{224, 238, 27}: "Panasonic Automotive Systems Company of America", + [3]byte{224, 239, 37}: "Lintes Technology Co., Ltd.", + [3]byte{224, 242, 17}: "Digitalwatt", + [3]byte{224, 243, 121}: "Vaddio", + [3]byte{224, 245, 198}: "Apple, Inc.", + [3]byte{224, 245, 202}: "CHENG UEI PRECISION INDUSTRY CO.,LTD.", + [3]byte{224, 248, 71}: "Apple, Inc.", + [3]byte{224, 249, 190}: "Cloudena Corp.", + [3]byte{224, 250, 236}: "Platan sp. z o.o. sp. k.", + [3]byte{224, 255, 247}: "Softiron Inc.", + [3]byte{228, 2, 155}: "Intel Corporate", + [3]byte{228, 4, 57}: "TomTom Software Ltd", + [3]byte{228, 17, 91}: "Hewlett Packard", + [3]byte{228, 18, 24}: "ShenZhen Rapoo Technology Co., Ltd.", + [3]byte{228, 18, 29}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 18, 137}: "topsystem Systemhaus GmbH", + [3]byte{228, 23, 216}: "8BITDO TECHNOLOGY HK LIMITED", + [3]byte{228, 24, 107}: "ZyXEL Communications Corporation", + [3]byte{228, 26, 44}: "ZPE Systems, Inc.", + [3]byte{228, 28, 75}: "V2 TECHNOLOGY, INC.", + [3]byte{228, 29, 45}: "Mellanox Technologies, Inc.", + [3]byte{228, 31, 19}: "IBM Corp", + [3]byte{228, 34, 165}: "PLANTRONICS, INC.", + [3]byte{228, 35, 84}: "SHENZHEN FUZHI SOFTWARE TECHNOLOGY CO.,LTD", + [3]byte{228, 37, 231}: "Apple, Inc.", + [3]byte{228, 37, 233}: "Color-Chip", + [3]byte{228, 39, 113}: "Smartlabs", + [3]byte{228, 42, 211}: "Magneti Marelli S.p.A. Powertrain", + [3]byte{228, 44, 86}: "Lilee Systems, Ltd.", + [3]byte{228, 45, 2}: "TCT mobile ltd", + [3]byte{228, 47, 38}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{228, 47, 86}: "OptoMET GmbH", + [3]byte{228, 47, 246}: "Unicore communication Inc.", + [3]byte{228, 50, 203}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 53, 147}: "Hangzhou GoTo technology Co.Ltd", + [3]byte{228, 53, 200}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{228, 53, 251}: "Sabre Technology (Hull) Ltd", + [3]byte{228, 55, 215}: "HENRI DEPAEPE S.A.S.", + [3]byte{228, 56, 242}: "Advantage Controls", + [3]byte{228, 62, 215}: "Arcadyan Corporation", + [3]byte{228, 63, 162}: "Wuxi DSP Technologies Inc.", + [3]byte{228, 64, 226}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 65, 230}: "Ottec Technology GmbH", + [3]byte{228, 66, 166}: "Intel Corporate", + [3]byte{228, 70, 189}: "C&C TECHNIC TAIWAN CO., LTD.", + [3]byte{228, 71, 144}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{228, 72, 199}: "Cisco SPVTG", + [3]byte{228, 76, 108}: "Shenzhen Guo Wei Electronic Co,. Ltd.", + [3]byte{228, 78, 24}: "Gardasoft VisionLimited", + [3]byte{228, 79, 41}: "MA Lighting Technology GmbH", + [3]byte{228, 79, 95}: "EDS Elektronik Destek San.Tic.Ltd.Sti", + [3]byte{228, 80, 154}: "HW Communications Ltd", + [3]byte{228, 85, 234}: "Dedicated Computing", + [3]byte{228, 86, 20}: "Suttle Apparatus", + [3]byte{228, 87, 168}: "Stuart Manufacturing, Inc.", + [3]byte{228, 88, 184}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 88, 231}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 90, 162}: "vivo Mobile Communication Co., Ltd.", + [3]byte{228, 93, 81}: "SFR", + [3]byte{228, 93, 82}: "Avaya Inc", + [3]byte{228, 93, 117}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 98, 81}: "HAO CHENG GROUP LIMITED", + [3]byte{228, 100, 73}: "ARRIS Group, Inc.", + [3]byte{228, 103, 186}: "Danish Interpretation Systems A/S", + [3]byte{228, 104, 163}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{228, 105, 90}: "Dictum Health, Inc.", + [3]byte{228, 108, 33}: "messMa GmbH", + [3]byte{228, 111, 19}: "D-Link International", + [3]byte{228, 113, 133}: "Securifi Ltd", + [3]byte{228, 117, 30}: "Getinge Sterilization AB", + [3]byte{228, 119, 35}: "zte corporation", + [3]byte{228, 119, 107}: "AARTESYS AG", + [3]byte{228, 119, 212}: "Minrray Industry Co.,Ltd ", + [3]byte{228, 123, 63}: "BEIJING CO-CLOUD TECHNOLOGY LTD.", + [3]byte{228, 124, 249}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 125, 90}: "Beijing Hanbang Technology Corp.", + [3]byte{228, 125, 189}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 125, 235}: "Shanghai Notion Information Technology CO.,LTD.", + [3]byte{228, 126, 102}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{228, 127, 178}: "FUJITSU LIMITED", + [3]byte{228, 129, 132}: "Nokia", + [3]byte{228, 129, 179}: "Shenzhen ACT Industrial Co.,Ltd.", + [3]byte{228, 131, 153}: "ARRIS Group, Inc.", + [3]byte{228, 133, 1}: "Geberit International AG", + [3]byte{228, 138, 213}: "RF WINDOW CO., LTD.", + [3]byte{228, 139, 127}: "Apple, Inc.", + [3]byte{228, 140, 15}: "Discovery Insure", + [3]byte{228, 141, 140}: "Routerboard.com", + [3]byte{228, 144, 105}: "Rockwell Automation", + [3]byte{228, 144, 126}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{228, 146, 231}: "Gridlink Tech. Co.,Ltd.", + [3]byte{228, 146, 251}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 149, 110}: "IEEE Registration Authority", + [3]byte{228, 150, 174}: "ALTOGRAPHICS Inc.", + [3]byte{228, 151, 240}: "Shanghai VLC Technologies Ltd. Co.", + [3]byte{228, 152, 209}: "Microsoft Mobile Oy", + [3]byte{228, 152, 214}: "Apple, Inc.", + [3]byte{228, 154, 121}: "Apple, Inc.", + [3]byte{228, 158, 18}: "FREEBOX SAS", + [3]byte{228, 161, 230}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{228, 163, 47}: "Shanghai Artimen Technology Co., Ltd.", + [3]byte{228, 163, 135}: "Control Solutions LLC", + [3]byte{228, 164, 113}: "Intel Corporate", + [3]byte{228, 165, 239}: "TRON LINK ELECTRONICS CO., LTD.", + [3]byte{228, 167, 73}: "Palo Alto Networks ", + [3]byte{228, 167, 160}: "Intel Corporate", + [3]byte{228, 167, 253}: "Cellco Partnership", + [3]byte{228, 168, 182}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{228, 170, 93}: "Cisco Systems, Inc", + [3]byte{228, 171, 70}: "UAB Selteka", + [3]byte{228, 173, 125}: "SCL Elements", + [3]byte{228, 175, 161}: "HES-SO", + [3]byte{228, 176, 5}: "Beijing IQIYI Science & Technology Co., Ltd.", + [3]byte{228, 176, 33}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 179, 24}: "Intel Corporate", + [3]byte{228, 186, 217}: "360 Fly Inc.", + [3]byte{228, 190, 237}: "Netcore Technology Inc.", + [3]byte{228, 193, 70}: "Objetivos y Servicios de Valor A", + [3]byte{228, 193, 241}: "SHENZHEN SPOTMAU INFORMATION TECHNOLIGY CO., Ltd ", + [3]byte{228, 194, 209}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{228, 198, 43}: "Airware", + [3]byte{228, 198, 61}: "Apple, Inc.", + [3]byte{228, 198, 230}: "Mophie, LLC", + [3]byte{228, 199, 34}: "Cisco Systems, Inc", + [3]byte{228, 200, 1}: "BLU Products Inc", + [3]byte{228, 200, 6}: "Ceiec Electric Technology Inc.", + [3]byte{228, 206, 2}: "WyreStorm Technologies Ltd", + [3]byte{228, 206, 112}: "Health & Life co., Ltd.", + [3]byte{228, 206, 143}: "Apple, Inc.", + [3]byte{228, 211, 50}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{228, 211, 241}: "Cisco Systems, Inc", + [3]byte{228, 213, 61}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{228, 215, 29}: "Oraya Therapeutics", + [3]byte{228, 221, 121}: "En-Vision America, Inc.", + [3]byte{228, 224, 197}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 228, 9}: "LEIFHEIT AG", + [3]byte{228, 228, 171}: "Apple, Inc.", + [3]byte{228, 236, 16}: "Nokia Corporation", + [3]byte{228, 238, 253}: "MR&D Manufacturing", + [3]byte{228, 243, 101}: "Time-O-Matic, Inc.", + [3]byte{228, 243, 227}: "Shanghai iComhome Co.,Ltd.", + [3]byte{228, 243, 245}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{228, 244, 198}: "NETGEAR", + [3]byte{228, 247, 161}: "Datafox GmbH", + [3]byte{228, 248, 156}: "Intel Corporate", + [3]byte{228, 248, 239}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 249, 57}: "Minxon Hotel Technology INC.", + [3]byte{228, 250, 29}: "PAD Peripheral Advanced Design Inc.", + [3]byte{228, 250, 237}: "Samsung Electronics Co.,Ltd", + [3]byte{228, 250, 253}: "Intel Corporate", + [3]byte{228, 251, 143}: "MOBIWIRE MOBILES (NINGBO) CO.,LTD", + [3]byte{228, 254, 217}: "EDMI Europe Ltd", + [3]byte{228, 255, 221}: "ELECTRON INDIA", + [3]byte{232, 0, 54}: "Befs co,. ltd", + [3]byte{232, 3, 154}: "Samsung Electronics Co.,Ltd", + [3]byte{232, 4, 11}: "Apple, Inc.", + [3]byte{232, 4, 16}: "Private", + [3]byte{232, 4, 98}: "Cisco Systems, Inc", + [3]byte{232, 4, 243}: "Throughtek Co., Ltd.", + [3]byte{232, 5, 109}: "Nortel Networks", + [3]byte{232, 6, 136}: "Apple, Inc.", + [3]byte{232, 7, 52}: "Champion Optical Network Engineering, LLC", + [3]byte{232, 7, 191}: "SHENZHEN BOOMTECH INDUSTRY CO.,LTD", + [3]byte{232, 8, 139}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{232, 9, 69}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{232, 9, 89}: "Guoguang Electric Co.,Ltd", + [3]byte{232, 11, 19}: "Akib Systems Taiwan, INC", + [3]byte{232, 12, 56}: "DAEYOUNG INFORMATION SYSTEM CO., LTD", + [3]byte{232, 12, 117}: "Syncbak, Inc.", + [3]byte{232, 16, 46}: "Really Simple Software, Inc", + [3]byte{232, 17, 50}: "Samsung Electronics Co.,Ltd", + [3]byte{232, 17, 202}: "SHANDONG KAER ELECTRIC.CO.,LTD", + [3]byte{232, 19, 36}: "GuangZhou Bonsoninfo System CO.,LTD", + [3]byte{232, 19, 99}: "Comstock RD, Inc.", + [3]byte{232, 19, 103}: "AIRSOUND Inc.", + [3]byte{232, 21, 14}: "Nokia Corporation", + [3]byte{232, 22, 43}: "IDEO Security Co., Ltd.", + [3]byte{232, 23, 252}: "NIFTY Corporation", + [3]byte{232, 24, 99}: "IEEE Registration Authority", + [3]byte{232, 40, 119}: "TMY Co., Ltd.", + [3]byte{232, 40, 213}: "Cots Technology", + [3]byte{232, 42, 234}: "Intel Corporate", + [3]byte{232, 46, 36}: "Out of the Fog Research LLC", + [3]byte{232, 51, 129}: "ARRIS Group, Inc.", + [3]byte{232, 52, 62}: "Beijing Infosec Technologies Co., LTD.", + [3]byte{232, 55, 122}: "ZyXEL Communications Corporation", + [3]byte{232, 57, 53}: "Hewlett Packard", + [3]byte{232, 57, 223}: "ASKEY COMPUTER CORP", + [3]byte{232, 58, 18}: "Samsung Electronics Co.,Ltd", + [3]byte{232, 58, 151}: "Toshiba Corporation", + [3]byte{232, 62, 182}: "RIM", + [3]byte{232, 62, 251}: "GEODESIC LTD.", + [3]byte{232, 62, 252}: "ARRIS Group, Inc.", + [3]byte{232, 64, 64}: "Cisco Systems, Inc", + [3]byte{232, 64, 242}: "PEGATRON CORPORATION", + [3]byte{232, 67, 182}: "QNAP Systems, Inc.", + [3]byte{232, 68, 126}: "Bitdefender SRL", + [3]byte{232, 72, 31}: "Advanced Automotive Antennas", + [3]byte{232, 77, 208}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{232, 78, 6}: "EDUP INTERNATIONAL (HK) CO., LTD", + [3]byte{232, 78, 132}: "Samsung Electronics Co.,Ltd", + [3]byte{232, 78, 206}: "Nintendo Co., Ltd.", + [3]byte{232, 80, 139}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{232, 81, 110}: "TSMART Inc.", + [3]byte{232, 81, 157}: "Yeonhab Precision Co.,LTD", + [3]byte{232, 84, 132}: "NEO Information Systems Co., Ltd.", + [3]byte{232, 85, 180}: "SAI Technology Inc.", + [3]byte{232, 86, 89}: "Advanced-Connectek Inc.", + [3]byte{232, 86, 214}: "NCTech Ltd", + [3]byte{232, 90, 167}: "LLC Emzior", + [3]byte{232, 91, 91}: "LG ELECTRONICS INC", + [3]byte{232, 91, 240}: "Imaging Diagnostics", + [3]byte{232, 93, 107}: "Luminate Wireless", + [3]byte{232, 94, 83}: "Infratec Datentechnik GmbH", + [3]byte{232, 97, 31}: "Dawning Information Industry Co.,Ltd", + [3]byte{232, 97, 126}: "Liteon Technology Corporation", + [3]byte{232, 97, 131}: "Black Diamond Advanced Technology, LLC", + [3]byte{232, 97, 190}: "Melec Inc.", + [3]byte{232, 101, 73}: "Cisco Systems, Inc", + [3]byte{232, 101, 212}: "Tenda Technology Co.,Ltd.Dongguan branch", + [3]byte{232, 102, 196}: "Diamanti", + [3]byte{232, 108, 218}: "Supercomputers and Neurocomputers Research Center", + [3]byte{232, 109, 82}: "ARRIS Group, Inc.", + [3]byte{232, 109, 84}: "Digit Mobile Inc", + [3]byte{232, 109, 110}: "voestalpine SIGNALING Fareham Ltd.", + [3]byte{232, 113, 141}: "Elsys Equipamentos Eletronicos Ltda", + [3]byte{232, 116, 230}: "ADB Broadband Italia", + [3]byte{232, 117, 127}: "FIRS Technologies(Shenzhen) Co., Ltd", + [3]byte{232, 120, 161}: "BEOVIEW INTERCOM DOO", + [3]byte{232, 122, 243}: "S5 Tech S.r.l.", + [3]byte{232, 128, 46}: "Apple, Inc.", + [3]byte{232, 128, 216}: "GNTEK Electronics Co.,Ltd.", + [3]byte{232, 135, 163}: "Loxley Public Company Limited", + [3]byte{232, 136, 108}: "Shenzhen SC Technologies Co.,LTD", + [3]byte{232, 137, 44}: "ARRIS Group, Inc.", + [3]byte{232, 141, 40}: "Apple, Inc.", + [3]byte{232, 141, 245}: "ZNYX Networks, Inc.", + [3]byte{232, 142, 96}: "NSD Corporation", + [3]byte{232, 145, 32}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{232, 146, 24}: "Arcontia International AB", + [3]byte{232, 146, 164}: "LG Electronics (Mobile Communications)", + [3]byte{232, 147, 9}: "Samsung Electronics Co.,Ltd", + [3]byte{232, 148, 76}: "Cogent Healthcare Systems Ltd", + [3]byte{232, 148, 246}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{232, 150, 6}: "testo Instruments (Shenzhen) Co., Ltd.", + [3]byte{232, 153, 90}: "PiiGAB, Processinformation i Goteborg AB", + [3]byte{232, 153, 196}: "HTC Corporation", + [3]byte{232, 154, 143}: "QUANTA COMPUTER INC.", + [3]byte{232, 154, 255}: "Fujian Landi Commercial Equipment Co.,Ltd", + [3]byte{232, 157, 135}: "Toshiba", + [3]byte{232, 158, 12}: "Private", + [3]byte{232, 158, 180}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{232, 159, 236}: "CHENGDU KT ELECTRONIC HI-TECH CO.,LTD", + [3]byte{232, 163, 100}: "Signal Path International / Peachtree Audio", + [3]byte{232, 164, 193}: "Deep Sea Electronics PLC", + [3]byte{232, 167, 242}: "sTraffic", + [3]byte{232, 171, 250}: "Shenzhen Reecam Tech.Ltd.", + [3]byte{232, 177, 252}: "Intel Corporate", + [3]byte{232, 178, 172}: "Apple, Inc.", + [3]byte{232, 180, 174}: "Shenzhen C&D Electronics Co.,Ltd", + [3]byte{232, 180, 200}: "Samsung Electronics Co.,Ltd", + [3]byte{232, 183, 72}: "Cisco Systems, Inc", + [3]byte{232, 186, 112}: "Cisco Systems, Inc", + [3]byte{232, 187, 61}: "Sino Prime-Tech Limited", + [3]byte{232, 187, 168}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{232, 189, 209}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{232, 190, 129}: "Sagemcom Broadband SAS", + [3]byte{232, 193, 215}: "Philips", + [3]byte{232, 194, 41}: "H-Displays (MSC) Bhd", + [3]byte{232, 195, 32}: "Austco Communication Systems Pty Ltd", + [3]byte{232, 199, 79}: "Liteon Technology Corporation", + [3]byte{232, 203, 161}: "Nokia Corporation", + [3]byte{232, 204, 24}: "D-Link International", + [3]byte{232, 204, 50}: "Micronet LTD", + [3]byte{232, 205, 45}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{232, 206, 6}: "SkyHawke Technologies, LLC.", + [3]byte{232, 208, 250}: "MKS Instruments Deutschland GmbH", + [3]byte{232, 209, 27}: "ASKEY COMPUTER CORP", + [3]byte{232, 212, 131}: "ULTIMATE Europe Transportation Equipment GmbH", + [3]byte{232, 212, 224}: "Beijing BenyWave Technology Co., Ltd.", + [3]byte{232, 218, 150}: "Zhuhai Tianrui Electrical Power Tech. Co., Ltd.", + [3]byte{232, 218, 170}: "VideoHome Technology Corp.", + [3]byte{232, 222, 39}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{232, 222, 142}: "Integrated Device Technology (Malaysia) Sdn. Bhd.", + [3]byte{232, 222, 214}: "Intrising Networks, Inc.", + [3]byte{232, 223, 242}: "PRF Co., Ltd.", + [3]byte{232, 224, 143}: "GRAVOTECH MARKING SAS", + [3]byte{232, 224, 183}: "Toshiba", + [3]byte{232, 225, 226}: "Energotest", + [3]byte{232, 229, 214}: "Samsung Electronics Co.,Ltd", + [3]byte{232, 231, 50}: "Alcatel-Lucent Enterprise", + [3]byte{232, 231, 112}: "Warp9 Tech Design, Inc.", + [3]byte{232, 231, 118}: "Shenzhen Kootion Technology Co., Ltd", + [3]byte{232, 232, 117}: "iS5 Communications Inc.", + [3]byte{232, 234, 106}: "StarTech.com", + [3]byte{232, 234, 218}: "Denkovi Assembly Electronics LTD", + [3]byte{232, 235, 17}: "Texas Instruments", + [3]byte{232, 237, 5}: "ARRIS Group, Inc.", + [3]byte{232, 237, 243}: "Cisco Systems, Inc", + [3]byte{232, 239, 137}: "OPMEX Tech.", + [3]byte{232, 241, 176}: "Sagemcom Broadband SAS", + [3]byte{232, 242, 38}: "MILLSON CUSTOM SOLUTIONS INC.", + [3]byte{232, 242, 226}: "LG Innotek", + [3]byte{232, 242, 227}: "Starcor Beijing Co.,Limited", + [3]byte{232, 247, 36}: "Hewlett Packard Enterprise", + [3]byte{232, 249, 40}: "RFTECH SRL", + [3]byte{232, 252, 96}: "ELCOM Innovations Private Limited", + [3]byte{232, 252, 175}: "NETGEAR", + [3]byte{232, 253, 114}: "SHANGHAI LINGUO TECHNOLOGY CO., LTD.", + [3]byte{232, 253, 144}: "Turbostor", + [3]byte{232, 253, 232}: "CeLa Link Corporation", + [3]byte{236, 1, 51}: "TRINUS SYSTEMS INC.", + [3]byte{236, 1, 226}: "FOXCONN INTERCONNECT TECHNOLOGY", + [3]byte{236, 1, 238}: "GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD", + [3]byte{236, 8, 107}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{236, 13, 154}: "Mellanox Technologies, Inc.", + [3]byte{236, 14, 196}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{236, 14, 214}: "ITECH INSTRUMENTS SAS", + [3]byte{236, 16, 123}: "Samsung Electronics Co.,Ltd", + [3]byte{236, 17, 32}: "FloDesign Wind Turbine Corporation", + [3]byte{236, 17, 39}: "Texas Instruments", + [3]byte{236, 19, 178}: "Netonix", + [3]byte{236, 19, 219}: "Juniper Networks", + [3]byte{236, 20, 246}: "BioControl AS", + [3]byte{236, 23, 47}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{236, 23, 102}: "Research Centre Module", + [3]byte{236, 26, 89}: "Belkin International Inc.", + [3]byte{236, 29, 127}: "zte corporation", + [3]byte{236, 31, 114}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{236, 33, 159}: "VidaBox LLC", + [3]byte{236, 33, 229}: "Toshiba", + [3]byte{236, 34, 87}: "JiangSu NanJing University Electronic Information Technology Co.,Ltd", + [3]byte{236, 34, 128}: "D-Link International", + [3]byte{236, 35, 61}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{236, 35, 104}: "IntelliVoice Co.,Ltd.", + [3]byte{236, 36, 184}: "Texas Instruments", + [3]byte{236, 38, 202}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{236, 38, 251}: "TECC CO.,LTD.", + [3]byte{236, 42, 240}: "Ypsomed AG", + [3]byte{236, 44, 73}: "University of Tokyo", + [3]byte{236, 46, 78}: "HITACHI-LG DATA STORAGE INC", + [3]byte{236, 48, 145}: "Cisco Systems, Inc", + [3]byte{236, 53, 134}: "Apple, Inc.", + [3]byte{236, 56, 143}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{236, 59, 240}: "NovelSat", + [3]byte{236, 60, 90}: "SHEN ZHEN HENG SHENG HUI DIGITAL TECHNOLOGY CO.,LTD", + [3]byte{236, 60, 136}: "MCNEX Co.,Ltd.", + [3]byte{236, 62, 9}: "PERFORMANCE DESIGNED PRODUCTS, LLC", + [3]byte{236, 62, 247}: "Juniper Networks", + [3]byte{236, 63, 5}: "Institute 706, The Second Academy China Aerospace Science & Industry Corp", + [3]byte{236, 66, 240}: "ADL Embedded Solutions, Inc.", + [3]byte{236, 67, 139}: "YAPTV", + [3]byte{236, 67, 230}: "AWCER Ltd.", + [3]byte{236, 67, 246}: "ZyXEL Communications Corporation", + [3]byte{236, 68, 118}: "Cisco Systems, Inc", + [3]byte{236, 70, 68}: "TTK SAS", + [3]byte{236, 70, 112}: "Meinberg Funkuhren GmbH & Co. KG", + [3]byte{236, 71, 60}: "Redwire, LLC", + [3]byte{236, 73, 147}: "Qihan Technology Co., Ltd ", + [3]byte{236, 76, 77}: "ZAO NPK RoTeK", + [3]byte{236, 77, 71}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{236, 79, 130}: "Calix Inc.", + [3]byte{236, 82, 220}: "WORLD MEDIA AND TECHNOLOGY Corp.", + [3]byte{236, 84, 46}: "Shanghai XiMei Electronic Technology Co. Ltd", + [3]byte{236, 85, 249}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{236, 89, 231}: "Microsoft Corporation", + [3]byte{236, 90, 134}: "Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd", + [3]byte{236, 92, 105}: "MITSUBISHI HEAVY INDUSTRIES MECHATRONICS SYSTEMS,LTD.", + [3]byte{236, 95, 35}: "Qinghai Kimascend Electronics Technology Co. Ltd.", + [3]byte{236, 96, 224}: "AVI-ON LABS", + [3]byte{236, 98, 100}: "Global411 Internet Services, LLC", + [3]byte{236, 99, 229}: "ePBoard Design LLC", + [3]byte{236, 100, 231}: "MOCACARE Corporation", + [3]byte{236, 102, 209}: "B&W Group LTD", + [3]byte{236, 104, 129}: "Palo Alto Networks", + [3]byte{236, 108, 159}: "Chengdu Volans Technology CO.,LTD", + [3]byte{236, 113, 219}: "Shenzhen Baichuan Digital Technology Co., Ltd.", + [3]byte{236, 116, 186}: "Hirschmann Automation and Control GmbH", + [3]byte{236, 124, 116}: "Justone Technologies Co., Ltd.", + [3]byte{236, 125, 157}: "MEI", + [3]byte{236, 128, 9}: "NovaSparks", + [3]byte{236, 131, 108}: "RM Tech Co., Ltd.", + [3]byte{236, 133, 47}: "Apple, Inc.", + [3]byte{236, 136, 143}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{236, 136, 146}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{236, 137, 245}: "Lenovo Mobile Communication Technology Ltd.", + [3]byte{236, 138, 76}: "zte corporation", + [3]byte{236, 140, 162}: "Ruckus Wireless", + [3]byte{236, 142, 173}: "DLX", + [3]byte{236, 142, 174}: "Nagravision SA", + [3]byte{236, 142, 181}: "Hewlett Packard", + [3]byte{236, 146, 51}: "Eddyfi NDT Inc", + [3]byte{236, 147, 39}: "MEMMERT GmbH + Co. KG", + [3]byte{236, 147, 237}: "DDoS-Guard LTD", + [3]byte{236, 150, 129}: "2276427 Ontario Inc", + [3]byte{236, 152, 108}: "Lufft Mess- und Regeltechnik GmbH", + [3]byte{236, 152, 193}: "Beijing Risbo Network Technology Co.,Ltd", + [3]byte{236, 154, 116}: "Hewlett Packard", + [3]byte{236, 155, 91}: "Nokia Corporation", + [3]byte{236, 155, 243}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{236, 158, 205}: "Artesyn Embedded Technologies", + [3]byte{236, 162, 155}: "Kemppi Oy", + [3]byte{236, 168, 107}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{236, 169, 250}: "GUANGDONG GENIUS TECHNOLOGY CO.,LTD.", + [3]byte{236, 170, 160}: "PEGATRON CORPORATION", + [3]byte{236, 173, 184}: "Apple, Inc.", + [3]byte{236, 177, 6}: "Acuro Networks, Inc", + [3]byte{236, 177, 215}: "Hewlett Packard", + [3]byte{236, 181, 65}: "SHINANO E and E Co.Ltd.", + [3]byte{236, 184, 112}: "Beijing Heweinet Technology Co.,Ltd.", + [3]byte{236, 185, 7}: "CloudGenix Inc", + [3]byte{236, 186, 254}: "GIROPTIC", + [3]byte{236, 187, 174}: "Digivoice Tecnologia em Eletronica Ltda", + [3]byte{236, 189, 9}: "FUSION Electronics Ltd", + [3]byte{236, 189, 29}: "Cisco Systems, Inc", + [3]byte{236, 195, 138}: "Accuenergy (CANADA) Inc", + [3]byte{236, 200, 130}: "Cisco Systems, Inc", + [3]byte{236, 203, 48}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{236, 205, 109}: "Allied Telesis, Inc.", + [3]byte{236, 208, 14}: "MiraeRecognition Co., Ltd.", + [3]byte{236, 208, 64}: "GEA Farm Technologies GmbH", + [3]byte{236, 209, 154}: "Zhuhai Liming Industries Co., Ltd", + [3]byte{236, 214, 138}: "Shenzhen JMicron Intelligent Technology Developmen", + [3]byte{236, 217, 37}: "RAMI", + [3]byte{236, 217, 80}: "IRT SA", + [3]byte{236, 217, 209}: "Shenzhen TG-NET Botone Technology Co.,Ltd.", + [3]byte{236, 222, 61}: "Lamprey Networks, Inc.", + [3]byte{236, 223, 58}: "vivo Mobile Communication Co., Ltd.", + [3]byte{236, 224, 155}: "Samsung Electronics Co.,Ltd", + [3]byte{236, 225, 84}: "Beijing Unisound Information Technology Co.,Ltd.", + [3]byte{236, 225, 169}: "Cisco Systems, Inc", + [3]byte{236, 226, 253}: "SKG Electric Group(Thailand) Co., Ltd.", + [3]byte{236, 229, 18}: "tado GmbH", + [3]byte{236, 229, 85}: "Hirschmann Automation", + [3]byte{236, 231, 68}: "Omntec mfg. inc", + [3]byte{236, 233, 11}: "SISTEMA SOLUCOES ELETRONICAS LTDA - EASYTECH", + [3]byte{236, 233, 21}: "STI Ltd", + [3]byte{236, 233, 248}: "Guang Zhou TRI-SUN Electronics Technology Co., Ltd", + [3]byte{236, 234, 3}: "DARFON LIGHTING CORP", + [3]byte{236, 238, 216}: "ZTLX Network Technology Co.,Ltd", + [3]byte{236, 240, 14}: "AboCom", + [3]byte{236, 242, 54}: "NEOMONTANA ELECTRONICS", + [3]byte{236, 243, 91}: "Nokia Corporation", + [3]byte{236, 244, 187}: "Dell Inc.", + [3]byte{236, 247, 43}: "HD DIGITAL TECH CO., LTD.", + [3]byte{236, 250, 170}: "The IMS Company", + [3]byte{236, 252, 85}: "A. Eberle GmbH & Co. KG", + [3]byte{236, 254, 126}: "BlueRadios, Inc.", + [3]byte{240, 0, 127}: "Janz - Contadores de Energia, SA", + [3]byte{240, 2, 43}: "Chrontel", + [3]byte{240, 2, 72}: "SmarteBuilding", + [3]byte{240, 3, 140}: "AzureWave Technology Inc.", + [3]byte{240, 7, 134}: "Shandong Bittel Electronics Co., Ltd", + [3]byte{240, 8, 241}: "Samsung Electronics Co.,Ltd", + [3]byte{240, 13, 92}: "JinQianMao Technology Co.,Ltd.", + [3]byte{240, 19, 195}: "SHENZHEN FENDA TECHNOLOGY CO., LTD", + [3]byte{240, 21, 160}: "KyungDong One Co., Ltd.", + [3]byte{240, 21, 185}: "PlayFusion Limited", + [3]byte{240, 24, 43}: "LG Chem", + [3]byte{240, 27, 108}: "vivo Mobile Communication Co., Ltd.", + [3]byte{240, 28, 19}: "LG Electronics (Mobile Communications)", + [3]byte{240, 28, 45}: "Juniper Networks", + [3]byte{240, 29, 188}: "Microsoft Corporation", + [3]byte{240, 30, 52}: "ORICO Technologies Co., Ltd", + [3]byte{240, 31, 175}: "Dell Inc.", + [3]byte{240, 33, 157}: "Cal-Comp Electronics & Communications Company Ltd.", + [3]byte{240, 34, 78}: "Esan electronic co.", + [3]byte{240, 35, 41}: "SHOWA DENKI CO.,LTD.", + [3]byte{240, 36, 5}: "OPUS High Technology Corporation", + [3]byte{240, 36, 8}: "Talaris (Sweden) AB", + [3]byte{240, 36, 117}: "Apple, Inc.", + [3]byte{240, 37, 114}: "Cisco Systems, Inc", + [3]byte{240, 37, 183}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{240, 38, 36}: "WAFA TECHNOLOGIES CO., LTD.", + [3]byte{240, 38, 76}: "Dr. Sigrist AG", + [3]byte{240, 39, 45}: "Amazon Technologies Inc.", + [3]byte{240, 39, 69}: "F-Secure Corporation", + [3]byte{240, 39, 101}: "Murata Manufacturing Co., Ltd.", + [3]byte{240, 41, 41}: "Cisco Systems, Inc", + [3]byte{240, 42, 35}: "Creative Next Design", + [3]byte{240, 42, 97}: "Waldo Networks, Inc.", + [3]byte{240, 47, 167}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{240, 47, 216}: "Bi2-Vision", + [3]byte{240, 50, 26}: "Mita-Teknik A/S", + [3]byte{240, 52, 4}: "TCT mobile ltd", + [3]byte{240, 55, 161}: "Huike Electronics (SHENZHEN) CO., LTD.", + [3]byte{240, 58, 75}: "Bloombase, Inc.", + [3]byte{240, 58, 85}: "Omega Elektronik AS", + [3]byte{240, 61, 41}: "Actility", + [3]byte{240, 62, 144}: "Ruckus Wireless", + [3]byte{240, 62, 191}: "GOGORO TAIWAN LIMITED", + [3]byte{240, 63, 248}: "R L Drake", + [3]byte{240, 64, 123}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{240, 66, 28}: "Intel Corporate", + [3]byte{240, 67, 53}: "DVN(Shanghai)Ltd.", + [3]byte{240, 67, 71}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{240, 74, 43}: "PYRAMID Computer GmbH", + [3]byte{240, 75, 106}: "Scientific Production Association Siberian Arsenal, Ltd.", + [3]byte{240, 75, 242}: "JTECH Communications, Inc.", + [3]byte{240, 77, 162}: "Dell Inc.", + [3]byte{240, 79, 124}: "Private", + [3]byte{240, 88, 73}: "CareView Communications", + [3]byte{240, 90, 9}: "Samsung Electronics Co.,Ltd", + [3]byte{240, 91, 123}: "Samsung Electronics Co.,Ltd", + [3]byte{240, 92, 25}: "Aruba Networks", + [3]byte{240, 93, 137}: "Dycon Limited", + [3]byte{240, 93, 200}: "Duracell Powermat", + [3]byte{240, 95, 90}: "Getriebebau NORD GmbH and Co. KG", + [3]byte{240, 97, 48}: "Advantage Pharmacy Services, LLC", + [3]byte{240, 98, 13}: "Shenzhen Egreat Tech Corp.,Ltd", + [3]byte{240, 98, 129}: "ProCurve Networking by HP", + [3]byte{240, 101, 221}: "Primax Electronics Ltd.", + [3]byte{240, 104, 83}: "Integrated Corporation", + [3]byte{240, 107, 202}: "Samsung Electronics Co.,Ltd", + [3]byte{240, 110, 50}: "MICROTEL INNOVATION S.R.L.", + [3]byte{240, 114, 140}: "Samsung Electronics Co.,Ltd", + [3]byte{240, 115, 174}: "PEAK-System Technik", + [3]byte{240, 116, 228}: "Thundercomm Technology Co., Ltd", + [3]byte{240, 118, 28}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{240, 119, 101}: "Sourcefire, Inc", + [3]byte{240, 119, 208}: "Xcellen", + [3]byte{240, 120, 22}: "Cisco Systems, Inc", + [3]byte{240, 121, 89}: "ASUSTek COMPUTER INC.", + [3]byte{240, 121, 96}: "Apple, Inc.", + [3]byte{240, 123, 203}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{240, 125, 104}: "D-Link Corporation", + [3]byte{240, 127, 6}: "Cisco Systems, Inc", + [3]byte{240, 127, 12}: "Leopold Kostal GmbH &Co. KG", + [3]byte{240, 129, 175}: "IRZ AUTOMATION TECHNOLOGIES LTD", + [3]byte{240, 130, 97}: "Sagemcom Broadband SAS", + [3]byte{240, 132, 47}: "ADB Broadband Italia", + [3]byte{240, 132, 201}: "zte corporation", + [3]byte{240, 138, 40}: "JIANGSU HENGSION ELECTRONIC S and T CO.,LTD", + [3]byte{240, 139, 254}: "COSTEL.,CO.LTD", + [3]byte{240, 140, 251}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{240, 142, 219}: "VeloCloud Networks", + [3]byte{240, 146, 28}: "Hewlett Packard", + [3]byte{240, 147, 58}: "NxtConect", + [3]byte{240, 147, 197}: "Garland Technology", + [3]byte{240, 151, 229}: "TAMIO, INC", + [3]byte{240, 152, 56}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{240, 153, 191}: "Apple, Inc.", + [3]byte{240, 154, 81}: "Shanghai Viroyal Electronic Technology Company Limited", + [3]byte{240, 156, 187}: "RaonThink Inc.", + [3]byte{240, 156, 233}: "Aerohive Networks Inc.", + [3]byte{240, 158, 99}: "Cisco Systems, Inc", + [3]byte{240, 159, 194}: "Ubiquiti Networks Inc.", + [3]byte{240, 162, 37}: "Private", + [3]byte{240, 167, 100}: "GST Co., Ltd.", + [3]byte{240, 171, 84}: "MITSUMI ELECTRIC CO.,LTD.", + [3]byte{240, 172, 164}: "HBC-radiomatic", + [3]byte{240, 172, 215}: "IEEE Registration Authority", + [3]byte{240, 173, 78}: "Globalscale Technologies, Inc.", + [3]byte{240, 174, 81}: "Xi3 Corp", + [3]byte{240, 176, 82}: "Ruckus Wireless", + [3]byte{240, 176, 231}: "Apple, Inc.", + [3]byte{240, 178, 229}: "Cisco Systems, Inc", + [3]byte{240, 180, 41}: "Xiaomi Communications Co Ltd", + [3]byte{240, 180, 121}: "Apple, Inc.", + [3]byte{240, 182, 235}: "Poslab Technology Co., Ltd.", + [3]byte{240, 188, 200}: "MaxID (Pty) Ltd", + [3]byte{240, 189, 241}: "Sipod Inc.", + [3]byte{240, 191, 151}: "Sony Corporation", + [3]byte{240, 193, 241}: "Apple, Inc.", + [3]byte{240, 194, 76}: "Zhejiang FeiYue Digital Technology Co., Ltd", + [3]byte{240, 194, 124}: "Mianyang Netop Telecom Equipment Co.,Ltd.", + [3]byte{240, 199, 127}: "Texas Instruments", + [3]byte{240, 200, 80}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{240, 200, 140}: "LeddarTech Inc.", + [3]byte{240, 203, 161}: "Apple, Inc.", + [3]byte{240, 209, 79}: "LINEAR LLC", + [3]byte{240, 209, 169}: "Apple, Inc.", + [3]byte{240, 209, 184}: "LEDVANCE", + [3]byte{240, 210, 241}: "Amazon Technologies Inc.", + [3]byte{240, 211, 167}: "CobaltRay Co., Ltd", + [3]byte{240, 211, 231}: "Sensometrix SA", + [3]byte{240, 213, 191}: "Intel Corporate", + [3]byte{240, 214, 87}: "ECHOSENS", + [3]byte{240, 215, 103}: "Axema Passagekontroll AB", + [3]byte{240, 215, 170}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{240, 217, 178}: "EXO S.A.", + [3]byte{240, 218, 124}: "RLH INDUSTRIES,INC.", + [3]byte{240, 219, 48}: "Yottabyte", + [3]byte{240, 219, 226}: "Apple, Inc.", + [3]byte{240, 219, 248}: "Apple, Inc.", + [3]byte{240, 220, 226}: "Apple, Inc.", + [3]byte{240, 222, 113}: "Shanghai EDO Technologies Co.,Ltd.", + [3]byte{240, 222, 185}: "ShangHai Y&Y Electronics Co., Ltd", + [3]byte{240, 222, 241}: "Wistron Infocomm (Zhongshan) Corporation", + [3]byte{240, 229, 195}: "Drägerwerk AG & Co. KG aA", + [3]byte{240, 231, 126}: "Samsung Electronics Co.,Ltd", + [3]byte{240, 235, 208}: "Shanghai Feixun Communication Co.,Ltd.", + [3]byte{240, 236, 57}: "Essec", + [3]byte{240, 237, 30}: "Bilkon Bilgisayar Kontrollu Cih. Im.Ltd.", + [3]byte{240, 238, 16}: "Samsung Electronics Co.,Ltd", + [3]byte{240, 238, 88}: "PACE Telematics GmbH", + [3]byte{240, 238, 187}: "VIPAR GmbH", + [3]byte{240, 240, 2}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{240, 242, 73}: "Hitron Technologies. Inc", + [3]byte{240, 242, 96}: "Mobitec AB", + [3]byte{240, 243, 54}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{240, 245, 174}: "Adaptrum Inc.", + [3]byte{240, 246, 28}: "Apple, Inc.", + [3]byte{240, 246, 68}: "Whitesky Science & Technology Co.,Ltd.", + [3]byte{240, 246, 105}: "Motion Analysis Corporation", + [3]byte{240, 247, 85}: "Cisco Systems, Inc", + [3]byte{240, 247, 179}: "Phorm", + [3]byte{240, 248, 66}: "KEEBOX, Inc.", + [3]byte{240, 249, 247}: "IES GmbH & Co. KG", + [3]byte{240, 253, 160}: "Acurix Networks Pty Ltd", + [3]byte{240, 254, 107}: "Shanghai High-Flying Electronics Technology Co., Ltd", + [3]byte{244, 3, 4}: "Google, Inc.", + [3]byte{244, 3, 33}: "BeNeXt B.V.", + [3]byte{244, 3, 47}: "Reduxio Systems", + [3]byte{244, 3, 67}: "Hewlett Packard Enterprise", + [3]byte{244, 4, 76}: "ValenceTech Limited", + [3]byte{244, 6, 105}: "Intel Corporate", + [3]byte{244, 6, 141}: "devolo AG", + [3]byte{244, 6, 165}: "Hangzhou Bianfeng Networking Technology Co., Ltd.", + [3]byte{244, 9, 216}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{244, 10, 74}: "INDUSNET Communication Technology Co.,LTD", + [3]byte{244, 11, 147}: "BlackBerry RTS", + [3]byte{244, 14, 17}: "IEEE Registration Authority", + [3]byte{244, 14, 34}: "Samsung Electronics Co.,Ltd", + [3]byte{244, 15, 27}: "Cisco Systems, Inc", + [3]byte{244, 15, 36}: "Apple, Inc.", + [3]byte{244, 15, 155}: "WAVELINK", + [3]byte{244, 21, 53}: "SPON Communication Technology Co.,Ltd", + [3]byte{244, 21, 99}: "F5 Networks, Inc.", + [3]byte{244, 21, 253}: "Shanghai Pateo Electronic Equipment Manufacturing Co., Ltd.", + [3]byte{244, 27, 161}: "Apple, Inc.", + [3]byte{244, 30, 38}: "Simon-Kaloi Engineering", + [3]byte{244, 31, 11}: "YAMABISHI Corporation", + [3]byte{244, 31, 136}: "zte corporation", + [3]byte{244, 31, 194}: "Cisco Systems, Inc", + [3]byte{244, 32, 18}: "Cuciniale GmbH", + [3]byte{244, 40, 51}: "MMPC Inc.", + [3]byte{244, 40, 83}: "Zioncom Electronics (Shenzhen) Ltd.", + [3]byte{244, 40, 150}: "SPECTO PAINEIS ELETRONICOS LTDA", + [3]byte{244, 41, 129}: "vivo Mobile Communication Co., Ltd.", + [3]byte{244, 43, 72}: "Ubiqam", + [3]byte{244, 44, 86}: "SENOR TECH CO LTD", + [3]byte{244, 49, 195}: "Apple, Inc.", + [3]byte{244, 54, 225}: "Abilis Systems SARL", + [3]byte{244, 55, 183}: "Apple, Inc.", + [3]byte{244, 56, 20}: "Shanghai Howell Electronic Co.,Ltd", + [3]byte{244, 61, 128}: "FAG Industrial Services GmbH", + [3]byte{244, 62, 97}: "SHENZHEN GONGJIN ELECTRONICS CO.,LT", + [3]byte{244, 62, 157}: "Benu Networks, Inc.", + [3]byte{244, 66, 39}: "S & S Research Inc.", + [3]byte{244, 66, 143}: "Samsung Electronics Co.,Ltd", + [3]byte{244, 68, 80}: "BND Co., Ltd.", + [3]byte{244, 69, 237}: "Portable Innovation Technology Ltd.", + [3]byte{244, 71, 19}: "Leading Public Performance Co., Ltd.", + [3]byte{244, 71, 42}: "Nanjing Rousing Sci. and Tech. Industrial Co., Ltd", + [3]byte{244, 72, 72}: "Amscreen Group Ltd", + [3]byte{244, 75, 42}: "Cisco SPVTG", + [3]byte{244, 76, 127}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 77, 23}: "GOLDCARD HIGH-TECH CO.,LTD.", + [3]byte{244, 77, 48}: "Elitegroup Computer Systems Co.,Ltd.", + [3]byte{244, 78, 5}: "Cisco Systems, Inc", + [3]byte{244, 78, 253}: "Actions Semiconductor Co.,Ltd.(Cayman Islands)", + [3]byte{244, 80, 235}: "Telechips Inc", + [3]byte{244, 82, 20}: "Mellanox Technologies, Inc.", + [3]byte{244, 84, 51}: "Rockwell Automation", + [3]byte{244, 85, 149}: "HENGBAO Corporation LTD.", + [3]byte{244, 85, 156}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 85, 224}: "Niceway CNC Technology Co.,Ltd.Hunan Province", + [3]byte{244, 87, 62}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{244, 88, 66}: "Boxx TV Ltd", + [3]byte{244, 91, 115}: "Wanjiaan Interconnected Technology Co., Ltd", + [3]byte{244, 92, 137}: "Apple, Inc.", + [3]byte{244, 94, 171}: "Texas Instruments", + [3]byte{244, 95, 105}: "Matsufu Electronics distribution Company", + [3]byte{244, 95, 212}: "Cisco SPVTG", + [3]byte{244, 95, 247}: "DQ Technology Inc.", + [3]byte{244, 96, 13}: "Panoptic Technology, Inc", + [3]byte{244, 98, 208}: "Not for Radio, LLC", + [3]byte{244, 99, 73}: "Diffon Corporation", + [3]byte{244, 100, 93}: "Toshiba", + [3]byte{244, 103, 45}: "ShenZhen Topstar Technology Company", + [3]byte{244, 106, 146}: "SHENZHEN FAST TECHNOLOGIES CO.,LTD", + [3]byte{244, 106, 188}: "Adonit Corp. Ltd.", + [3]byte{244, 109, 4}: "ASUSTek COMPUTER INC.", + [3]byte{244, 109, 226}: "zte corporation", + [3]byte{244, 112, 171}: "vivo Mobile Communication Co., Ltd.", + [3]byte{244, 115, 202}: "Conversion Sound Inc.", + [3]byte{244, 118, 38}: "Viltechmeda UAB ", + [3]byte{244, 122, 78}: "Woojeon&Handan", + [3]byte{244, 122, 204}: "SolidFire, Inc.", + [3]byte{244, 123, 94}: "Samsung Electronics Co.,Ltd", + [3]byte{244, 127, 53}: "Cisco Systems, Inc", + [3]byte{244, 129, 57}: "CANON INC.", + [3]byte{244, 131, 205}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{244, 131, 225}: "Shanghai Clouder Semiconductor Co.,Ltd", + [3]byte{244, 133, 198}: "FDT Technologies", + [3]byte{244, 135, 113}: "Infoblox", + [3]byte{244, 139, 50}: "Xiaomi Communications Co Ltd", + [3]byte{244, 140, 80}: "Intel Corporate", + [3]byte{244, 142, 9}: "Nokia Corporation", + [3]byte{244, 142, 56}: "Dell Inc.", + [3]byte{244, 142, 146}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 144, 202}: "Tensorcom", + [3]byte{244, 144, 234}: "Deciso B.V.", + [3]byte{244, 145, 30}: "ZHUHAI EWPE INFORMATION TECHNOLOGY INC", + [3]byte{244, 148, 97}: "NexGen Storage", + [3]byte{244, 148, 102}: "CountMax, ltd", + [3]byte{244, 150, 52}: "Intel Corporate", + [3]byte{244, 150, 81}: "NAKAYO Inc", + [3]byte{244, 153, 172}: "WEBER Schraubautomaten GmbH", + [3]byte{244, 158, 239}: "Taicang T&W Electronics", + [3]byte{244, 159, 84}: "Samsung Electronics Co.,Ltd", + [3]byte{244, 159, 243}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 162, 148}: "EAGLE WORLD DEVELOPMENT CO., LIMITED", + [3]byte{244, 165, 42}: "Hawa Technologies Inc", + [3]byte{244, 167, 57}: "Juniper Networks", + [3]byte{244, 172, 193}: "Cisco Systems, Inc", + [3]byte{244, 177, 100}: "Lightning Telecommunications Technology Co. Ltd", + [3]byte{244, 179, 129}: "WindowMaster A/S", + [3]byte{244, 181, 47}: "Juniper Networks", + [3]byte{244, 181, 73}: "Xiamen Yeastar Information Technology Co., Ltd.", + [3]byte{244, 182, 229}: "TerraSem Co.,Ltd", + [3]byte{244, 183, 42}: "TIME INTERCONNECT LTD", + [3]byte{244, 183, 226}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{244, 184, 94}: "Texas Instruments", + [3]byte{244, 184, 167}: "zte corporation", + [3]byte{244, 189, 124}: "Chengdu jinshi communication Co., LTD", + [3]byte{244, 196, 71}: "Coagent International Enterprise Limited", + [3]byte{244, 196, 214}: "Shenzhen Xinfa Electronic Co.,ltd", + [3]byte{244, 198, 19}: "Alcatel-Lucent Shanghai Bell Co., Ltd", + [3]byte{244, 198, 215}: "blackned GmbH", + [3]byte{244, 199, 20}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 199, 149}: "WEY Elektronik AG", + [3]byte{244, 202, 36}: "FreeBit Co., Ltd.", + [3]byte{244, 202, 229}: "FREEBOX SAS", + [3]byte{244, 203, 82}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 204, 85}: "Juniper Networks", + [3]byte{244, 205, 144}: "Vispiron Rotec GmbH", + [3]byte{244, 206, 70}: "Hewlett Packard", + [3]byte{244, 207, 226}: "Cisco Systems, Inc", + [3]byte{244, 208, 50}: "Yunnan Ideal Information&Technology.,Ltd", + [3]byte{244, 210, 97}: "SEMOCON Co., Ltd", + [3]byte{244, 217, 251}: "Samsung Electronics Co.,Ltd", + [3]byte{244, 220, 65}: "YOUNGZONE CULTURE (SHANGHAI) CORP", + [3]byte{244, 220, 77}: "Beijing CCD Digital Technology Co., Ltd", + [3]byte{244, 220, 218}: "Zhuhai Jiahe Communication Technology Co., limited", + [3]byte{244, 220, 249}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 221, 158}: "GoPro", + [3]byte{244, 222, 12}: "ESPOD Ltd.", + [3]byte{244, 225, 66}: "Delta Elektronika BV", + [3]byte{244, 227, 251}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{244, 228, 173}: "zte corporation", + [3]byte{244, 230, 215}: "Solar Power Technologies, Inc.", + [3]byte{244, 233, 38}: "Tianjin Zanpu Technology Inc.", + [3]byte{244, 233, 212}: "QLogic Corporation", + [3]byte{244, 234, 103}: "Cisco Systems, Inc", + [3]byte{244, 235, 56}: "Sagemcom Broadband SAS", + [3]byte{244, 236, 56}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{244, 237, 95}: "SHENZHEN KTC TECHNOLOGY GROUP ", + [3]byte{244, 238, 20}: "SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.", + [3]byte{244, 239, 158}: "SGSG SCIENCE & TECHNOLOGY CO. LTD", + [3]byte{244, 241, 90}: "Apple, Inc.", + [3]byte{244, 241, 225}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{244, 242, 109}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{244, 245, 36}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{244, 245, 165}: "Nokia Corporation", + [3]byte{244, 245, 216}: "Google, Inc.", + [3]byte{244, 245, 232}: "Google, Inc.", + [3]byte{244, 246, 70}: "Dediprog Technology Co. Ltd.", + [3]byte{244, 249, 81}: "Apple, Inc.", + [3]byte{244, 252, 50}: "Texas Instruments", + [3]byte{244, 253, 43}: "ZOYI Company", + [3]byte{248, 1, 19}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 2, 120}: "IEEE Registration Authority", + [3]byte{248, 3, 50}: "Khomp", + [3]byte{248, 3, 119}: "Apple, Inc.", + [3]byte{248, 4, 46}: "SAMSUNG ELECTRO-MECHANICS(THAILAND)", + [3]byte{248, 5, 28}: "DRS Imaging and Targeting Solutions", + [3]byte{248, 11, 190}: "ARRIS Group, Inc.", + [3]byte{248, 11, 203}: "Cisco Systems, Inc", + [3]byte{248, 11, 208}: "Datang Telecom communication terminal (Tianjin) Co., Ltd.", + [3]byte{248, 12, 243}: "LG Electronics (Mobile Communications)", + [3]byte{248, 13, 67}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{248, 13, 96}: "CANON INC.", + [3]byte{248, 13, 234}: "ZyCast Technology Inc.", + [3]byte{248, 15, 65}: "Wistron Infocomm (Zhongshan) Corporation", + [3]byte{248, 15, 132}: "Natural Security SAS", + [3]byte{248, 16, 55}: "Atopia Systems, LP", + [3]byte{248, 21, 71}: "Avaya Inc", + [3]byte{248, 22, 84}: "Intel Corporate", + [3]byte{248, 24, 151}: "2Wire Inc", + [3]byte{248, 26, 103}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{248, 28, 229}: "Telefonbau Behnke GmbH", + [3]byte{248, 29, 120}: "IEEE Registration Authority", + [3]byte{248, 29, 147}: "Longdhua(Beijing) Controls Technology Co.,Ltd", + [3]byte{248, 30, 223}: "Apple, Inc.", + [3]byte{248, 34, 133}: "Cypress Technology CO., LTD.", + [3]byte{248, 35, 178}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 36, 65}: "Yeelink", + [3]byte{248, 39, 147}: "Apple, Inc.", + [3]byte{248, 43, 200}: "Jiangsu Switter Co., Ltd", + [3]byte{248, 44, 24}: "2Wire Inc", + [3]byte{248, 46, 219}: "RTW GmbH & Co. KG", + [3]byte{248, 47, 8}: "Molex", + [3]byte{248, 47, 91}: "eGauge Systems LLC", + [3]byte{248, 47, 168}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{248, 48, 148}: "Alcatel-Lucent Telecom Limited", + [3]byte{248, 49, 62}: "endeavour GmbH", + [3]byte{248, 50, 228}: "ASUSTek COMPUTER INC.", + [3]byte{248, 51, 118}: "Good Mind Innovation Co., Ltd.", + [3]byte{248, 53, 83}: "Magenta Research Ltd.", + [3]byte{248, 53, 221}: "Gemtek Technology Co., Ltd.", + [3]byte{248, 61, 78}: "Softlink Automation System Co., Ltd", + [3]byte{248, 61, 255}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 63, 81}: "Samsung Electronics Co.,Ltd", + [3]byte{248, 66, 251}: "Yasuda Joho Co.,ltd.", + [3]byte{248, 69, 173}: "Konka Group Co., Ltd.", + [3]byte{248, 70, 28}: "Sony Interactive Entertainment Inc.", + [3]byte{248, 70, 45}: "SYNTEC Incorporation", + [3]byte{248, 71, 45}: "X2gen Digital Corp. Ltd", + [3]byte{248, 72, 151}: "Hitachi, Ltd.", + [3]byte{248, 74, 115}: "EUMTECH CO., LTD", + [3]byte{248, 74, 127}: "Innometriks Inc", + [3]byte{248, 74, 191}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 79, 87}: "Cisco Systems, Inc", + [3]byte{248, 80, 99}: "Verathon", + [3]byte{248, 81, 109}: "Denwa Technology Corp.", + [3]byte{248, 82, 223}: "VNL Europe AB", + [3]byte{248, 84, 175}: "ECI Telecom Ltd.", + [3]byte{248, 87, 46}: "Core Brands, LLC", + [3]byte{248, 89, 113}: "Intel Corporate", + [3]byte{248, 90, 0}: "Sanford LP", + [3]byte{248, 91, 156}: "SB SYSTEMS Co.,Ltd", + [3]byte{248, 91, 201}: "M-Cube Spa", + [3]byte{248, 92, 69}: "IC Nexus Co. Ltd.", + [3]byte{248, 92, 77}: "NOKIA", + [3]byte{248, 95, 42}: "Nokia Corporation", + [3]byte{248, 98, 20}: "Apple, Inc.", + [3]byte{248, 98, 170}: "xn systems", + [3]byte{248, 99, 63}: "Intel Corporate", + [3]byte{248, 102, 1}: "Suzhou Chi-tek information technology Co., Ltd", + [3]byte{248, 102, 209}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{248, 102, 242}: "Cisco Systems, Inc", + [3]byte{248, 105, 113}: "Seibu Electric Co.,", + [3]byte{248, 110, 207}: "Arcx Inc", + [3]byte{248, 113, 254}: "The Goldman Sachs Group, Inc.", + [3]byte{248, 114, 234}: "Cisco Systems, Inc", + [3]byte{248, 115, 148}: "NETGEAR", + [3]byte{248, 115, 162}: "Avaya Inc", + [3]byte{248, 117, 136}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 118, 155}: "Neopis Co., Ltd.", + [3]byte{248, 119, 184}: "Samsung Electronics Co.,Ltd", + [3]byte{248, 122, 239}: "Rosonix Technology, Inc.", + [3]byte{248, 123, 98}: "FASTWEL INTERNATIONAL CO., LTD. Taiwan Branch", + [3]byte{248, 123, 122}: "ARRIS Group, Inc.", + [3]byte{248, 123, 140}: "Amped Wireless", + [3]byte{248, 128, 150}: "Elsys Equipamentos Eletrônicos Ltda", + [3]byte{248, 129, 26}: "OVERKIZ", + [3]byte{248, 132, 121}: "Yaojin Technology(Shenzhen)Co.,Ltd", + [3]byte{248, 132, 242}: "Samsung Electronics Co.,Ltd", + [3]byte{248, 140, 28}: "KAISHUN ELECTRONIC TECHNOLOGY CO., LTD. BEIJING", + [3]byte{248, 141, 239}: "Tenebraex", + [3]byte{248, 142, 133}: "Comtrend Corporation", + [3]byte{248, 143, 202}: "Google, Inc.", + [3]byte{248, 145, 42}: "GLP German Light Products GmbH", + [3]byte{248, 147, 243}: "VOLANS", + [3]byte{248, 148, 194}: "Intel Corporate", + [3]byte{248, 149, 80}: "Proton Products Chengdu Ltd", + [3]byte{248, 149, 199}: "LG Electronics (Mobile Communications)", + [3]byte{248, 151, 207}: "DAESHIN-INFORMATION TECHNOLOGY CO., LTD.", + [3]byte{248, 152, 58}: "Leeman International (HongKong) Limited", + [3]byte{248, 152, 185}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 153, 85}: "Fortress Technology Inc", + [3]byte{248, 157, 13}: "Control Technology Inc.", + [3]byte{248, 159, 184}: "YAZAKI Energy System Corporation", + [3]byte{248, 160, 61}: "Dinstar Technologies Co., Ltd.", + [3]byte{248, 160, 151}: "ARRIS Group, Inc.", + [3]byte{248, 161, 136}: "LED Roadway Lighting", + [3]byte{248, 162, 180}: "RHEWA-WAAGENFABRIK August Freudewald GmbH &Co. KG", + [3]byte{248, 163, 79}: "zte corporation", + [3]byte{248, 164, 95}: "Xiaomi Communications Co Ltd", + [3]byte{248, 165, 197}: "Cisco Systems, Inc", + [3]byte{248, 169, 99}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{248, 169, 208}: "LG Electronics (Mobile Communications)", + [3]byte{248, 169, 222}: "PUISSANCE PLUS", + [3]byte{248, 170, 138}: "Axview Technology (Shenzhen) Co.,Ltd", + [3]byte{248, 171, 5}: "Sagemcom Broadband SAS", + [3]byte{248, 172, 109}: "Deltenna Ltd", + [3]byte{248, 177, 86}: "Dell Inc.", + [3]byte{248, 178, 243}: "GUANGZHOU BOSMA TECHNOLOGY CO.,LTD", + [3]byte{248, 181, 153}: "Guangzhou CHNAVS Digital Technology Co.,Ltd", + [3]byte{248, 187, 191}: "eero inc.", + [3]byte{248, 188, 18}: "Dell Inc.", + [3]byte{248, 188, 65}: "Rosslare Enterprises Limited", + [3]byte{248, 190, 13}: "A2UICT Co.,Ltd.", + [3]byte{248, 191, 9}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 192, 1}: "Juniper Networks", + [3]byte{248, 192, 145}: "Highgates Technology", + [3]byte{248, 194, 136}: "Cisco Systems, Inc", + [3]byte{248, 195, 114}: "TSUZUKI DENKI", + [3]byte{248, 195, 151}: "NZXT Corp. Ltd.", + [3]byte{248, 198, 120}: "Carefusion", + [3]byte{248, 201, 108}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{248, 202, 184}: "Dell Inc.", + [3]byte{248, 207, 197}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{248, 208, 39}: "Seiko Epson Corporation", + [3]byte{248, 208, 172}: "Sony Interactive Entertainment Inc.", + [3]byte{248, 208, 189}: "Samsung Electronics Co.,Ltd", + [3]byte{248, 209, 17}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{248, 211, 169}: "AXAN Networks", + [3]byte{248, 212, 98}: "Pumatronix Equipamentos Eletronicos Ltda.", + [3]byte{248, 215, 86}: "Simm Tronic Limited ", + [3]byte{248, 215, 191}: "REV Ritter GmbH", + [3]byte{248, 218, 12}: "Hon Hai Precision Ind. Co.,Ltd.", + [3]byte{248, 218, 223}: "EcoTech, Inc.", + [3]byte{248, 218, 226}: "Beta LaserMike", + [3]byte{248, 218, 244}: "Taishan Online Technology Co., Ltd.", + [3]byte{248, 219, 76}: "PNY Technologies, INC.", + [3]byte{248, 219, 127}: "HTC Corporation", + [3]byte{248, 219, 136}: "Dell Inc.", + [3]byte{248, 220, 122}: "Variscite LTD", + [3]byte{248, 223, 168}: "zte corporation", + [3]byte{248, 224, 121}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{248, 228, 251}: "Actiontec Electronics, Inc", + [3]byte{248, 230, 26}: "Samsung Electronics Co.,Ltd", + [3]byte{248, 231, 30}: "Ruckus Wireless", + [3]byte{248, 231, 181}: "µTech Tecnologia LTDA", + [3]byte{248, 232, 17}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{248, 233, 3}: "D-Link International", + [3]byte{248, 233, 104}: "Egker Kft.", + [3]byte{248, 234, 10}: "Dipl.-Math. Michael Rauch", + [3]byte{248, 237, 165}: "ARRIS Group, Inc.", + [3]byte{248, 240, 5}: "Newport Media Inc.", + [3]byte{248, 240, 20}: "RackWare Inc.", + [3]byte{248, 240, 130}: "NAG LLC", + [3]byte{248, 241, 182}: "Motorola Mobility LLC, a Lenovo Company", + [3]byte{248, 242, 90}: "G-Lab GmbH", + [3]byte{248, 244, 100}: "Rawe Electonic GmbH", + [3]byte{248, 247, 211}: "International Communications Corporation", + [3]byte{248, 247, 255}: "SYN-TECH SYSTEMS INC", + [3]byte{248, 251, 47}: "Santur Corporation", + [3]byte{248, 254, 92}: "Reciprocal Labs Corp", + [3]byte{248, 254, 168}: "Technico Japan Corporation", + [3]byte{248, 255, 11}: "Electronic Technology Inc.", + [3]byte{248, 255, 95}: "Shenzhen Communication Technology Co.,Ltd", + [3]byte{252, 0, 18}: "Toshiba Samsung Storage Technolgoy Korea Corporation ", + [3]byte{252, 1, 158}: "VIEVU", + [3]byte{252, 1, 205}: "FUNDACION TEKNIKER", + [3]byte{252, 6, 71}: "Cortland Research, LLC", + [3]byte{252, 7, 160}: "LRE Medical GmbH", + [3]byte{252, 8, 74}: "FUJITSU LIMITED", + [3]byte{252, 8, 119}: "Prentke Romich Company", + [3]byte{252, 9, 216}: "ACTEON Group", + [3]byte{252, 9, 246}: "GUANGDONG TONZE ELECTRIC CO.,LTD", + [3]byte{252, 10, 129}: "Extreme Networks", + [3]byte{252, 15, 75}: "Texas Instruments", + [3]byte{252, 15, 230}: "Sony Interactive Entertainment Inc.", + [3]byte{252, 16, 189}: "Control Sistematizado S.A.", + [3]byte{252, 16, 198}: "Taicang T&W Electronics", + [3]byte{252, 17, 134}: "Logic3 plc", + [3]byte{252, 19, 73}: "Global Apps Corp.", + [3]byte{252, 21, 180}: "Hewlett Packard", + [3]byte{252, 22, 7}: "Taian Technology(Wuxi) Co.,Ltd.", + [3]byte{252, 23, 148}: "InterCreative Co., Ltd", + [3]byte{252, 25, 16}: "Samsung Electronics Co.,Ltd", + [3]byte{252, 25, 208}: "Cloud Vision Networks Technology Co.,Ltd.", + [3]byte{252, 26, 17}: "vivo Mobile Communication Co., Ltd.", + [3]byte{252, 27, 255}: "V-ZUG AG", + [3]byte{252, 29, 89}: "I Smart Cities HK Ltd", + [3]byte{252, 29, 132}: "Autobase", + [3]byte{252, 30, 22}: "IPEVO corp", + [3]byte{252, 31, 25}: "SAMSUNG ELECTRO MECHANICS CO., LTD.", + [3]byte{252, 31, 192}: "EURECAM", + [3]byte{252, 34, 156}: "Han Kyung I Net Co.,Ltd.", + [3]byte{252, 35, 37}: "EosTek (Shenzhen) Co., Ltd.", + [3]byte{252, 37, 63}: "Apple, Inc.", + [3]byte{252, 39, 162}: "TRANS ELECTRIC CO., LTD.", + [3]byte{252, 42, 84}: "Connected Data, Inc.", + [3]byte{252, 45, 94}: "zte corporation", + [3]byte{252, 46, 45}: "Lorom Industrial Co.LTD.", + [3]byte{252, 47, 64}: "Calxeda, Inc.", + [3]byte{252, 47, 170}: "Nokia", + [3]byte{252, 47, 239}: "UTT Technologies Co., Ltd.", + [3]byte{252, 50, 136}: "CELOT Wireless Co., Ltd", + [3]byte{252, 51, 95}: "Polyera", + [3]byte{252, 53, 152}: "Favite Inc.", + [3]byte{252, 53, 230}: "Visteon corp", + [3]byte{252, 55, 43}: "SICHUAN TIANYI COMHEART TELECOMCO.,LTD", + [3]byte{252, 60, 233}: "Tsingtong Technologies Co, Ltd.", + [3]byte{252, 61, 147}: "LONGCHEER TELECOMMUNICATION LIMITED", + [3]byte{252, 63, 124}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{252, 63, 171}: "Henan Lanxin Technology Co., Ltd", + [3]byte{252, 63, 219}: "Hewlett Packard", + [3]byte{252, 66, 3}: "Samsung Electronics Co.,Ltd", + [3]byte{252, 68, 99}: "Universal Audio, Inc", + [3]byte{252, 68, 153}: "Swarco LEA d.o.o.", + [3]byte{252, 69, 95}: "JIANGXI SHANSHUI OPTOELECTRONIC TECHNOLOGY CO.,LTD", + [3]byte{252, 69, 150}: "COMPAL INFORMATION (KUNSHAN) CO., LTD.", + [3]byte{252, 72, 239}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{252, 74, 233}: "Castlenet Technology Inc.", + [3]byte{252, 75, 28}: "INTERSENSOR S.R.L.", + [3]byte{252, 75, 188}: "Sunplus Technology Co., Ltd.", + [3]byte{252, 77, 212}: "Universal Global Scientific Industrial Co., Ltd.", + [3]byte{252, 80, 144}: "SIMEX Sp. z o.o.", + [3]byte{252, 81, 164}: "ARRIS Group, Inc.", + [3]byte{252, 82, 141}: "Technicolor CH USA Inc.", + [3]byte{252, 82, 206}: "Control iD", + [3]byte{252, 83, 158}: "Shanghai Wind Technologies Co.,Ltd", + [3]byte{252, 85, 220}: "Baltic Latvian Universal Electronics LLC", + [3]byte{252, 88, 250}: "Shen Zhen Shi Xin Zhong Xin Technology Co.,Ltd.", + [3]byte{252, 91, 36}: "Weibel Scientific A/S", + [3]byte{252, 91, 38}: "MikroBits", + [3]byte{252, 91, 57}: "Cisco Systems, Inc", + [3]byte{252, 96, 24}: "Zhejiang Kangtai Electric Co., Ltd.", + [3]byte{252, 97, 152}: "NEC Personal Products, Ltd", + [3]byte{252, 98, 110}: "Beijing MDC Telecom", + [3]byte{252, 98, 185}: "ALPS ELECTRIC CO.,LTD.", + [3]byte{252, 100, 186}: "Xiaomi Communications Co Ltd", + [3]byte{252, 104, 62}: "Directed Perception, Inc", + [3]byte{252, 108, 49}: "LXinstruments GmbH", + [3]byte{252, 109, 192}: "BME CORPORATION", + [3]byte{252, 111, 183}: "ARRIS Group, Inc.", + [3]byte{252, 117, 22}: "D-Link International", + [3]byte{252, 117, 230}: "Handreamnet", + [3]byte{252, 121, 11}: "Hitachi High Technologies America, Inc.", + [3]byte{252, 124, 231}: "FCI USA LLC", + [3]byte{252, 131, 41}: "Trei technics", + [3]byte{252, 131, 153}: "Avaya Inc", + [3]byte{252, 131, 198}: "N-Radio Technologies Co., Ltd.", + [3]byte{252, 139, 151}: "Shenzhen Gongjin Electronics Co.,Ltd", + [3]byte{252, 142, 126}: "ARRIS Group, Inc.", + [3]byte{252, 143, 144}: "Samsung Electronics Co.,Ltd", + [3]byte{252, 143, 196}: "Intelligent Technology Inc.", + [3]byte{252, 145, 20}: "Technicolor CH USA Inc.", + [3]byte{252, 146, 59}: "Nokia Corporation", + [3]byte{252, 148, 108}: "UBIVELOX", + [3]byte{252, 148, 227}: "Technicolor CH USA Inc.", + [3]byte{252, 153, 71}: "Cisco Systems, Inc", + [3]byte{252, 154, 250}: "Motus Global Inc.", + [3]byte{252, 159, 174}: "Fidus Systems Inc", + [3]byte{252, 159, 225}: "CONWIN.Tech. Ltd", + [3]byte{252, 161, 62}: "Samsung Electronics Co.,Ltd", + [3]byte{252, 162, 42}: "PT. Callysta Multi Engineering", + [3]byte{252, 163, 134}: "SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD", + [3]byte{252, 168, 65}: "Avaya Inc", + [3]byte{252, 168, 154}: "Sunitec Enterprise Co.,Ltd", + [3]byte{252, 169, 176}: "MIARTECH (SHANGHAI),INC.", + [3]byte{252, 170, 20}: "GIGA-BYTE TECHNOLOGY CO.,LTD.", + [3]byte{252, 173, 15}: "QTS NETWORKS", + [3]byte{252, 175, 106}: "Qulsar Inc", + [3]byte{252, 175, 172}: "Socionext Inc.", + [3]byte{252, 176, 196}: "Shanghai DareGlobal Technologies Co.,Ltd", + [3]byte{252, 180, 230}: "ASKEY COMPUTER CORP", + [3]byte{252, 181, 138}: "Wapice Ltd.", + [3]byte{252, 182, 152}: "Cambridge Industries(Group) Co.,Ltd.", + [3]byte{252, 187, 161}: "Shenzhen Minicreate Technology Co.,Ltd", + [3]byte{252, 188, 156}: "Vimar Spa", + [3]byte{252, 194, 51}: "Private", + [3]byte{252, 194, 61}: "Atmel Corporation", + [3]byte{252, 194, 222}: "Murata Manufacturing Co., Ltd.", + [3]byte{252, 199, 52}: "Samsung Electronics Co.,Ltd", + [3]byte{252, 200, 151}: "zte corporation", + [3]byte{252, 202, 196}: "LifeHealth, LLC", + [3]byte{252, 204, 228}: "Ascon Ltd.", + [3]byte{252, 207, 67}: "HUIZHOU CITY HUIYANG DISTRICT MEISIQI INDUSTRY DEVELOPMENT CO,.LTD", + [3]byte{252, 207, 98}: "IBM Corp", + [3]byte{252, 212, 242}: "The Coca Cola Company", + [3]byte{252, 212, 246}: "Messana Air.Ray Conditioning s.r.l.", + [3]byte{252, 213, 217}: "Shenzhen SDMC Technology Co., Ltd.", + [3]byte{252, 214, 189}: "Robert Bosch GmbH", + [3]byte{252, 215, 51}: "TP-LINK TECHNOLOGIES CO.,LTD.", + [3]byte{252, 216, 23}: "Beijing Hesun Technologies Co.Ltd.", + [3]byte{252, 216, 72}: "Apple, Inc.", + [3]byte{252, 219, 150}: "ENERVALLEY CO., LTD", + [3]byte{252, 219, 179}: "Murata Manufacturing Co., Ltd.", + [3]byte{252, 220, 74}: "G-Wearables Corp.", + [3]byte{252, 221, 85}: "Shenzhen WeWins wireless Co.,Ltd", + [3]byte{252, 225, 134}: "A3M Co., LTD", + [3]byte{252, 225, 146}: "Sichuan Jinwangtong Electronic Science&Technology Co,.Ltd", + [3]byte{252, 225, 217}: "Stable Imaging Solutions LLC", + [3]byte{252, 225, 251}: "Array Networks", + [3]byte{252, 226, 63}: "CLAY PAKY SPA", + [3]byte{252, 227, 60}: "HUAWEI TECHNOLOGIES CO.,LTD", + [3]byte{252, 229, 87}: "Nokia Corporation", + [3]byte{252, 232, 146}: "Hangzhou Lancable Technology Co.,Ltd", + [3]byte{252, 233, 152}: "Apple, Inc.", + [3]byte{252, 236, 218}: "Ubiquiti Networks Inc.", + [3]byte{252, 237, 185}: "Arrayent", + [3]byte{252, 241, 54}: "Samsung Electronics Co.,Ltd", + [3]byte{252, 241, 82}: "Sony Corporation", + [3]byte{252, 241, 205}: "OPTEX-FA CO.,LTD.", + [3]byte{252, 245, 40}: "ZyXEL Communications Corporation", + [3]byte{252, 246, 71}: "Fiberhome Telecommunication Technologies Co.,LTD", + [3]byte{252, 248, 174}: "Intel Corporate", + [3]byte{252, 248, 183}: "TRONTEQ Electronic", + [3]byte{252, 250, 247}: "Shanghai Baud Data Communication Co.,Ltd.", + [3]byte{252, 251, 251}: "Cisco Systems, Inc", + [3]byte{252, 252, 72}: "Apple, Inc.", + [3]byte{252, 254, 119}: "Hitachi Reftechno, Inc.", + [3]byte{252, 254, 194}: "Invensys Controls UK Limited", + [3]byte{252, 255, 170}: "IEEE Registration Authority", +} diff --git a/vendor/github.com/google/gopacket/packet.go b/vendor/github.com/google/gopacket/packet.go new file mode 100644 index 00000000..76b62d8a --- /dev/null +++ b/vendor/github.com/google/gopacket/packet.go @@ -0,0 +1,838 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "io" + "os" + "reflect" + "runtime/debug" + "strings" + "time" +) + +// CaptureInfo provides standardized information about a packet captured off +// the wire or read from a file. +type CaptureInfo struct { + // Timestamp is the time the packet was captured, if that is known. + Timestamp time.Time + // CaptureLength is the total number of bytes read off of the wire. + CaptureLength int + // Length is the size of the original packet. Should always be >= + // CaptureLength. + Length int + // InterfaceIndex + InterfaceIndex int +} + +// PacketMetadata contains metadata for a packet. +type PacketMetadata struct { + CaptureInfo + // Truncated is true if packet decoding logic detects that there are fewer + // bytes in the packet than are detailed in various headers (for example, if + // the number of bytes in the IPv4 contents/payload is less than IPv4.Length). + // This is also set automatically for packets captured off the wire if + // CaptureInfo.CaptureLength < CaptureInfo.Length. + Truncated bool +} + +// Packet is the primary object used by gopacket. Packets are created by a +// Decoder's Decode call. A packet is made up of a set of Data, which +// is broken into a number of Layers as it is decoded. +type Packet interface { + //// Functions for outputting the packet as a human-readable string: + //// ------------------------------------------------------------------ + // String returns a human-readable string representation of the packet. + // It uses LayerString on each layer to output the layer. + String() string + // Dump returns a verbose human-readable string representation of the packet, + // including a hex dump of all layers. It uses LayerDump on each layer to + // output the layer. + Dump() string + + //// Functions for accessing arbitrary packet layers: + //// ------------------------------------------------------------------ + // Layers returns all layers in this packet, computing them as necessary + Layers() []Layer + // Layer returns the first layer in this packet of the given type, or nil + Layer(LayerType) Layer + // LayerClass returns the first layer in this packet of the given class, + // or nil. + LayerClass(LayerClass) Layer + + //// Functions for accessing specific types of packet layers. These functions + //// return the first layer of each type found within the packet. + //// ------------------------------------------------------------------ + // LinkLayer returns the first link layer in the packet + LinkLayer() LinkLayer + // NetworkLayer returns the first network layer in the packet + NetworkLayer() NetworkLayer + // TransportLayer returns the first transport layer in the packet + TransportLayer() TransportLayer + // ApplicationLayer returns the first application layer in the packet + ApplicationLayer() ApplicationLayer + // ErrorLayer is particularly useful, since it returns nil if the packet + // was fully decoded successfully, and non-nil if an error was encountered + // in decoding and the packet was only partially decoded. Thus, its output + // can be used to determine if the entire packet was able to be decoded. + ErrorLayer() ErrorLayer + + //// Functions for accessing data specific to the packet: + //// ------------------------------------------------------------------ + // Data returns the set of bytes that make up this entire packet. + Data() []byte + // Metadata returns packet metadata associated with this packet. + Metadata() *PacketMetadata +} + +// packet contains all the information we need to fulfill the Packet interface, +// and its two "subclasses" (yes, no such thing in Go, bear with me), +// eagerPacket and lazyPacket, provide eager and lazy decoding logic around the +// various functions needed to access this information. +type packet struct { + // data contains the entire packet data for a packet + data []byte + // initialLayers is space for an initial set of layers already created inside + // the packet. + initialLayers [6]Layer + // layers contains each layer we've already decoded + layers []Layer + // last is the last layer added to the packet + last Layer + // metadata is the PacketMetadata for this packet + metadata PacketMetadata + + decodeOptions DecodeOptions + + // Pointers to the various important layers + link LinkLayer + network NetworkLayer + transport TransportLayer + application ApplicationLayer + failure ErrorLayer +} + +func (p *packet) SetTruncated() { + p.metadata.Truncated = true +} + +func (p *packet) SetLinkLayer(l LinkLayer) { + if p.link == nil { + p.link = l + } +} + +func (p *packet) SetNetworkLayer(l NetworkLayer) { + if p.network == nil { + p.network = l + } +} + +func (p *packet) SetTransportLayer(l TransportLayer) { + if p.transport == nil { + p.transport = l + } +} + +func (p *packet) SetApplicationLayer(l ApplicationLayer) { + if p.application == nil { + p.application = l + } +} + +func (p *packet) SetErrorLayer(l ErrorLayer) { + if p.failure == nil { + p.failure = l + } +} + +func (p *packet) AddLayer(l Layer) { + p.layers = append(p.layers, l) + p.last = l +} + +func (p *packet) DumpPacketData() { + fmt.Fprint(os.Stderr, p.packetDump()) + os.Stderr.Sync() +} + +func (p *packet) Metadata() *PacketMetadata { + return &p.metadata +} + +func (p *packet) Data() []byte { + return p.data +} + +func (p *packet) DecodeOptions() *DecodeOptions { + return &p.decodeOptions +} + +func (p *packet) addFinalDecodeError(err error, stack []byte) { + fail := &DecodeFailure{err: err, stack: stack} + if p.last == nil { + fail.data = p.data + } else { + fail.data = p.last.LayerPayload() + } + p.AddLayer(fail) + p.SetErrorLayer(fail) +} + +func (p *packet) recoverDecodeError() { + if !p.decodeOptions.SkipDecodeRecovery { + if r := recover(); r != nil { + p.addFinalDecodeError(fmt.Errorf("%v", r), debug.Stack()) + } + } +} + +// LayerString outputs an individual layer as a string. The layer is output +// in a single line, with no trailing newline. This function is specifically +// designed to do the right thing for most layers... it follows the following +// rules: +// * If the Layer has a String function, just output that. +// * Otherwise, output all exported fields in the layer, recursing into +// exported slices and structs. +// NOTE: This is NOT THE SAME AS fmt's "%#v". %#v will output both exported +// and unexported fields... many times packet layers contain unexported stuff +// that would just mess up the output of the layer, see for example the +// Payload layer and it's internal 'data' field, which contains a large byte +// array that would really mess up formatting. +func LayerString(l Layer) string { + return fmt.Sprintf("%v\t%s", l.LayerType(), layerString(reflect.ValueOf(l), false, false)) +} + +// Dumper dumps verbose information on a value. If a layer type implements +// Dumper, then its LayerDump() string will include the results in its output. +type Dumper interface { + Dump() string +} + +// LayerDump outputs a very verbose string representation of a layer. Its +// output is a concatenation of LayerString(l) and hex.Dump(l.LayerContents()). +// It contains newlines and ends with a newline. +func LayerDump(l Layer) string { + var b bytes.Buffer + b.WriteString(LayerString(l)) + b.WriteByte('\n') + if d, ok := l.(Dumper); ok { + dump := d.Dump() + if dump != "" { + b.WriteString(dump) + if dump[len(dump)-1] != '\n' { + b.WriteByte('\n') + } + } + } + b.WriteString(hex.Dump(l.LayerContents())) + return b.String() +} + +// layerString outputs, recursively, a layer in a "smart" way. See docs for +// LayerString for more details. +// +// Params: +// i - value to write out +// anonymous: if we're currently recursing an anonymous member of a struct +// writeSpace: if we've already written a value in a struct, and need to +// write a space before writing more. This happens when we write various +// anonymous values, and need to keep writing more. +func layerString(v reflect.Value, anonymous bool, writeSpace bool) string { + // Let String() functions take precedence. + if v.CanInterface() { + if s, ok := v.Interface().(fmt.Stringer); ok { + return s.String() + } + } + // Reflect, and spit out all the exported fields as key=value. + switch v.Type().Kind() { + case reflect.Interface, reflect.Ptr: + if v.IsNil() { + return "nil" + } + r := v.Elem() + return layerString(r, anonymous, writeSpace) + case reflect.Struct: + var b bytes.Buffer + typ := v.Type() + if !anonymous { + b.WriteByte('{') + } + for i := 0; i < v.NumField(); i++ { + // Check if this is upper-case. + ftype := typ.Field(i) + f := v.Field(i) + if ftype.Anonymous { + anonStr := layerString(f, true, writeSpace) + writeSpace = writeSpace || anonStr != "" + b.WriteString(anonStr) + } else if ftype.PkgPath == "" { // exported + if writeSpace { + b.WriteByte(' ') + } + writeSpace = true + fmt.Fprintf(&b, "%s=%s", typ.Field(i).Name, layerString(f, false, writeSpace)) + } + } + if !anonymous { + b.WriteByte('}') + } + return b.String() + case reflect.Slice: + var b bytes.Buffer + b.WriteByte('[') + if v.Len() > 4 { + fmt.Fprintf(&b, "..%d..", v.Len()) + } else { + for j := 0; j < v.Len(); j++ { + if j != 0 { + b.WriteString(", ") + } + b.WriteString(layerString(v.Index(j), false, false)) + } + } + b.WriteByte(']') + return b.String() + } + return fmt.Sprintf("%v", v.Interface()) +} + +const ( + longBytesLength = 128 +) + +// LongBytesGoString returns a string representation of the byte slice shortened +// using the format '{ ... ( bytes)}' if it +// exceeds a predetermined length. Can be used to avoid filling the display with +// very long byte strings. +func LongBytesGoString(buf []byte) string { + if len(buf) < longBytesLength { + return fmt.Sprintf("%#v", buf) + } + s := fmt.Sprintf("%#v", buf[:longBytesLength-1]) + s = strings.TrimSuffix(s, "}") + return fmt.Sprintf("%s ... (%d bytes)}", s, len(buf)) +} + +func baseLayerString(value reflect.Value) string { + t := value.Type() + content := value.Field(0) + c := make([]byte, content.Len()) + for i := range c { + c[i] = byte(content.Index(i).Uint()) + } + payload := value.Field(1) + p := make([]byte, payload.Len()) + for i := range p { + p[i] = byte(payload.Index(i).Uint()) + } + return fmt.Sprintf("%s{Contents:%s, Payload:%s}", t.String(), + LongBytesGoString(c), + LongBytesGoString(p)) +} + +func layerGoString(i interface{}, b *bytes.Buffer) { + if s, ok := i.(fmt.GoStringer); ok { + b.WriteString(s.GoString()) + return + } + + var v reflect.Value + var ok bool + if v, ok = i.(reflect.Value); !ok { + v = reflect.ValueOf(i) + } + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + if v.Kind() == reflect.Ptr { + b.WriteByte('&') + } + layerGoString(v.Elem().Interface(), b) + case reflect.Struct: + t := v.Type() + b.WriteString(t.String()) + b.WriteByte('{') + for i := 0; i < v.NumField(); i++ { + if i > 0 { + b.WriteString(", ") + } + if t.Field(i).Name == "BaseLayer" { + fmt.Fprintf(b, "BaseLayer:%s", baseLayerString(v.Field(i))) + } else if v.Field(i).Kind() == reflect.Struct { + fmt.Fprintf(b, "%s:", t.Field(i).Name) + layerGoString(v.Field(i), b) + } else if v.Field(i).Kind() == reflect.Ptr { + b.WriteByte('&') + layerGoString(v.Field(i), b) + } else { + fmt.Fprintf(b, "%s:%#v", t.Field(i).Name, v.Field(i)) + } + } + b.WriteByte('}') + default: + fmt.Fprintf(b, "%#v", i) + } +} + +// LayerGoString returns a representation of the layer in Go syntax, +// taking care to shorten "very long" BaseLayer byte slices +func LayerGoString(l Layer) string { + b := new(bytes.Buffer) + layerGoString(l, b) + return b.String() +} + +func (p *packet) packetString() string { + var b bytes.Buffer + fmt.Fprintf(&b, "PACKET: %d bytes", len(p.Data())) + if p.metadata.Truncated { + b.WriteString(", truncated") + } + if p.metadata.Length > 0 { + fmt.Fprintf(&b, ", wire length %d cap length %d", p.metadata.Length, p.metadata.CaptureLength) + } + if !p.metadata.Timestamp.IsZero() { + fmt.Fprintf(&b, " @ %v", p.metadata.Timestamp) + } + b.WriteByte('\n') + for i, l := range p.layers { + fmt.Fprintf(&b, "- Layer %d (%02d bytes) = %s\n", i+1, len(l.LayerContents()), LayerString(l)) + } + return b.String() +} + +func (p *packet) packetDump() string { + var b bytes.Buffer + fmt.Fprintf(&b, "-- FULL PACKET DATA (%d bytes) ------------------------------------\n%s", len(p.data), hex.Dump(p.data)) + for i, l := range p.layers { + fmt.Fprintf(&b, "--- Layer %d ---\n%s", i+1, LayerDump(l)) + } + return b.String() +} + +// eagerPacket is a packet implementation that does eager decoding. Upon +// initial construction, it decodes all the layers it can from packet data. +// eagerPacket implements Packet and PacketBuilder. +type eagerPacket struct { + packet +} + +var errNilDecoder = errors.New("NextDecoder passed nil decoder, probably an unsupported decode type") + +func (p *eagerPacket) NextDecoder(next Decoder) error { + if next == nil { + return errNilDecoder + } + if p.last == nil { + return errors.New("NextDecoder called, but no layers added yet") + } + d := p.last.LayerPayload() + if len(d) == 0 { + return nil + } + // Since we're eager, immediately call the next decoder. + return next.Decode(d, p) +} +func (p *eagerPacket) initialDecode(dec Decoder) { + defer p.recoverDecodeError() + err := dec.Decode(p.data, p) + if err != nil { + p.addFinalDecodeError(err, nil) + } +} +func (p *eagerPacket) LinkLayer() LinkLayer { + return p.link +} +func (p *eagerPacket) NetworkLayer() NetworkLayer { + return p.network +} +func (p *eagerPacket) TransportLayer() TransportLayer { + return p.transport +} +func (p *eagerPacket) ApplicationLayer() ApplicationLayer { + return p.application +} +func (p *eagerPacket) ErrorLayer() ErrorLayer { + return p.failure +} +func (p *eagerPacket) Layers() []Layer { + return p.layers +} +func (p *eagerPacket) Layer(t LayerType) Layer { + for _, l := range p.layers { + if l.LayerType() == t { + return l + } + } + return nil +} +func (p *eagerPacket) LayerClass(lc LayerClass) Layer { + for _, l := range p.layers { + if lc.Contains(l.LayerType()) { + return l + } + } + return nil +} +func (p *eagerPacket) String() string { return p.packetString() } +func (p *eagerPacket) Dump() string { return p.packetDump() } + +// lazyPacket does lazy decoding on its packet data. On construction it does +// no initial decoding. For each function call, it decodes only as many layers +// as are necessary to compute the return value for that function. +// lazyPacket implements Packet and PacketBuilder. +type lazyPacket struct { + packet + next Decoder +} + +func (p *lazyPacket) NextDecoder(next Decoder) error { + if next == nil { + return errNilDecoder + } + p.next = next + return nil +} +func (p *lazyPacket) decodeNextLayer() { + if p.next == nil { + return + } + d := p.data + if p.last != nil { + d = p.last.LayerPayload() + } + next := p.next + p.next = nil + // We've just set p.next to nil, so if we see we have no data, this should be + // the final call we get to decodeNextLayer if we return here. + if len(d) == 0 { + return + } + defer p.recoverDecodeError() + err := next.Decode(d, p) + if err != nil { + p.addFinalDecodeError(err, nil) + } +} +func (p *lazyPacket) LinkLayer() LinkLayer { + for p.link == nil && p.next != nil { + p.decodeNextLayer() + } + return p.link +} +func (p *lazyPacket) NetworkLayer() NetworkLayer { + for p.network == nil && p.next != nil { + p.decodeNextLayer() + } + return p.network +} +func (p *lazyPacket) TransportLayer() TransportLayer { + for p.transport == nil && p.next != nil { + p.decodeNextLayer() + } + return p.transport +} +func (p *lazyPacket) ApplicationLayer() ApplicationLayer { + for p.application == nil && p.next != nil { + p.decodeNextLayer() + } + return p.application +} +func (p *lazyPacket) ErrorLayer() ErrorLayer { + for p.failure == nil && p.next != nil { + p.decodeNextLayer() + } + return p.failure +} +func (p *lazyPacket) Layers() []Layer { + for p.next != nil { + p.decodeNextLayer() + } + return p.layers +} +func (p *lazyPacket) Layer(t LayerType) Layer { + for _, l := range p.layers { + if l.LayerType() == t { + return l + } + } + numLayers := len(p.layers) + for p.next != nil { + p.decodeNextLayer() + for _, l := range p.layers[numLayers:] { + if l.LayerType() == t { + return l + } + } + numLayers = len(p.layers) + } + return nil +} +func (p *lazyPacket) LayerClass(lc LayerClass) Layer { + for _, l := range p.layers { + if lc.Contains(l.LayerType()) { + return l + } + } + numLayers := len(p.layers) + for p.next != nil { + p.decodeNextLayer() + for _, l := range p.layers[numLayers:] { + if lc.Contains(l.LayerType()) { + return l + } + } + numLayers = len(p.layers) + } + return nil +} +func (p *lazyPacket) String() string { p.Layers(); return p.packetString() } +func (p *lazyPacket) Dump() string { p.Layers(); return p.packetDump() } + +// DecodeOptions tells gopacket how to decode a packet. +type DecodeOptions struct { + // Lazy decoding decodes the minimum number of layers needed to return data + // for a packet at each function call. Be careful using this with concurrent + // packet processors, as each call to packet.* could mutate the packet, and + // two concurrent function calls could interact poorly. + Lazy bool + // NoCopy decoding doesn't copy its input buffer into storage that's owned by + // the packet. If you can guarantee that the bytes underlying the slice + // passed into NewPacket aren't going to be modified, this can be faster. If + // there's any chance that those bytes WILL be changed, this will invalidate + // your packets. + NoCopy bool + // SkipDecodeRecovery skips over panic recovery during packet decoding. + // Normally, when packets decode, if a panic occurs, that panic is captured + // by a recover(), and a DecodeFailure layer is added to the packet detailing + // the issue. If this flag is set, panics are instead allowed to continue up + // the stack. + SkipDecodeRecovery bool + // DecodeStreamsAsDatagrams enables routing of application-level layers in the TCP + // decoder. If true, we should try to decode layers after TCP in single packets. + // This is disabled by default because the reassembly package drives the decoding + // of TCP payload data after reassembly. + DecodeStreamsAsDatagrams bool +} + +// Default decoding provides the safest (but slowest) method for decoding +// packets. It eagerly processes all layers (so it's concurrency-safe) and it +// copies its input buffer upon creation of the packet (so the packet remains +// valid if the underlying slice is modified. Both of these take time, +// though, so beware. If you can guarantee that the packet will only be used +// by one goroutine at a time, set Lazy decoding. If you can guarantee that +// the underlying slice won't change, set NoCopy decoding. +var Default = DecodeOptions{} + +// Lazy is a DecodeOptions with just Lazy set. +var Lazy = DecodeOptions{Lazy: true} + +// NoCopy is a DecodeOptions with just NoCopy set. +var NoCopy = DecodeOptions{NoCopy: true} + +// DecodeStreamsAsDatagrams is a DecodeOptions with just DecodeStreamsAsDatagrams set. +var DecodeStreamsAsDatagrams = DecodeOptions{DecodeStreamsAsDatagrams: true} + +// NewPacket creates a new Packet object from a set of bytes. The +// firstLayerDecoder tells it how to interpret the first layer from the bytes, +// future layers will be generated from that first layer automatically. +func NewPacket(data []byte, firstLayerDecoder Decoder, options DecodeOptions) Packet { + if !options.NoCopy { + dataCopy := make([]byte, len(data)) + copy(dataCopy, data) + data = dataCopy + } + if options.Lazy { + p := &lazyPacket{ + packet: packet{data: data, decodeOptions: options}, + next: firstLayerDecoder, + } + p.layers = p.initialLayers[:0] + // Crazy craziness: + // If the following return statemet is REMOVED, and Lazy is FALSE, then + // eager packet processing becomes 17% FASTER. No, there is no logical + // explanation for this. However, it's such a hacky micro-optimization that + // we really can't rely on it. It appears to have to do with the size the + // compiler guesses for this function's stack space, since one symptom is + // that with the return statement in place, we more than double calls to + // runtime.morestack/runtime.lessstack. We'll hope the compiler gets better + // over time and we get this optimization for free. Until then, we'll have + // to live with slower packet processing. + return p + } + p := &eagerPacket{ + packet: packet{data: data, decodeOptions: options}, + } + p.layers = p.initialLayers[:0] + p.initialDecode(firstLayerDecoder) + return p +} + +// PacketDataSource is an interface for some source of packet data. Users may +// create their own implementations, or use the existing implementations in +// gopacket/pcap (libpcap, allows reading from live interfaces or from +// pcap files) or gopacket/pfring (PF_RING, allows reading from live +// interfaces). +type PacketDataSource interface { + // ReadPacketData returns the next packet available from this data source. + // It returns: + // data: The bytes of an individual packet. + // ci: Metadata about the capture + // err: An error encountered while reading packet data. If err != nil, + // then data/ci will be ignored. + ReadPacketData() (data []byte, ci CaptureInfo, err error) +} + +// ConcatFinitePacketDataSources returns a PacketDataSource that wraps a set +// of internal PacketDataSources, each of which will stop with io.EOF after +// reading a finite number of packets. The returned PacketDataSource will +// return all packets from the first finite source, followed by all packets from +// the second, etc. Once all finite sources have returned io.EOF, the returned +// source will as well. +func ConcatFinitePacketDataSources(pds ...PacketDataSource) PacketDataSource { + c := concat(pds) + return &c +} + +type concat []PacketDataSource + +func (c *concat) ReadPacketData() (data []byte, ci CaptureInfo, err error) { + for len(*c) > 0 { + data, ci, err = (*c)[0].ReadPacketData() + if err == io.EOF { + *c = (*c)[1:] + continue + } + return + } + return nil, CaptureInfo{}, io.EOF +} + +// ZeroCopyPacketDataSource is an interface to pull packet data from sources +// that allow data to be returned without copying to a user-controlled buffer. +// It's very similar to PacketDataSource, except that the caller must be more +// careful in how the returned buffer is handled. +type ZeroCopyPacketDataSource interface { + // ZeroCopyReadPacketData returns the next packet available from this data source. + // It returns: + // data: The bytes of an individual packet. Unlike with + // PacketDataSource's ReadPacketData, the slice returned here points + // to a buffer owned by the data source. In particular, the bytes in + // this buffer may be changed by future calls to + // ZeroCopyReadPacketData. Do not use the returned buffer after + // subsequent ZeroCopyReadPacketData calls. + // ci: Metadata about the capture + // err: An error encountered while reading packet data. If err != nil, + // then data/ci will be ignored. + ZeroCopyReadPacketData() (data []byte, ci CaptureInfo, err error) +} + +// PacketSource reads in packets from a PacketDataSource, decodes them, and +// returns them. +// +// There are currently two different methods for reading packets in through +// a PacketSource: +// +// Reading With Packets Function +// +// This method is the most convenient and easiest to code, but lacks +// flexibility. Packets returns a 'chan Packet', then asynchronously writes +// packets into that channel. Packets uses a blocking channel, and closes +// it if an io.EOF is returned by the underlying PacketDataSource. All other +// PacketDataSource errors are ignored and discarded. +// for packet := range packetSource.Packets() { +// ... +// } +// +// Reading With NextPacket Function +// +// This method is the most flexible, and exposes errors that may be +// encountered by the underlying PacketDataSource. It's also the fastest +// in a tight loop, since it doesn't have the overhead of a channel +// read/write. However, it requires the user to handle errors, most +// importantly the io.EOF error in cases where packets are being read from +// a file. +// for { +// packet, err := packetSource.NextPacket() +// if err == io.EOF { +// break +// } else if err != nil { +// log.Println("Error:", err) +// continue +// } +// handlePacket(packet) // Do something with each packet. +// } +type PacketSource struct { + source PacketDataSource + decoder Decoder + // DecodeOptions is the set of options to use for decoding each piece + // of packet data. This can/should be changed by the user to reflect the + // way packets should be decoded. + DecodeOptions + c chan Packet +} + +// NewPacketSource creates a packet data source. +func NewPacketSource(source PacketDataSource, decoder Decoder) *PacketSource { + return &PacketSource{ + source: source, + decoder: decoder, + } +} + +// NextPacket returns the next decoded packet from the PacketSource. On error, +// it returns a nil packet and a non-nil error. +func (p *PacketSource) NextPacket() (Packet, error) { + data, ci, err := p.source.ReadPacketData() + if err != nil { + return nil, err + } + packet := NewPacket(data, p.decoder, p.DecodeOptions) + m := packet.Metadata() + m.CaptureInfo = ci + m.Truncated = m.Truncated || ci.CaptureLength < ci.Length + return packet, nil +} + +// packetsToChannel reads in all packets from the packet source and sends them +// to the given channel. When it receives an error, it ignores it. When it +// receives an io.EOF, it closes the channel. +func (p *PacketSource) packetsToChannel() { + defer close(p.c) + for { + packet, err := p.NextPacket() + if err == io.EOF { + return + } else if err == nil { + p.c <- packet + } + } +} + +// Packets returns a channel of packets, allowing easy iterating over +// packets. Packets will be asynchronously read in from the underlying +// PacketDataSource and written to the returned channel. If the underlying +// PacketDataSource returns an io.EOF error, the channel will be closed. +// If any other error is encountered, it is ignored. +// +// for packet := range packetSource.Packets() { +// handlePacket(packet) // Do something with each packet. +// } +// +// If called more than once, returns the same channel. +func (p *PacketSource) Packets() chan Packet { + if p.c == nil { + p.c = make(chan Packet, 1000) + go p.packetsToChannel() + } + return p.c +} diff --git a/vendor/github.com/google/gopacket/packet_test.go b/vendor/github.com/google/gopacket/packet_test.go new file mode 100644 index 00000000..bd054488 --- /dev/null +++ b/vendor/github.com/google/gopacket/packet_test.go @@ -0,0 +1,62 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "io" + "reflect" + "testing" +) + +type embedded struct { + A, B int +} + +type embedding struct { + embedded + C, D int +} + +func TestDumpEmbedded(t *testing.T) { + e := embedding{embedded: embedded{A: 1, B: 2}, C: 3, D: 4} + if got, want := layerString(reflect.ValueOf(e), false, false), "{A=1 B=2 C=3 D=4}"; got != want { + t.Errorf("embedded dump mismatch:\n got: %v\n want: %v", got, want) + } +} + +type singlePacketSource [1][]byte + +func (s *singlePacketSource) ReadPacketData() ([]byte, CaptureInfo, error) { + if (*s)[0] == nil { + return nil, CaptureInfo{}, io.EOF + } + out := (*s)[0] + (*s)[0] = nil + return out, CaptureInfo{}, nil +} + +func TestConcatPacketSources(t *testing.T) { + sourceA := &singlePacketSource{[]byte{1}} + sourceB := &singlePacketSource{[]byte{2}} + sourceC := &singlePacketSource{[]byte{3}} + concat := ConcatFinitePacketDataSources(sourceA, sourceB, sourceC) + a, _, err := concat.ReadPacketData() + if err != nil || len(a) != 1 || a[0] != 1 { + t.Errorf("expected [1], got %v/%v", a, err) + } + b, _, err := concat.ReadPacketData() + if err != nil || len(b) != 1 || b[0] != 2 { + t.Errorf("expected [2], got %v/%v", b, err) + } + c, _, err := concat.ReadPacketData() + if err != nil || len(c) != 1 || c[0] != 3 { + t.Errorf("expected [3], got %v/%v", c, err) + } + if _, _, err := concat.ReadPacketData(); err != io.EOF { + t.Errorf("expected io.EOF, got %v", err) + } +} diff --git a/vendor/github.com/google/gopacket/parser.go b/vendor/github.com/google/gopacket/parser.go new file mode 100644 index 00000000..f786834e --- /dev/null +++ b/vendor/github.com/google/gopacket/parser.go @@ -0,0 +1,198 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" +) + +// DecodingLayer is an interface for packet layers that can decode themselves. +// +// The important part of DecodingLayer is that they decode themselves in-place. +// Calling DecodeFromBytes on a DecodingLayer totally resets the entire layer to +// the new state defined by the data passed in. A returned error leaves the +// DecodingLayer in an unknown intermediate state, thus its fields should not be +// trusted. +// +// Because the DecodingLayer is resetting its own fields, a call to +// DecodeFromBytes should normally not require any memory allocation. +type DecodingLayer interface { + // DecodeFromBytes resets the internal state of this layer to the state + // defined by the passed-in bytes. Slices in the DecodingLayer may + // reference the passed-in data, so care should be taken to copy it + // first should later modification of data be required before the + // DecodingLayer is discarded. + DecodeFromBytes(data []byte, df DecodeFeedback) error + // CanDecode returns the set of LayerTypes this DecodingLayer can + // decode. For Layers that are also DecodingLayers, this will most + // often be that Layer's LayerType(). + CanDecode() LayerClass + // NextLayerType returns the LayerType which should be used to decode + // the LayerPayload. + NextLayerType() LayerType + // LayerPayload is the set of bytes remaining to decode after a call to + // DecodeFromBytes. + LayerPayload() []byte +} + +// DecodingLayerParser parses a given set of layer types. See DecodeLayers for +// more information on how DecodingLayerParser should be used. +type DecodingLayerParser struct { + // DecodingLayerParserOptions is the set of options available to the + // user to define the parser's behavior. + DecodingLayerParserOptions + first LayerType + decoders map[LayerType]DecodingLayer + df DecodeFeedback + // Truncated is set when a decode layer detects that the packet has been + // truncated. + Truncated bool +} + +// AddDecodingLayer adds a decoding layer to the parser. This adds support for +// the decoding layer's CanDecode layers to the parser... should they be +// encountered, they'll be parsed. +func (l *DecodingLayerParser) AddDecodingLayer(d DecodingLayer) { + for _, typ := range d.CanDecode().LayerTypes() { + l.decoders[typ] = d + } +} + +// SetTruncated is used by DecodingLayers to set the Truncated boolean in the +// DecodingLayerParser. Users should simply read Truncated after calling +// DecodeLayers. +func (l *DecodingLayerParser) SetTruncated() { + l.Truncated = true +} + +// NewDecodingLayerParser creates a new DecodingLayerParser and adds in all +// of the given DecodingLayers with AddDecodingLayer. +// +// Each call to DecodeLayers will attempt to decode the given bytes first by +// treating them as a 'first'-type layer, then by using NextLayerType on +// subsequently decoded layers to find the next relevant decoder. Should a +// deoder not be available for the layer type returned by NextLayerType, +// decoding will stop. +func NewDecodingLayerParser(first LayerType, decoders ...DecodingLayer) *DecodingLayerParser { + dlp := &DecodingLayerParser{ + decoders: make(map[LayerType]DecodingLayer), + first: first, + } + dlp.df = dlp // Cast this once to the interface + for _, d := range decoders { + dlp.AddDecodingLayer(d) + } + return dlp +} + +// DecodeLayers decodes as many layers as possible from the given data. It +// initially treats the data as layer type 'typ', then uses NextLayerType on +// each subsequent decoded layer until it gets to a layer type it doesn't know +// how to parse. +// +// For each layer successfully decoded, DecodeLayers appends the layer type to +// the decoded slice. DecodeLayers truncates the 'decoded' slice initially, so +// there's no need to empty it yourself. +// +// This decoding method is about an order of magnitude faster than packet +// decoding, because it only decodes known layers that have already been +// allocated. This means it doesn't need to allocate each layer it returns... +// instead it overwrites the layers that already exist. +// +// Example usage: +// func main() { +// var eth layers.Ethernet +// var ip4 layers.IPv4 +// var ip6 layers.IPv6 +// var tcp layers.TCP +// var udp layers.UDP +// var payload gopacket.Payload +// parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp, &udp, &payload) +// var source gopacket.PacketDataSource = getMyDataSource() +// decodedLayers := make([]gopacket.LayerType, 0, 10) +// for { +// data, _, err := source.ReadPacketData() +// if err == nil { +// fmt.Println("Error reading packet data: ", err) +// continue +// } +// fmt.Println("Decoding packet") +// err = parser.DecodeLayers(data, &decodedLayers) +// for _, typ := range decodedLayers { +// fmt.Println(" Successfully decoded layer type", typ) +// switch typ { +// case layers.LayerTypeEthernet: +// fmt.Println(" Eth ", eth.SrcMAC, eth.DstMAC) +// case layers.LayerTypeIPv4: +// fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP) +// case layers.LayerTypeIPv6: +// fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP) +// case layers.LayerTypeTCP: +// fmt.Println(" TCP ", tcp.SrcPort, tcp.DstPort) +// case layers.LayerTypeUDP: +// fmt.Println(" UDP ", udp.SrcPort, udp.DstPort) +// } +// } +// if decodedLayers.Truncated { +// fmt.Println(" Packet has been truncated") +// } +// if err != nil { +// fmt.Println(" Error encountered:", err) +// } +// } +// } +// +// If DecodeLayers is unable to decode the next layer type, it will return the +// error UnsupportedLayerType. +func (l *DecodingLayerParser) DecodeLayers(data []byte, decoded *[]LayerType) (err error) { + l.Truncated = false + if !l.IgnorePanic { + defer panicToError(&err) + } + typ := l.first + *decoded = (*decoded)[:0] // Truncated decoded layers. + for len(data) > 0 { + decoder, ok := l.decoders[typ] + if !ok { + return UnsupportedLayerType(typ) + } else if err = decoder.DecodeFromBytes(data, l.df); err != nil { + return err + } + *decoded = append(*decoded, typ) + typ = decoder.NextLayerType() + data = decoder.LayerPayload() + } + return nil +} + +// UnsupportedLayerType is returned by DecodingLayerParser if DecodeLayers +// encounters a layer type that the DecodingLayerParser has no decoder for. +type UnsupportedLayerType LayerType + +// Error implements the error interface, returning a string to say that the +// given layer type is unsupported. +func (e UnsupportedLayerType) Error() string { + return fmt.Sprintf("No decoder for layer type %v", LayerType(e)) +} + +func panicToError(e *error) { + if r := recover(); r != nil { + *e = fmt.Errorf("panic: %v", r) + } +} + +// DecodingLayerParserOptions provides options to affect the behavior of a given +// DecodingLayerParser. +type DecodingLayerParserOptions struct { + // IgnorePanic determines whether a DecodingLayerParser should stop + // panics on its own (by returning them as an error from DecodeLayers) + // or should allow them to raise up the stack. Handling errors does add + // latency to the process of decoding layers, but is much safer for + // callers. IgnorePanic defaults to false, thus if the caller does + // nothing decode panics will be returned as errors. + IgnorePanic bool +} diff --git a/vendor/github.com/google/gopacket/pcap/doc.go b/vendor/github.com/google/gopacket/pcap/doc.go new file mode 100644 index 00000000..5bf8d86f --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/doc.go @@ -0,0 +1,106 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/* +Package pcap allows users of gopacket to read packets off the wire or from +pcap files. + +This package is meant to be used with its parent, +http://github.com/google/gopacket, although it can also be used independently +if you just want to get packet data from the wire. + +Reading PCAP Files + +The following code can be used to read in data from a pcap file. + + if handle, err := pcap.OpenOffline("/path/to/my/file"); err != nil { + panic(err) + } else { + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + for packet := range packetSource.Packets() { + handlePacket(packet) // Do something with a packet here. + } + } + +Reading Live Packets + +The following code can be used to read in data from a live device, in this case +"eth0". + + if handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever); err != nil { + panic(err) + } else if err := handle.SetBPFFilter("tcp and port 80"); err != nil { // optional + panic(err) + } else { + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + for packet := range packetSource.Packets() { + handlePacket(packet) // Do something with a packet here. + } + } + +Inactive Handles + +Newer PCAP functionality requires the concept of an 'inactive' PCAP handle. +Instead of constantly adding new arguments to pcap_open_live, users now call +pcap_create to create a handle, set it up with a bunch of optional function +calls, then call pcap_activate to activate it. This library mirrors that +mechanism, for those that want to expose/use these new features: + + inactive, err := pcap.NewInactiveHandle(deviceName) + if err != nil { + log.Fatal(err) + } + defer inactive.CleanUp() + + // Call various functions on inactive to set it up the way you'd like: + if err = inactive.SetTimeout(time.Minute); err != nil { + log.Fatal(err) + } else if err = inactive.SetTimestampSource("foo"); err != nil { + log.Fatal(err) + } + + // Finally, create the actual handle by calling Activate: + handle, err := inactive.Activate() // after this, inactive is no longer valid + if err != nil { + log.Fatal(err) + } + defer handle.Close() + + // Now use your handle as you see fit. + +PCAP Timeouts + +pcap.OpenLive and pcap.SetTimeout both take timeouts. +If you don't care about timeouts, just pass in BlockForever, +which should do what you expect with minimal fuss. + +A timeout of 0 is not recommended. Some platforms, like Macs +(http://www.manpages.info/macosx/pcap.3.html) say: + The read timeout is used to arrange that the read not necessarily return + immediately when a packet is seen, but that it wait for some amount of time + to allow more packets to arrive and to read multiple packets from the OS + kernel in one operation. +This means that if you only capture one packet, the kernel might decide to wait +'timeout' for more packets to batch with it before returning. A timeout of +0, then, means 'wait forever for more packets', which is... not good. + +To get around this, we've introduced the following behavior: if a negative +timeout is passed in, we set the positive timeout in the handle, then loop +internally in ReadPacketData/ZeroCopyReadPacketData when we see timeout +errors. + +PCAP File Writing + +This package does not implement PCAP file writing. However, gopacket/pcapgo +does! Look there if you'd like to write PCAP files. + +Note For Windows 10 Users + +If you're trying to use 64-bit winpcap on Windows 10, you might have to do +the crazy hijinks detailed at +http://stackoverflow.com/questions/38047858/compile-gopacket-on-windows-64bit +*/ +package pcap diff --git a/vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go b/vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go new file mode 100644 index 00000000..cbcae17c --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go @@ -0,0 +1,247 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This benchmark reads in file /gopacket_benchmark.pcap and measures +// the time it takes to decode all packets from that file. If the file doesn't +// exist, it's pulled down from a publicly available location. However, you can +// feel free to substitute your own file at that location, in which case the +// benchmark will run on your own data. +// +// It's also useful for figuring out which packets may be causing errors. Pass +// in the --printErrors flag, and it'll print out error layers for each packet +// that has them. This includes any packets that it's just unable to decode, +// which is a great way to find new protocols to decode, and get test packets to +// write tests for them. +package main + +import ( + "compress/gzip" + "encoding/hex" + "flag" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/tcpassembly" + "io" + "io/ioutil" + "net/http" + "os" + "runtime" + "runtime/pprof" + "time" +) + +var decodeLazy *bool = flag.Bool("lazy", false, "If true, use lazy decoding") +var decodeNoCopy *bool = flag.Bool("nocopy", true, "If true, avoid an extra copy when decoding packets") +var printErrors *bool = flag.Bool("printErrors", false, "If true, check for and print error layers.") +var printLayers *bool = flag.Bool("printLayers", false, "If true, print out the layers of each packet") +var repeat *int = flag.Int("repeat", 5, "Read over the file N times") +var cpuProfile *string = flag.String("cpuprofile", "", "If set, write CPU profile to filename") +var url *string = flag.String("url", "http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/tuesday/inside.tcpdump.gz", "URL to gzip'd pcap file") + +type BufferPacketSource struct { + index int + data [][]byte + ci []gopacket.CaptureInfo +} + +func NewBufferPacketSource(p gopacket.PacketDataSource) *BufferPacketSource { + start := time.Now() + b := &BufferPacketSource{} + for { + data, ci, err := p.ReadPacketData() + if err == io.EOF { + break + } + b.data = append(b.data, data) + b.ci = append(b.ci, ci) + } + duration := time.Since(start) + fmt.Printf("Reading packet data into memory: %d packets in %v, %v per packet\n", len(b.data), duration, duration/time.Duration(len(b.data))) + return b +} + +func (b *BufferPacketSource) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + if b.index >= len(b.data) { + err = io.EOF + return + } + data = b.data[b.index] + ci = b.ci[b.index] + b.index++ + return +} + +func (b *BufferPacketSource) Reset() { + runtime.GC() + b.index = 0 +} + +func main() { + flag.Parse() + filename := os.TempDir() + string(os.PathSeparator) + "gopacket_benchmark.pcap" + if _, err := os.Stat(filename); err != nil { + // This URL points to a publicly available packet data set from a DARPA + // intrusion detection evaluation. See + // http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/index.html + // for more details. + fmt.Println("Local pcap file", filename, "doesn't exist, reading from", *url) + if resp, err := http.Get(*url); err != nil { + panic(err) + } else if out, err := os.Create(filename); err != nil { + panic(err) + } else if gz, err := gzip.NewReader(resp.Body); err != nil { + panic(err) + } else if n, err := io.Copy(out, gz); err != nil { + panic(err) + } else if err := gz.Close(); err != nil { + panic(err) + } else if err := out.Close(); err != nil { + panic(err) + } else { + fmt.Println("Successfully read", n, "bytes from url, unzipped to local storage") + } + } + fmt.Println("Reading file once through to hopefully cache most of it") + if f, err := os.Open(filename); err != nil { + panic(err) + } else if n, err := io.Copy(ioutil.Discard, f); err != nil { + panic(err) + } else if err := f.Close(); err != nil { + panic(err) + } else { + fmt.Println("Read in file", filename, ", total of", n, "bytes") + } + if *cpuProfile != "" { + if cpu, err := os.Create(*cpuProfile); err != nil { + panic(err) + } else if err := pprof.StartCPUProfile(cpu); err != nil { + panic(err) + } else { + defer func() { + pprof.StopCPUProfile() + cpu.Close() + }() + } + } + var packetDataSource *BufferPacketSource + var packetSource *gopacket.PacketSource + fmt.Printf("Opening file %q for read\n", filename) + if h, err := pcap.OpenOffline(filename); err != nil { + panic(err) + } else { + fmt.Println("Reading all packets into memory with BufferPacketSource.") + start := time.Now() + packetDataSource = NewBufferPacketSource(h) + duration := time.Since(start) + fmt.Printf("Time to read packet data into memory from file: %v\n", duration) + packetSource = gopacket.NewPacketSource(packetDataSource, h.LinkType()) + packetSource.DecodeOptions.Lazy = *decodeLazy + packetSource.DecodeOptions.NoCopy = *decodeNoCopy + } + fmt.Println() + for i := 0; i < *repeat; i++ { + packetDataSource.Reset() + fmt.Printf("Benchmarking decode %d/%d\n", i+1, *repeat) + benchmarkPacketDecode(packetSource) + } + fmt.Println() + for i := 0; i < *repeat; i++ { + packetDataSource.Reset() + fmt.Printf("Benchmarking decoding layer parser %d/%d\n", i+1, *repeat) + benchmarkLayerDecode(packetDataSource, false) + } + fmt.Println() + for i := 0; i < *repeat; i++ { + packetDataSource.Reset() + fmt.Printf("Benchmarking decoding layer parser with assembly %d/%d\n", i+1, *repeat) + benchmarkLayerDecode(packetDataSource, true) + } +} + +func benchmarkPacketDecode(packetSource *gopacket.PacketSource) { + count, errors := 0, 0 + start := time.Now() + for packet, err := packetSource.NextPacket(); err != io.EOF; packet, err = packetSource.NextPacket() { + if err != nil { + fmt.Println("Error reading in packet:", err) + continue + } + count++ + var hasError bool + if *printErrors && packet.ErrorLayer() != nil { + fmt.Println("\n\n\nError decoding packet:", packet.ErrorLayer().Error()) + fmt.Println(hex.Dump(packet.Data())) + fmt.Printf("%#v\n", packet.Data()) + errors++ + hasError = true + } + if *printLayers || hasError { + fmt.Printf("\n=== PACKET %d ===\n", count) + for _, l := range packet.Layers() { + fmt.Printf("--- LAYER %v ---\n%#v\n\n", l.LayerType(), l) + } + fmt.Println() + } + } + duration := time.Since(start) + fmt.Printf("\tRead in %v packets in %v, %v per packet\n", count, duration, duration/time.Duration(count)) + if *printErrors { + fmt.Printf("%v errors, successfully decoded %.02f%%\n", errors, float64(count-errors)*100.0/float64(count)) + } +} + +type streamFactory struct { +} + +func (s *streamFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream { + return s +} +func (s *streamFactory) Reassembled([]tcpassembly.Reassembly) { +} +func (s *streamFactory) ReassemblyComplete() { +} + +func benchmarkLayerDecode(source *BufferPacketSource, assemble bool) { + var tcp layers.TCP + var ip layers.IPv4 + var eth layers.Ethernet + var udp layers.UDP + var icmp layers.ICMPv4 + var payload gopacket.Payload + parser := gopacket.NewDecodingLayerParser( + layers.LayerTypeEthernet, + ð, &ip, &icmp, &tcp, &udp, &payload) + pool := tcpassembly.NewStreamPool(&streamFactory{}) + assembler := tcpassembly.NewAssembler(pool) + var decoded []gopacket.LayerType + start := time.Now() + packets, decodedlayers, assembled := 0, 0, 0 + for { + packets++ + data, ci, err := source.ReadPacketData() + if err == io.EOF { + break + } else if err != nil { + fmt.Println("Error reading packet: ", err) + continue + } + err = parser.DecodeLayers(data, &decoded) + for _, typ := range decoded { + decodedlayers++ + if typ == layers.LayerTypeTCP && assemble { + assembled++ + assembler.AssembleWithTimestamp(ip.NetworkFlow(), &tcp, ci.Timestamp) + } + } + } + if assemble { + assembler.FlushAll() + } + duration := time.Since(start) + fmt.Printf("\tRead in %d packets in %v, decoded %v layers, assembled %v packets: %v per packet\n", packets, duration, decodedlayers, assembled, duration/time.Duration(packets)) +} diff --git a/vendor/github.com/google/gopacket/pcap/pcap.go b/vendor/github.com/google/gopacket/pcap/pcap.go new file mode 100644 index 00000000..1ecdf035 --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/pcap.go @@ -0,0 +1,1005 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcap + +/* +#cgo solaris LDFLAGS: -L /opt/local/lib -lpcap +#cgo linux LDFLAGS: -lpcap +#cgo dragonfly LDFLAGS: -lpcap +#cgo freebsd LDFLAGS: -lpcap +#cgo openbsd LDFLAGS: -lpcap +#cgo netbsd LDFLAGS: -lpcap +#cgo darwin LDFLAGS: -lpcap +#cgo windows CFLAGS: -I C:/WpdPack/Include +#cgo windows,386 LDFLAGS: -L C:/WpdPack/Lib -lwpcap +#cgo windows,amd64 LDFLAGS: -L C:/WpdPack/Lib/x64 -lwpcap +#include +#include + +// Some old versions of pcap don't define this constant. +#ifndef PCAP_NETMASK_UNKNOWN +#define PCAP_NETMASK_UNKNOWN 0xffffffff +#endif + +// libpcap doesn't actually export its version in a #define-guardable way, +// so we have to use other defined things to differentiate versions. +// We assume at least libpcap v1.1 at the moment. +// See http://upstream-tracker.org/versions/libpcap.html + +#ifndef PCAP_ERROR_TSTAMP_PRECISION_NOTSUP // < v1.5 + +int pcap_set_immediate_mode(pcap_t *p, int mode) { + return PCAP_ERROR; +} + +#ifndef PCAP_TSTAMP_HOST // < v1.2 + +int pcap_set_tstamp_type(pcap_t* p, int t) { return -1; } +int pcap_list_tstamp_types(pcap_t* p, int** t) { return 0; } +void pcap_free_tstamp_types(int *tstamp_types) {} +const char* pcap_tstamp_type_val_to_name(int t) { + return "pcap timestamp types not supported"; +} +int pcap_tstamp_type_name_to_val(const char* t) { + return PCAP_ERROR; +} + +#endif // < v1.2 +#endif // < v1.5 + +#ifndef PCAP_ERROR_PROMISC_PERM_DENIED +#define PCAP_ERROR_PROMISC_PERM_DENIED -11 +#endif + +// WinPcap doesn't export a pcap_statustostr, so use the less-specific +// pcap_strerror. Note that linking against something like cygwin libpcap +// may result is less-specific error messages. +#ifdef WIN32 +#define pcap_statustostr pcap_strerror + +// WinPcap also doesn't export pcap_can_set_rfmon and pcap_set_rfmon, +// as those are handled by separate libraries (airpcap). +// https://www.winpcap.org/docs/docs_412/html/group__wpcapfunc.html +// Stub out those functions here, returning values that indicate rfmon +// setting is unavailable/unsuccessful. +int pcap_can_set_rfmon(pcap_t *p) { + return 0; +} + +int pcap_set_rfmon(pcap_t *p, int rfmon) { + return PCAP_ERROR; +} +#endif + +// Windows, Macs, and Linux all use different time types. Joy. +#ifdef WIN32 +#define gopacket_time_secs_t long +#define gopacket_time_usecs_t long +#elif __APPLE__ +#define gopacket_time_secs_t __darwin_time_t +#define gopacket_time_usecs_t __darwin_suseconds_t +#elif __GLIBC__ +#define gopacket_time_secs_t __time_t +#define gopacket_time_usecs_t __suseconds_t +#else // Some form of linux/bsd/etc... +#include +#ifdef __OpenBSD__ +#define gopacket_time_secs_t u_int32_t +#define gopacket_time_usecs_t u_int32_t +#else +#define gopacket_time_secs_t time_t +#define gopacket_time_usecs_t suseconds_t +#endif +#endif +*/ +import "C" + +import ( + "errors" + "fmt" + "io" + "net" + "reflect" + "runtime" + "strconv" + "sync" + "sync/atomic" + "syscall" + "time" + "unsafe" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +const errorBufferSize = 256 + +// MaxBpfInstructions is the maximum number of BPF instructions supported (BPF_MAXINSNS), +// taken from Linux kernel: include/uapi/linux/bpf_common.h +// +// https://github.com/torvalds/linux/blob/master/include/uapi/linux/bpf_common.h +const MaxBpfInstructions = 4096 + +// 8 bytes per instruction, max 4096 instructions +const bpfInstructionBufferSize = 8 * MaxBpfInstructions + +// Handle provides a connection to a pcap handle, allowing users to read packets +// off the wire (Next), inject packets onto the wire (Inject), and +// perform a number of other functions to affect and understand packet output. +// +// Handles are already pcap_activate'd +type Handle struct { + // cptr is the handle for the actual pcap C object. + cptr *C.pcap_t + timeout time.Duration + device string + deviceIndex int + mu sync.Mutex + closeMu sync.Mutex + // stop is set to a non-zero value by Handle.Close to signal to + // getNextBufPtrLocked to stop trying to read packets + stop uint64 + + // Since pointers to these objects are passed into a C function, if + // they're declared locally then the Go compiler thinks they may have + // escaped into C-land, so it allocates them on the heap. This causes a + // huge memory hit, so to handle that we store them here instead. + pkthdr *C.struct_pcap_pkthdr + bufptr *C.u_char +} + +// Stats contains statistics on how many packets were handled by a pcap handle, +// and what was done with those packets. +type Stats struct { + PacketsReceived int + PacketsDropped int + PacketsIfDropped int +} + +// Interface describes a single network interface on a machine. +type Interface struct { + Name string + Description string + Addresses []InterfaceAddress + // TODO: add more elements +} + +// Datalink describes the datalink +type Datalink struct { + Name string + Description string +} + +// InterfaceAddress describes an address associated with an Interface. +// Currently, it's IPv4/6 specific. +type InterfaceAddress struct { + IP net.IP + Netmask net.IPMask // Netmask may be nil if we were unable to retrieve it. + // TODO: add broadcast + PtP dst ? +} + +// BPF is a compiled filter program, useful for offline packet matching. +type BPF struct { + orig string + bpf _Ctype_struct_bpf_program // takes a finalizer, not overriden by outsiders +} + +// BPFInstruction is a byte encoded structure holding a BPF instruction +type BPFInstruction struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +// BlockForever causes it to block forever waiting for packets, when passed +// into SetTimeout or OpenLive, while still returning incoming packets to userland relatively +// quickly. +const BlockForever = -time.Millisecond * 10 + +func timeoutMillis(timeout time.Duration) C.int { + // Flip sign if necessary. See package docs on timeout for reasoning behind this. + if timeout < 0 { + timeout *= -1 + } + // Round up + if timeout != 0 && timeout < time.Millisecond { + timeout = time.Millisecond + } + return C.int(timeout / time.Millisecond) +} + +// OpenLive opens a device and returns a *Handle. +// It takes as arguments the name of the device ("eth0"), the maximum size to +// read for each packet (snaplen), whether to put the interface in promiscuous +// mode, and a timeout. +// +// See the package documentation for important details regarding 'timeout'. +func OpenLive(device string, snaplen int32, promisc bool, timeout time.Duration) (handle *Handle, _ error) { + buf := (*C.char)(C.calloc(errorBufferSize, 1)) + defer C.free(unsafe.Pointer(buf)) + + var pro C.int + if promisc { + pro = 1 + } + p := &Handle{timeout: timeout, device: device} + + ifc, err := net.InterfaceByName(device) + if err != nil { + // The device wasn't found in the OS, but could be "any" + // Set index to 0 + p.deviceIndex = 0 + } else { + p.deviceIndex = ifc.Index + } + + dev := C.CString(device) + defer C.free(unsafe.Pointer(dev)) + + p.cptr = C.pcap_open_live(dev, C.int(snaplen), pro, timeoutMillis(timeout), buf) + if p.cptr == nil { + return nil, errors.New(C.GoString(buf)) + } + + if err := p.openLive(); err != nil { + C.pcap_close(p.cptr) + return nil, err + } + + return p, nil +} + +// OpenOffline opens a file and returns its contents as a *Handle. +func OpenOffline(file string) (handle *Handle, err error) { + buf := (*C.char)(C.calloc(errorBufferSize, 1)) + defer C.free(unsafe.Pointer(buf)) + cf := C.CString(file) + defer C.free(unsafe.Pointer(cf)) + + cptr := C.pcap_open_offline(cf, buf) + if cptr == nil { + return nil, errors.New(C.GoString(buf)) + } + return &Handle{cptr: cptr}, nil +} + +// NextError is the return code from a call to Next. +type NextError int32 + +// NextError implements the error interface. +func (n NextError) Error() string { + switch n { + case NextErrorOk: + return "OK" + case NextErrorTimeoutExpired: + return "Timeout Expired" + case NextErrorReadError: + return "Read Error" + case NextErrorNoMorePackets: + return "No More Packets In File" + case NextErrorNotActivated: + return "Not Activated" + } + return strconv.Itoa(int(n)) +} + +// NextError values. +const ( + NextErrorOk NextError = 1 + NextErrorTimeoutExpired NextError = 0 + NextErrorReadError NextError = -1 + // NextErrorNoMorePackets is returned when reading from a file (OpenOffline) and + // EOF is reached. When this happens, Next() returns io.EOF instead of this. + NextErrorNoMorePackets NextError = -2 + NextErrorNotActivated NextError = -3 +) + +// ReadPacketData returns the next packet read from the pcap handle, along with an error +// code associated with that packet. If the packet is read successfully, the +// returned error is nil. +func (p *Handle) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + p.mu.Lock() + err = p.getNextBufPtrLocked(&ci) + if err == nil { + data = C.GoBytes(unsafe.Pointer(p.bufptr), C.int(ci.CaptureLength)) + } + p.mu.Unlock() + if err == NextErrorTimeoutExpired { + runtime.Gosched() + } + return +} + +type activateError C.int + +const ( + aeNoError = 0 + aeActivated = C.PCAP_ERROR_ACTIVATED + aePromisc = C.PCAP_WARNING_PROMISC_NOTSUP + aeNoSuchDevice = C.PCAP_ERROR_NO_SUCH_DEVICE + aeDenied = C.PCAP_ERROR_PERM_DENIED + aeNotUp = C.PCAP_ERROR_IFACE_NOT_UP +) + +func (a activateError) Error() string { + switch a { + case aeNoError: + return "No Error" + case aeActivated: + return "Already Activated" + case aePromisc: + return "Cannot set as promisc" + case aeNoSuchDevice: + return "No Such Device" + case aeDenied: + return "Permission Denied" + case aeNotUp: + return "Interface Not Up" + default: + return fmt.Sprintf("unknown activated error: %d", a) + } +} + +// getNextBufPtrLocked is shared code for ReadPacketData and +// ZeroCopyReadPacketData. +func (p *Handle) getNextBufPtrLocked(ci *gopacket.CaptureInfo) error { + if p.cptr == nil { + return io.EOF + } + + for atomic.LoadUint64(&p.stop) == 0 { + // try to read a packet if one is immediately available + result := NextError(C.pcap_next_ex(p.cptr, &p.pkthdr, &p.bufptr)) + + switch result { + case NextErrorOk: + // got a packet, set capture info and return + sec := int64(p.pkthdr.ts.tv_sec) + // convert micros to nanos + nanos := int64(p.pkthdr.ts.tv_usec) * 1000 + + ci.Timestamp = time.Unix(sec, nanos) + ci.CaptureLength = int(p.pkthdr.caplen) + ci.Length = int(p.pkthdr.len) + ci.InterfaceIndex = p.deviceIndex + + return nil + case NextErrorNoMorePackets: + // no more packets, return EOF rather than libpcap-specific error + return io.EOF + case NextErrorTimeoutExpired: + // Negative timeout means to loop forever, instead of actually returning + // the timeout error. + if p.timeout < 0 { + // must have had a timeout... wait before trying again + p.waitForPacket() + continue + } + default: + return result + } + } + + // stop must be set + return io.EOF +} + +// ZeroCopyReadPacketData reads the next packet off the wire, and returns its data. +// The slice returned by ZeroCopyReadPacketData points to bytes owned by the +// the Handle. Each call to ZeroCopyReadPacketData invalidates any data previously +// returned by ZeroCopyReadPacketData. Care must be taken not to keep pointers +// to old bytes when using ZeroCopyReadPacketData... if you need to keep data past +// the next time you call ZeroCopyReadPacketData, use ReadPacketData, which copies +// the bytes into a new buffer for you. +// data1, _, _ := handle.ZeroCopyReadPacketData() +// // do everything you want with data1 here, copying bytes out of it if you'd like to keep them around. +// data2, _, _ := handle.ZeroCopyReadPacketData() // invalidates bytes in data1 +func (p *Handle) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + p.mu.Lock() + err = p.getNextBufPtrLocked(&ci) + if err == nil { + slice := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + slice.Data = uintptr(unsafe.Pointer(p.bufptr)) + slice.Len = ci.CaptureLength + slice.Cap = ci.CaptureLength + } + p.mu.Unlock() + if err == NextErrorTimeoutExpired { + runtime.Gosched() + } + return +} + +// Close closes the underlying pcap handle. +func (p *Handle) Close() { + p.closeMu.Lock() + defer p.closeMu.Unlock() + + if p.cptr == nil { + return + } + + atomic.StoreUint64(&p.stop, 1) + + // wait for packet reader to stop + p.mu.Lock() + defer p.mu.Unlock() + + C.pcap_close(p.cptr) + p.cptr = nil +} + +// Error returns the current error associated with a pcap handle (pcap_geterr). +func (p *Handle) Error() error { + return errors.New(C.GoString(C.pcap_geterr(p.cptr))) +} + +// Stats returns statistics on the underlying pcap handle. +func (p *Handle) Stats() (stat *Stats, err error) { + var cstats _Ctype_struct_pcap_stat + if -1 == C.pcap_stats(p.cptr, &cstats) { + return nil, p.Error() + } + return &Stats{ + PacketsReceived: int(cstats.ps_recv), + PacketsDropped: int(cstats.ps_drop), + PacketsIfDropped: int(cstats.ps_ifdrop), + }, nil +} + +// ListDataLinks obtains a list of all possible data link types supported for an interface. +func (p *Handle) ListDataLinks() (datalinks []Datalink, err error) { + var dltbuf *C.int + + n := int(C.pcap_list_datalinks(p.cptr, &dltbuf)) + if -1 == n { + return nil, p.Error() + } + + defer C.pcap_free_datalinks(dltbuf) + + datalinks = make([]Datalink, n) + + dltArray := (*[100]C.int)(unsafe.Pointer(dltbuf)) + + for i := 0; i < n; i++ { + expr := C.pcap_datalink_val_to_name((*dltArray)[i]) + datalinks[i].Name = C.GoString(expr) + + expr = C.pcap_datalink_val_to_description((*dltArray)[i]) + datalinks[i].Description = C.GoString(expr) + } + + return datalinks, nil +} + +// pcap_compile is NOT thread-safe, so protect it. +var pcapCompileMu sync.Mutex + +// compileBPFFilter always returns an allocated _Ctype_struct_bpf_program +// It is the callers responsibility to free the memory again, e.g. +// +// C.pcap_freecode(&bpf) +// +func (p *Handle) compileBPFFilter(expr string) (_Ctype_struct_bpf_program, error) { + errorBuf := (*C.char)(C.calloc(errorBufferSize, 1)) + defer C.free(unsafe.Pointer(errorBuf)) + + var netp uint32 + var maskp uint32 + + // Only do the lookup on network interfaces. + // No device indicates we're handling a pcap file. + if len(p.device) > 0 { + dev := C.CString(p.device) + defer C.free(unsafe.Pointer(dev)) + if -1 == C.pcap_lookupnet( + dev, + (*C.bpf_u_int32)(unsafe.Pointer(&netp)), + (*C.bpf_u_int32)(unsafe.Pointer(&maskp)), + errorBuf, + ) { + // We can't lookup the network, but that could be because the interface + // doesn't have an IPv4. + } + } + + var bpf _Ctype_struct_bpf_program + cexpr := C.CString(expr) + defer C.free(unsafe.Pointer(cexpr)) + + pcapCompileMu.Lock() + defer pcapCompileMu.Unlock() + if -1 == C.pcap_compile(p.cptr, &bpf, cexpr, 1, C.bpf_u_int32(maskp)) { + return bpf, p.Error() + } + + return bpf, nil +} + +// CompileBPFFilter compiles and returns a BPF filter with given a link type and capture length. +func CompileBPFFilter(linkType layers.LinkType, captureLength int, expr string) ([]BPFInstruction, error) { + cptr := C.pcap_open_dead(C.int(linkType), C.int(captureLength)) + if cptr == nil { + return nil, errors.New("error opening dead capture") + } + + h := Handle{cptr: cptr} + defer h.Close() + return h.CompileBPFFilter(expr) +} + +// CompileBPFFilter compiles and returns a BPF filter for the pcap handle. +func (p *Handle) CompileBPFFilter(expr string) ([]BPFInstruction, error) { + bpf, err := p.compileBPFFilter(expr) + defer C.pcap_freecode(&bpf) + if err != nil { + return nil, err + } + + bpfInsn := (*[bpfInstructionBufferSize]_Ctype_struct_bpf_insn)(unsafe.Pointer(bpf.bf_insns))[0:bpf.bf_len:bpf.bf_len] + bpfInstruction := make([]BPFInstruction, len(bpfInsn), len(bpfInsn)) + + for i, v := range bpfInsn { + bpfInstruction[i].Code = uint16(v.code) + bpfInstruction[i].Jt = uint8(v.jt) + bpfInstruction[i].Jf = uint8(v.jf) + bpfInstruction[i].K = uint32(v.k) + } + + return bpfInstruction, nil +} + +// SetBPFFilter compiles and sets a BPF filter for the pcap handle. +func (p *Handle) SetBPFFilter(expr string) (err error) { + bpf, err := p.compileBPFFilter(expr) + defer C.pcap_freecode(&bpf) + if err != nil { + return err + } + + if -1 == C.pcap_setfilter(p.cptr, &bpf) { + return p.Error() + } + + return nil +} + +// SetBPFInstructionFilter may be used to apply a filter in BPF asm byte code format. +// +// Simplest way to generate BPF asm byte code is with tcpdump: +// tcpdump -dd 'udp' +// +// The output may be used directly to add a filter, e.g.: +// bpfInstructions := []pcap.BpfInstruction{ +// {0x28, 0, 0, 0x0000000c}, +// {0x15, 0, 9, 0x00000800}, +// {0x30, 0, 0, 0x00000017}, +// {0x15, 0, 7, 0x00000006}, +// {0x28, 0, 0, 0x00000014}, +// {0x45, 5, 0, 0x00001fff}, +// {0xb1, 0, 0, 0x0000000e}, +// {0x50, 0, 0, 0x0000001b}, +// {0x54, 0, 0, 0x00000012}, +// {0x15, 0, 1, 0x00000012}, +// {0x6, 0, 0, 0x0000ffff}, +// {0x6, 0, 0, 0x00000000}, +// } +// +// An other posibility is to write the bpf code in bpf asm. +// Documentation: https://www.kernel.org/doc/Documentation/networking/filter.txt +// +// To compile the code use bpf_asm from +// https://github.com/torvalds/linux/tree/master/tools/net +// +// The following command may be used to convert bpf_asm output to c/go struct, usable for SetBPFFilterByte: +// bpf_asm -c tcp.bpf +func (p *Handle) SetBPFInstructionFilter(bpfInstructions []BPFInstruction) (err error) { + bpf, err := bpfInstructionFilter(bpfInstructions) + if err != nil { + return err + } + + if -1 == C.pcap_setfilter(p.cptr, &bpf) { + C.pcap_freecode(&bpf) + return p.Error() + } + + C.pcap_freecode(&bpf) + + return nil +} +func bpfInstructionFilter(bpfInstructions []BPFInstruction) (bpf _Ctype_struct_bpf_program, err error) { + if len(bpfInstructions) < 1 { + return bpf, errors.New("bpfInstructions must not be empty") + } + + if len(bpfInstructions) > MaxBpfInstructions { + return bpf, fmt.Errorf("bpfInstructions must not be larger than %d", MaxBpfInstructions) + } + + bpf.bf_len = C.u_int(len(bpfInstructions)) + cbpfInsns := C.calloc(C.size_t(len(bpfInstructions)), C.size_t(unsafe.Sizeof(bpfInstructions[0]))) + + copy((*[bpfInstructionBufferSize]BPFInstruction)(cbpfInsns)[0:len(bpfInstructions)], bpfInstructions) + bpf.bf_insns = (*_Ctype_struct_bpf_insn)(cbpfInsns) + + return +} + +// NewBPF compiles the given string into a new filter program. +// +// BPF filters need to be created from activated handles, because they need to +// know the underlying link type to correctly compile their offsets. +func (p *Handle) NewBPF(expr string) (*BPF, error) { + bpf := &BPF{orig: expr} + cexpr := C.CString(expr) + defer C.free(unsafe.Pointer(cexpr)) + + pcapCompileMu.Lock() + defer pcapCompileMu.Unlock() + if C.pcap_compile(p.cptr, &bpf.bpf, cexpr /* optimize */, 1, C.PCAP_NETMASK_UNKNOWN) != 0 { + return nil, p.Error() + } + + runtime.SetFinalizer(bpf, destroyBPF) + return bpf, nil +} + +// NewBPFInstructionFilter sets the given BPFInstructions as new filter program. +// +// More details see func SetBPFInstructionFilter +// +// BPF filters need to be created from activated handles, because they need to +// know the underlying link type to correctly compile their offsets. +func (p *Handle) NewBPFInstructionFilter(bpfInstructions []BPFInstruction) (*BPF, error) { + var err error + bpf := &BPF{orig: "BPF Instruction Filter"} + + bpf.bpf, err = bpfInstructionFilter(bpfInstructions) + if err != nil { + return nil, err + } + + runtime.SetFinalizer(bpf, destroyBPF) + return bpf, nil +} +func destroyBPF(bpf *BPF) { + C.pcap_freecode(&bpf.bpf) +} + +// String returns the original string this BPF filter was compiled from. +func (b *BPF) String() string { + return b.orig +} + +// Matches returns true if the given packet data matches this filter. +func (b *BPF) Matches(ci gopacket.CaptureInfo, data []byte) bool { + var hdr C.struct_pcap_pkthdr + hdr.ts.tv_sec = C.gopacket_time_secs_t(ci.Timestamp.Unix()) + hdr.ts.tv_usec = C.gopacket_time_usecs_t(ci.Timestamp.Nanosecond() / 1000) + hdr.caplen = C.bpf_u_int32(len(data)) // Trust actual length over ci.Length. + hdr.len = C.bpf_u_int32(ci.Length) + dataptr := (*C.u_char)(unsafe.Pointer(&data[0])) + return C.pcap_offline_filter(&b.bpf, &hdr, dataptr) != 0 +} + +// Version returns pcap_lib_version. +func Version() string { + return C.GoString(C.pcap_lib_version()) +} + +// LinkType returns pcap_datalink, as a layers.LinkType. +func (p *Handle) LinkType() layers.LinkType { + return layers.LinkType(C.pcap_datalink(p.cptr)) +} + +// SetLinkType calls pcap_set_datalink on the pcap handle. +func (p *Handle) SetLinkType(dlt layers.LinkType) error { + if -1 == C.pcap_set_datalink(p.cptr, C.int(dlt)) { + return p.Error() + } + return nil +} + +// FindAllDevs attempts to enumerate all interfaces on the current machine. +func FindAllDevs() (ifs []Interface, err error) { + var buf *C.char + buf = (*C.char)(C.calloc(errorBufferSize, 1)) + defer C.free(unsafe.Pointer(buf)) + var alldevsp *C.pcap_if_t + + if -1 == C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp), buf) { + return nil, errors.New(C.GoString(buf)) + } + defer C.pcap_freealldevs((*C.pcap_if_t)(alldevsp)) + dev := alldevsp + var i uint32 + for i = 0; dev != nil; dev = (*C.pcap_if_t)(dev.next) { + i++ + } + ifs = make([]Interface, i) + dev = alldevsp + for j := uint32(0); dev != nil; dev = (*C.pcap_if_t)(dev.next) { + var iface Interface + iface.Name = C.GoString(dev.name) + iface.Description = C.GoString(dev.description) + iface.Addresses = findalladdresses(dev.addresses) + // TODO: add more elements + ifs[j] = iface + j++ + } + return +} + +func findalladdresses(addresses *_Ctype_struct_pcap_addr) (retval []InterfaceAddress) { + // TODO - make it support more than IPv4 and IPv6? + retval = make([]InterfaceAddress, 0, 1) + for curaddr := addresses; curaddr != nil; curaddr = (*_Ctype_struct_pcap_addr)(curaddr.next) { + // Strangely, it appears that in some cases, we get a pcap address back from + // pcap_findalldevs with a nil .addr. It appears that we can skip over + // these. + if curaddr.addr == nil { + continue + } + var a InterfaceAddress + var err error + if a.IP, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.addr))); err != nil { + continue + } + // To be safe, we'll also check for netmask. + if curaddr.netmask == nil { + continue + } + if a.Netmask, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.netmask))); err != nil { + // If we got an IP address but we can't get a netmask, just return the IP + // address. + a.Netmask = nil + } + retval = append(retval, a) + } + return +} + +func sockaddrToIP(rsa *syscall.RawSockaddr) (IP []byte, err error) { + switch rsa.Family { + case syscall.AF_INET: + pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) + IP = make([]byte, 4) + for i := 0; i < len(IP); i++ { + IP[i] = pp.Addr[i] + } + return + case syscall.AF_INET6: + pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) + IP = make([]byte, 16) + for i := 0; i < len(IP); i++ { + IP[i] = pp.Addr[i] + } + return + } + err = errors.New("Unsupported address type") + return +} + +// WritePacketData calls pcap_sendpacket, injecting the given data into the pcap handle. +func (p *Handle) WritePacketData(data []byte) (err error) { + if -1 == C.pcap_sendpacket(p.cptr, (*C.u_char)(&data[0]), (C.int)(len(data))) { + err = p.Error() + } + return +} + +// Direction is used by Handle.SetDirection. +type Direction uint8 + +// Direction values for Handle.SetDirection. +const ( + DirectionIn Direction = C.PCAP_D_IN + DirectionOut Direction = C.PCAP_D_OUT + DirectionInOut Direction = C.PCAP_D_INOUT +) + +// SetDirection sets the direction for which packets will be captured. +func (p *Handle) SetDirection(direction Direction) error { + if direction != DirectionIn && direction != DirectionOut && direction != DirectionInOut { + return fmt.Errorf("Invalid direction: %v", direction) + } + if status := C.pcap_setdirection(p.cptr, (C.pcap_direction_t)(direction)); status < 0 { + return statusError(status) + } + return nil +} + +// TimestampSource tells PCAP which type of timestamp to use for packets. +type TimestampSource C.int + +// String returns the timestamp type as a human-readable string. +func (t TimestampSource) String() string { + return C.GoString(C.pcap_tstamp_type_val_to_name(C.int(t))) +} + +// TimestampSourceFromString translates a string into a timestamp type, case +// insensitive. +func TimestampSourceFromString(s string) (TimestampSource, error) { + cs := C.CString(s) + defer C.free(unsafe.Pointer(cs)) + t := C.pcap_tstamp_type_name_to_val(cs) + if t < 0 { + return 0, statusError(t) + } + return TimestampSource(t), nil +} + +func statusError(status C.int) error { + return errors.New(C.GoString(C.pcap_statustostr(status))) +} + +// InactiveHandle allows you to call pre-pcap_activate functions on your pcap +// handle to set it up just the way you'd like. +type InactiveHandle struct { + // cptr is the handle for the actual pcap C object. + cptr *C.pcap_t + device string + deviceIndex int + timeout time.Duration +} + +// Activate activates the handle. The current InactiveHandle becomes invalid +// and all future function calls on it will fail. +func (p *InactiveHandle) Activate() (*Handle, error) { + err := activateError(C.pcap_activate(p.cptr)) + if err != aeNoError { + return nil, err + } + h := &Handle{ + cptr: p.cptr, + timeout: p.timeout, + device: p.device, + deviceIndex: p.deviceIndex, + } + p.cptr = nil + return h, nil +} + +// CleanUp cleans up any stuff left over from a successful or failed building +// of a handle. +func (p *InactiveHandle) CleanUp() { + if p.cptr != nil { + C.pcap_close(p.cptr) + } +} + +// NewInactiveHandle creates a new InactiveHandle, which wraps an un-activated PCAP handle. +// Callers of NewInactiveHandle should immediately defer 'CleanUp', as in: +// inactive := NewInactiveHandle("eth0") +// defer inactive.CleanUp() +func NewInactiveHandle(device string) (*InactiveHandle, error) { + buf := (*C.char)(C.calloc(errorBufferSize, 1)) + defer C.free(unsafe.Pointer(buf)) + dev := C.CString(device) + defer C.free(unsafe.Pointer(dev)) + + // Try to get the interface index, but iy could be something like "any" + // in which case use 0, which doesn't exist in nature + deviceIndex := 0 + ifc, err := net.InterfaceByName(device) + if err == nil { + deviceIndex = ifc.Index + } + + // This copies a bunch of the pcap_open_live implementation from pcap.c: + cptr := C.pcap_create(dev, buf) + if cptr == nil { + return nil, errors.New(C.GoString(buf)) + } + return &InactiveHandle{cptr: cptr, device: device, deviceIndex: deviceIndex}, nil +} + +// SetSnapLen sets the snap length (max bytes per packet to capture). +func (p *InactiveHandle) SetSnapLen(snaplen int) error { + if status := C.pcap_set_snaplen(p.cptr, C.int(snaplen)); status < 0 { + return statusError(status) + } + return nil +} + +// SetPromisc sets the handle to either be promiscuous (capture packets +// unrelated to this host) or not. +func (p *InactiveHandle) SetPromisc(promisc bool) error { + var pro C.int + if promisc { + pro = 1 + } + if status := C.pcap_set_promisc(p.cptr, pro); status < 0 { + return statusError(status) + } + return nil +} + +// SetTimeout sets the read timeout for the handle. +// +// See the package documentation for important details regarding 'timeout'. +func (p *InactiveHandle) SetTimeout(timeout time.Duration) error { + if status := C.pcap_set_timeout(p.cptr, timeoutMillis(timeout)); status < 0 { + return statusError(status) + } + p.timeout = timeout + return nil +} + +// SupportedTimestamps returns a list of supported timstamp types for this +// handle. +func (p *InactiveHandle) SupportedTimestamps() (out []TimestampSource) { + var types *C.int + n := int(C.pcap_list_tstamp_types(p.cptr, &types)) + defer C.pcap_free_tstamp_types(types) + typesArray := (*[100]C.int)(unsafe.Pointer(types)) + for i := 0; i < n; i++ { + out = append(out, TimestampSource((*typesArray)[i])) + } + return +} + +// SetTimestampSource sets the type of timestamp generator PCAP uses when +// attaching timestamps to packets. +func (p *InactiveHandle) SetTimestampSource(t TimestampSource) error { + if status := C.pcap_set_tstamp_type(p.cptr, C.int(t)); status < 0 { + return statusError(status) + } + return nil +} + +// CannotSetRFMon is returned by SetRFMon if the handle does not allow +// setting RFMon because pcap_can_set_rfmon returns 0. +var CannotSetRFMon = errors.New("Cannot set rfmon for this handle") + +// SetRFMon turns on radio monitoring mode, similar to promiscuous mode but for +// wireless networks. If this mode is enabled, the interface will not need to +// associate with an access point before it can receive traffic. +func (p *InactiveHandle) SetRFMon(monitor bool) error { + var mon C.int + if monitor { + mon = 1 + } + switch canset := C.pcap_can_set_rfmon(p.cptr); canset { + case 0: + return CannotSetRFMon + case 1: + // success + default: + return statusError(canset) + } + if status := C.pcap_set_rfmon(p.cptr, mon); status != 0 { + return statusError(status) + } + return nil +} + +// SetBufferSize sets the buffer size (in bytes) of the handle. +func (p *InactiveHandle) SetBufferSize(bufferSize int) error { + if status := C.pcap_set_buffer_size(p.cptr, C.int(bufferSize)); status < 0 { + return statusError(status) + } + return nil +} + +// SetImmediateMode sets (or unsets) the immediate mode of the +// handle. In immediate mode, packets are delivered to the application +// as soon as they arrive. In other words, this overrides SetTimeout. +func (p *InactiveHandle) SetImmediateMode(mode bool) error { + var md C.int + if mode { + md = 1 + } + if status := C.pcap_set_immediate_mode(p.cptr, md); status < 0 { + return statusError(status) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/pcap/pcap_test.go b/vendor/github.com/google/gopacket/pcap/pcap_test.go new file mode 100644 index 00000000..bb1f1a8a --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/pcap_test.go @@ -0,0 +1,276 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcap + +import ( + "fmt" + "io" + "log" + "testing" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +func TestPcapNonexistentFile(t *testing.T) { + handle, err := OpenOffline("/path/to/nonexistent/file") + if err == nil { + t.Error("No error returned for nonexistent file open") + } else { + t.Logf("Error returned for nonexistent file: %v", err) + } + if handle != nil { + t.Error("Non-nil handle returned for nonexistent file open") + } +} + +func TestPcapFileRead(t *testing.T) { + for _, file := range []struct { + filename string + num int + expectedLayers []gopacket.LayerType + }{ + {"test_loopback.pcap", + 24, + []gopacket.LayerType{ + layers.LayerTypeLoopback, + layers.LayerTypeIPv6, + layers.LayerTypeTCP, + }, + }, + {"test_ethernet.pcap", + 16, + []gopacket.LayerType{ + layers.LayerTypeEthernet, + layers.LayerTypeIPv4, + layers.LayerTypeTCP, + }, + }, + {"test_dns.pcap", + 10, + []gopacket.LayerType{ + layers.LayerTypeEthernet, + layers.LayerTypeIPv4, + layers.LayerTypeUDP, + layers.LayerTypeDNS, + }, + }, + } { + t.Logf("\n\n\n\nProcessing file %s\n\n\n\n", file.filename) + + packets := []gopacket.Packet{} + if handle, err := OpenOffline(file.filename); err != nil { + t.Fatal(err) + } else { + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + for packet := range packetSource.Packets() { + packets = append(packets, packet) + } + } + if len(packets) != file.num { + t.Fatal("Incorrect number of packets, want", file.num, "got", len(packets)) + } + for i, p := range packets { + t.Log(p.Dump()) + for _, layertype := range file.expectedLayers { + if p.Layer(layertype) == nil { + t.Fatal("Packet", i, "has no layer type\n%s", layertype, p.Dump()) + } + } + } + } +} + +func TestBPF(t *testing.T) { + handle, err := OpenOffline("test_ethernet.pcap") + if err != nil { + t.Fatal(err) + } + + for _, expected := range []struct { + expr string + Error bool + Result bool + }{ + {"foobar", true, false}, + {"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)", false, true}, + {"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack", false, true}, + {"udp", false, false}, + } { + data, ci, err := handle.ReadPacketData() + if err != nil { + t.Fatal(err) + } + t.Log("Testing filter", expected.expr) + if bpf, err := handle.NewBPF(expected.expr); err != nil { + if !expected.Error { + t.Error(err, "while compiling filter was unexpected") + } + } else if expected.Error { + t.Error("expected error but didn't see one") + } else if matches := bpf.Matches(ci, data); matches != expected.Result { + t.Error("Filter result was", matches, "but should be", expected.Result) + } + } +} + +func TestBPFInstruction(t *testing.T) { + handle, err := OpenOffline("test_ethernet.pcap") + if err != nil { + t.Fatal(err) + } + + cntr := 0 + oversizedBpfInstructionBuffer := [MaxBpfInstructions + 1]BPFInstruction{} + + for _, expected := range []struct { + Filter string + BpfInstruction []BPFInstruction + Error bool + Result bool + }{ + // {"foobar", true, false}, + {"foobar", []BPFInstruction{}, true, false}, + + // tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)' + {"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)", + []BPFInstruction{ + {0x28, 0, 0, 0x0000000c}, + {0x15, 0, 9, 0x00000800}, + {0x30, 0, 0, 0x00000017}, + {0x15, 0, 7, 0x00000006}, + {0x28, 0, 0, 0x00000014}, + {0x45, 5, 0, 0x00001fff}, + {0xb1, 0, 0, 0x0000000e}, + {0x50, 0, 0, 0x0000001b}, + {0x54, 0, 0, 0x00000012}, + {0x15, 0, 1, 0x00000012}, + {0x6, 0, 0, 0x0000ffff}, + {0x6, 0, 0, 0x00000000}, + }, false, true}, + + // tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack' + {"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack", + []BPFInstruction{ + {0x28, 0, 0, 0x0000000c}, + {0x15, 0, 9, 0x00000800}, + {0x30, 0, 0, 0x00000017}, + {0x15, 0, 7, 0x00000006}, + {0x28, 0, 0, 0x00000014}, + {0x45, 5, 0, 0x00001fff}, + {0xb1, 0, 0, 0x0000000e}, + {0x50, 0, 0, 0x0000001b}, + {0x54, 0, 0, 0x00000012}, + {0x15, 0, 1, 0x00000010}, + {0x6, 0, 0, 0x0000ffff}, + {0x6, 0, 0, 0x00000000}, + }, false, true}, + + // tcpdump -dd 'udp' + {"udp", + []BPFInstruction{ + {0x28, 0, 0, 0x0000000c}, + {0x15, 0, 5, 0x000086dd}, + {0x30, 0, 0, 0x00000014}, + {0x15, 6, 0, 0x00000011}, + {0x15, 0, 6, 0x0000002c}, + {0x30, 0, 0, 0x00000036}, + {0x15, 3, 4, 0x00000011}, + {0x15, 0, 3, 0x00000800}, + {0x30, 0, 0, 0x00000017}, + {0x15, 0, 1, 0x00000011}, + {0x6, 0, 0, 0x0000ffff}, + {0x6, 0, 0, 0x00000000}, + }, false, false}, + + {"", oversizedBpfInstructionBuffer[:], true, false}, + } { + cntr++ + data, ci, err := handle.ReadPacketData() + if err != nil { + t.Fatal(err) + } + + t.Log("Testing BpfInstruction filter", cntr) + if bpf, err := handle.NewBPFInstructionFilter(expected.BpfInstruction); err != nil { + if !expected.Error { + t.Error(err, "while compiling filter was unexpected") + } + } else if expected.Error { + t.Error("expected error but didn't see one") + } else if matches := bpf.Matches(ci, data); matches != expected.Result { + t.Error("Filter result was", matches, "but should be", expected.Result) + } + + if expected.Filter != "" { + t.Log("Testing dead bpf filter", cntr) + if bpf, err := CompileBPFFilter(layers.LinkTypeEthernet, 65535, expected.Filter); err != nil { + if !expected.Error { + t.Error(err, "while compiling filter was unexpected") + } + } else if expected.Error { + t.Error("expected error but didn't see one") + } else { + if len(bpf) != len(expected.BpfInstruction) { + t.Errorf("expected %d instructions, got %d", len(expected.BpfInstruction), len(bpf)) + } + for i := 0; i < len(bpf); i++ { + if bpf[i] != expected.BpfInstruction[i] { + t.Errorf("expected instruction %d = %d, got %d", i, expected.BpfInstruction[i], bpf[i]) + } + } + } + } + } +} + +func ExampleBPF() { + handle, err := OpenOffline("test_ethernet.pcap") + if err != nil { + log.Fatal(err) + } + synack, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)") + if err != nil { + log.Fatal(err) + } + syn, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn") + if err != nil { + log.Fatal(err) + } + for { + data, ci, err := handle.ReadPacketData() + switch { + case err == io.EOF: + return + case err != nil: + log.Fatal(err) + case synack.Matches(ci, data): + fmt.Println("SYN/ACK packet") + case syn.Matches(ci, data): + fmt.Println("SYN packet") + default: + fmt.Println("SYN flag not set") + } + } + // Output: + // SYN packet + // SYN/ACK packet + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set + // SYN flag not set +} diff --git a/vendor/github.com/google/gopacket/pcap/pcap_tester.go b/vendor/github.com/google/gopacket/pcap/pcap_tester.go new file mode 100644 index 00000000..ee326908 --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/pcap_tester.go @@ -0,0 +1,109 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build ignore + +// This binary tests that PCAP packet capture is working correctly by issuing +// HTTP requests, then making sure we actually capture data off the wire. +package main + +import ( + "errors" + "flag" + "fmt" + "log" + "net" + "net/http" + "os" + "time" + + "github.com/google/gopacket/pcap" +) + +var mode = flag.String("mode", "basic", "One of: basic,filtered,timestamp") + +func generatePackets() { + if resp, err := http.Get("http://code.google.com"); err != nil { + log.Printf("Could not get HTTP: %v", err) + } else { + resp.Body.Close() + } +} + +func main() { + flag.Parse() + ifaces, err := net.Interfaces() + if err != nil { + log.Fatal(err) + } + for _, iface := range ifaces { + log.Printf("Trying capture on %q", iface.Name) + if err := tryCapture(iface); err != nil { + log.Printf("Error capturing on %q: %v", iface.Name, err) + } else { + log.Printf("Successfully captured on %q", iface.Name) + return + } + } + os.Exit(1) +} + +func tryCapture(iface net.Interface) error { + if iface.Name[:2] == "lo" { + return errors.New("skipping loopback") + } + var h *pcap.Handle + var err error + switch *mode { + case "basic": + h, err = pcap.OpenLive(iface.Name, 65536, false, time.Second*3) + if err != nil { + return fmt.Errorf("openlive: %v", err) + } + defer h.Close() + case "filtered": + h, err = pcap.OpenLive(iface.Name, 65536, false, time.Second*3) + if err != nil { + return fmt.Errorf("openlive: %v", err) + } + defer h.Close() + if err := h.SetBPFFilter("port 80 or port 443"); err != nil { + return fmt.Errorf("setbpf: %v", err) + } + case "timestamp": + u, err := pcap.NewInactiveHandle(iface.Name) + if err != nil { + return err + } + defer u.CleanUp() + if err = u.SetSnapLen(65536); err != nil { + return err + } else if err = u.SetPromisc(false); err != nil { + return err + } else if err = u.SetTimeout(time.Second * 3); err != nil { + return err + } + sources := u.SupportedTimestamps() + if len(sources) == 0 { + return errors.New("no supported timestamp sources") + } else if err := u.SetTimestampSource(sources[0]); err != nil { + return fmt.Errorf("settimestampsource(%v): %v", sources[0], err) + } else if h, err = u.Activate(); err != nil { + return fmt.Errorf("could not activate: %v", err) + } + defer h.Close() + default: + panic("Invalid --mode: " + *mode) + } + go generatePackets() + h.ReadPacketData() // Do one dummy read to clear any timeouts. + data, ci, err := h.ReadPacketData() + if err != nil { + return fmt.Errorf("readpacketdata: %v", err) + } + log.Printf("Read packet, %v bytes, CI: %+v", len(data), ci) + return nil +} diff --git a/vendor/github.com/google/gopacket/pcap/pcap_unix.go b/vendor/github.com/google/gopacket/pcap/pcap_unix.go new file mode 100644 index 00000000..b2a6dcd6 --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/pcap_unix.go @@ -0,0 +1,71 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +// +// +build !windows + +package pcap + +/* +#include +#include + +// pcap_wait returns when the next packet is available or the timeout expires. +// Since it uses pcap_get_selectable_fd, it will not work in Windows. +int pcap_wait(pcap_t *p, int usec) { + fd_set fds; + int fd; + struct timeval tv; + + fd = pcap_get_selectable_fd(p); + if(fd < 0) { + return fd; + } + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + tv.tv_sec = 0; + tv.tv_usec = usec; + + if(usec != 0) { + return select(fd+1, &fds, NULL, NULL, &tv); + } + + // block indefinitely if no timeout provided + return select(fd+1, &fds, NULL, NULL, NULL); +} +*/ +import "C" + +import ( + "errors" + "unsafe" +) + +func (p *Handle) openLive() error { + buf := (*C.char)(C.calloc(errorBufferSize, 1)) + defer C.free(unsafe.Pointer(buf)) + + // Change the device to non-blocking, we'll use pcap_wait to wait until the + // handle is ready to read. + if v := C.pcap_setnonblock(p.cptr, 1, buf); v == -1 { + return errors.New(C.GoString(buf)) + } + + return nil +} + +// waitForPacket waits for a packet or for the timeout to expire. +func (p *Handle) waitForPacket() { + // need to wait less than the read timeout according to pcap documentation. + // timeoutMillis rounds up to at least one millisecond so we can safely + // subtract up to a millisecond. + usec := timeoutMillis(p.timeout) * 1000 + usec -= 100 + + C.pcap_wait(p.cptr, usec) +} diff --git a/vendor/github.com/google/gopacket/pcap/pcap_windows.go b/vendor/github.com/google/gopacket/pcap/pcap_windows.go new file mode 100644 index 00000000..e3df1239 --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/pcap_windows.go @@ -0,0 +1,23 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcap + +import ( + "runtime" +) + +func (p *Handle) openLive() error { + // do nothing + return nil +} + +// waitForPacket waits for a packet or for the timeout to expire. +func (p *Handle) waitForPacket() { + // can't use select() so instead just switch goroutines + runtime.Gosched() +} diff --git a/vendor/github.com/google/gopacket/pcap/pcapgo_test.go b/vendor/github.com/google/gopacket/pcap/pcapgo_test.go new file mode 100644 index 00000000..4de018ae --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/pcapgo_test.go @@ -0,0 +1,56 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcap + +import ( + "bytes" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcapgo" + "io/ioutil" + "reflect" + "testing" + "time" +) + +func TestPCAPGoWrite(t *testing.T) { + f, err := ioutil.TempFile("", "pcapgo") + if err != nil { + t.Fatal(err) + } + data := []byte{0xab, 0xcd, 0xef, 0x01, 0x02, 0x03, 0x04} + ci := gopacket.CaptureInfo{ + Timestamp: time.Unix(12345667, 1234567000), + Length: 700, + CaptureLength: len(data), + } + func() { + defer f.Close() + w := pcapgo.NewWriter(f) + if err := w.WriteFileHeader(65536, layers.LinkTypeEthernet); err != nil { + t.Fatal(err) + } + if err := w.WritePacket(ci, data); err != nil { + t.Fatal(err) + } + }() + h, err := OpenOffline(f.Name()) + if err != nil { + t.Fatal(err) + } + defer h.Close() + gotData, gotCI, err := h.ReadPacketData() + if err != nil { + t.Fatal("could not read first packet:", err) + } + if !bytes.Equal(gotData, data) { + t.Errorf("byte mismatch:\nwant: %v\n got: %v", data, gotData) + } + if !reflect.DeepEqual(ci, gotCI) { + t.Errorf("CI mismatch:\nwant: %v\n got: %v", ci, gotCI) + } +} diff --git a/vendor/github.com/google/gopacket/pcap/test_dns.pcap b/vendor/github.com/google/gopacket/pcap/test_dns.pcap new file mode 100644 index 00000000..3a79f928 Binary files /dev/null and b/vendor/github.com/google/gopacket/pcap/test_dns.pcap differ diff --git a/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap b/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap new file mode 100644 index 00000000..1f8a87c3 Binary files /dev/null and b/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap differ diff --git a/vendor/github.com/google/gopacket/pcap/test_loopback.pcap b/vendor/github.com/google/gopacket/pcap/test_loopback.pcap new file mode 100644 index 00000000..ddeb82cd Binary files /dev/null and b/vendor/github.com/google/gopacket/pcap/test_loopback.pcap differ diff --git a/vendor/github.com/google/gopacket/pcapgo/read.go b/vendor/github.com/google/gopacket/pcapgo/read.go new file mode 100644 index 00000000..5acd06f3 --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/read.go @@ -0,0 +1,155 @@ +// Copyright 2014 Damjan Cvetko. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcapgo + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "time" + + "bufio" + "compress/gzip" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +// Reader wraps an underlying io.Reader to read packet data in PCAP +// format. See http://wiki.wireshark.org/Development/LibpcapFileFormat +// for information on the file format. +// +// We currenty read v2.4 file format with nanosecond and microsecdond +// timestamp resolution in little-endian and big-endian encoding. +// +// If the PCAP data is gzip compressed it is transparently uncompressed +// by wrapping the given io.Reader with a gzip.Reader. +type Reader struct { + r io.Reader + byteOrder binary.ByteOrder + nanoSecsFactor uint32 + versionMajor uint16 + versionMinor uint16 + // timezone + // sigfigs + snaplen uint32 + linkType layers.LinkType + // reusable buffer + buf [16]byte +} + +const magicNanoseconds = 0xA1B23C4D +const magicMicrosecondsBigendian = 0xD4C3B2A1 +const magicNanosecondsBigendian = 0x4D3CB2A1 + +const magicGzip1 = 0x1f +const magicGzip2 = 0x8b + +// NewReader returns a new reader object, for reading packet data from +// the given reader. The reader must be open and header data is +// read from it at this point. +// If the file format is not supported an error is returned +// +// // Create new reader: +// f, _ := os.Open("/tmp/file.pcap") +// defer f.Close() +// r, err := NewReader(f) +// data, ci, err := r.ReadPacketData() +func NewReader(r io.Reader) (*Reader, error) { + ret := Reader{r: r} + if err := ret.readHeader(); err != nil { + return nil, err + } + return &ret, nil +} + +func (r *Reader) readHeader() error { + br := bufio.NewReader(r.r) + gzipMagic, err := br.Peek(2) + if err != nil { + return err + } + + if gzipMagic[0] == magicGzip1 && gzipMagic[1] == magicGzip2 { + if r.r, err = gzip.NewReader(br); err != nil { + return err + } + } else { + r.r = br + } + + buf := make([]byte, 24) + if n, err := io.ReadFull(r.r, buf); err != nil { + return err + } else if n < 24 { + return errors.New("Not enough data for read") + } + if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds { + r.byteOrder = binary.LittleEndian + r.nanoSecsFactor = 1 + } else if magic == magicNanosecondsBigendian { + r.byteOrder = binary.BigEndian + r.nanoSecsFactor = 1 + } else if magic == magicMicroseconds { + r.byteOrder = binary.LittleEndian + r.nanoSecsFactor = 1000 + } else if magic == magicMicrosecondsBigendian { + r.byteOrder = binary.BigEndian + r.nanoSecsFactor = 1000 + } else { + return fmt.Errorf("Unknown magic %x", magic) + } + if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor { + return fmt.Errorf("Unknown major version %d", r.versionMajor) + } + if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor { + return fmt.Errorf("Unknown minor version %d", r.versionMinor) + } + // ignore timezone 8:12 and sigfigs 12:16 + r.snaplen = r.byteOrder.Uint32(buf[16:20]) + r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24])) + return nil +} + +// ReadPacketData reads next packet from file. +func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + if ci, err = r.readPacketHeader(); err != nil { + return + } + if ci.CaptureLength > int(r.snaplen) { + err = fmt.Errorf("capture length exceeds snap length: %d > %d", 16+ci.CaptureLength, r.snaplen) + return + } + data = make([]byte, ci.CaptureLength) + _, err = io.ReadFull(r.r, data) + return data, ci, err +} + +func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) { + if _, err = io.ReadFull(r.r, r.buf[:]); err != nil { + return + } + ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC() + ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12])) + ci.Length = int(r.byteOrder.Uint32(r.buf[12:16])) + return +} + +// LinkType returns network, as a layers.LinkType. +func (r *Reader) LinkType() layers.LinkType { + return r.linkType +} + +// Snaplen returns the snapshot length of the capture file. +func (r *Reader) Snaplen() uint32 { + return r.snaplen +} + +// Reader formater +func (r *Reader) String() string { + return fmt.Sprintf("PcapFile maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType) +} diff --git a/vendor/github.com/google/gopacket/pcapgo/read_test.go b/vendor/github.com/google/gopacket/pcapgo/read_test.go new file mode 100644 index 00000000..87bf7abc --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/read_test.go @@ -0,0 +1,203 @@ +// Copyright 2014 Damjan Cvetko. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +package pcapgo + +import ( + "bytes" + "testing" + "time" +) + +// test header read +func TestCreatePcapReader(t *testing.T) { + test := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err != nil { + t.Error(err) + t.FailNow() + } +} + +// test big endian file read +func TestCreatePcapReaderBigEndian(t *testing.T) { + test := []byte{ + 0xa1, 0xb2, 0xc3, 0xd4, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err != nil { + t.Error(err) + t.FailNow() + } +} + +// test opening invalid data +func TestCreatePcapReaderFail(t *testing.T) { + test := []byte{ + 0xd0, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err == nil { + t.Error("Should fail but did not") + t.FailNow() + } +} + +func TestPacket(t *testing.T) { + test := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, // magic, maj, min + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tz, sigfigs + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // snaplen, linkType + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + } + + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + + data, ci, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if !ci.Timestamp.Equal(time.Date(2014, 9, 18, 12, 13, 14, 1000, time.UTC)) { + t.Error("Invalid time read") + t.FailNow() + } + if ci.CaptureLength != 4 || ci.Length != 8 { + t.Error("Invalid CapLen or Len") + } + want := []byte{1, 2, 3, 4} + if !bytes.Equal(data, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data) + } +} + +func TestPacketNano(t *testing.T) { + test := []byte{ + 0x4d, 0x3c, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, // magic, maj, min + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tz, sigfigs + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // snaplen, linkType + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + } + + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + + data, ci, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if !ci.Timestamp.Equal(time.Date(2014, 9, 18, 12, 13, 14, 1, time.UTC)) { + t.Error("Invalid time read") + t.FailNow() + } + if ci.CaptureLength != 4 || ci.Length != 8 { + t.Error("Invalid CapLen or Len") + } + want := []byte{1, 2, 3, 4} + if !bytes.Equal(data, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data) + } +} + +func TestGzipPacket(t *testing.T) { + test := []byte{ + 0x1f, 0x8b, 0x08, 0x08, 0x92, 0x4d, 0x81, 0x57, + 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x00, 0xbb, + 0x72, 0x78, 0xd3, 0x42, 0x26, 0x06, 0x16, 0x06, + 0x18, 0xf8, 0xff, 0x9f, 0x81, 0x81, 0x11, 0x48, + 0x47, 0x9d, 0x91, 0x0a, 0x01, 0xd1, 0x20, 0x19, + 0x0e, 0x20, 0x66, 0x64, 0x62, 0x66, 0x01, 0x00, + 0xe4, 0x76, 0x9b, 0x75, 0x2c, 0x00, 0x00, 0x00, + } + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + if err != nil { + t.Error("Unexpected error returned:", err) + t.FailNow() + } + + data, ci, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if !ci.Timestamp.Equal(time.Date(2014, 9, 18, 12, 13, 14, 1000, time.UTC)) { + t.Error("Invalid time read") + t.FailNow() + } + if ci.CaptureLength != 4 || ci.Length != 8 { + t.Error("Invalid CapLen or Len") + } + want := []byte{1, 2, 3, 4} + if !bytes.Equal(data, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data) + } +} + +func TestTruncatedGzipPacket(t *testing.T) { + test := []byte{ + 0x1f, 0x8b, 0x08, + } + buf := bytes.NewBuffer(test) + _, err := NewReader(buf) + if err == nil { + t.Error("Should fail but did not") + t.FailNow() + } +} + +func TestPacketBufferReuse(t *testing.T) { + test := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, // magic, maj, min + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tz, sigfigs + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // snaplen, linkType + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + 0x5A, 0xCC, 0x1A, 0x54, 0x01, 0x00, 0x00, 0x00, // sec, usec + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // cap len, full len + 0x01, 0x02, 0x03, 0x04, // data + } + + buf := bytes.NewBuffer(test) + r, err := NewReader(buf) + + data1, _, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + if want := []byte{1, 2, 3, 4}; !bytes.Equal(data1, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data1) + } + data2, _, err := r.ReadPacketData() + if err != nil { + t.Error(err) + t.FailNow() + } + for i := range data1 { + data1[i] = 0xff // modify data1 after getting data2, make sure we don't overlap buffers. + } + if want := []byte{1, 2, 3, 4}; !bytes.Equal(data2, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, data2) + } +} diff --git a/vendor/github.com/google/gopacket/pcapgo/write.go b/vendor/github.com/google/gopacket/pcapgo/write.go new file mode 100644 index 00000000..bfc312fd --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/write.go @@ -0,0 +1,103 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package pcapgo provides some native PCAP support, not requiring +// C libpcap to be installed. +package pcapgo + +import ( + "encoding/binary" + "fmt" + "io" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +// Writer wraps an underlying io.Writer to write packet data in PCAP +// format. See http://wiki.wireshark.org/Development/LibpcapFileFormat +// for information on the file format. +// +// For those that care, we currently write v2.4 files with nanosecond +// timestamp resolution and little-endian encoding. +type Writer struct { + w io.Writer +} + +const magicMicroseconds = 0xA1B2C3D4 +const versionMajor = 2 +const versionMinor = 4 + +// NewWriter returns a new writer object, for writing packet data out +// to the given writer. If this is a new empty writer (as opposed to +// an append), you must call WriteFileHeader before WritePacket. +// +// // Write a new file: +// f, _ := os.Create("/tmp/file.pcap") +// w := pcapgo.NewWriter(f) +// w.WriteFileHeader(65536, layers.LinkTypeEthernet) // new file, must do this. +// w.WritePacket(gopacket.CaptureInfo{...}, data1) +// f.Close() +// // Append to existing file (must have same snaplen and linktype) +// f2, _ := os.OpenFile("/tmp/file.pcap", os.O_APPEND, 0700) +// w2 := pcapgo.NewWriter(f2) +// // no need for file header, it's already written. +// w2.WritePacket(gopacket.CaptureInfo{...}, data2) +// f2.Close() +func NewWriter(w io.Writer) *Writer { + return &Writer{w: w} +} + +// WriteFileHeader writes a file header out to the writer. +// This must be called exactly once per output. +func (w *Writer) WriteFileHeader(snaplen uint32, linktype layers.LinkType) error { + var buf [24]byte + binary.LittleEndian.PutUint32(buf[0:4], magicMicroseconds) + binary.LittleEndian.PutUint16(buf[4:6], versionMajor) + binary.LittleEndian.PutUint16(buf[6:8], versionMinor) + // bytes 8:12 stay 0 (timezone = UTC) + // bytes 12:16 stay 0 (sigfigs is always set to zero, according to + // http://wiki.wireshark.org/Development/LibpcapFileFormat + binary.LittleEndian.PutUint32(buf[16:20], snaplen) + binary.LittleEndian.PutUint32(buf[20:24], uint32(linktype)) + _, err := w.w.Write(buf[:]) + return err +} + +const nanosPerMicro = 1000 + +func (w *Writer) writePacketHeader(ci gopacket.CaptureInfo) error { + var buf [16]byte + + t := ci.Timestamp + if t.IsZero() { + t = time.Now() + } + secs := t.Unix() + usecs := t.Nanosecond() / nanosPerMicro + binary.LittleEndian.PutUint32(buf[0:4], uint32(secs)) + binary.LittleEndian.PutUint32(buf[4:8], uint32(usecs)) + binary.LittleEndian.PutUint32(buf[8:12], uint32(ci.CaptureLength)) + binary.LittleEndian.PutUint32(buf[12:16], uint32(ci.Length)) + _, err := w.w.Write(buf[:]) + return err +} + +// WritePacket writes the given packet data out to the file. +func (w *Writer) WritePacket(ci gopacket.CaptureInfo, data []byte) error { + if ci.CaptureLength != len(data) { + return fmt.Errorf("capture length %d does not match data length %d", ci.CaptureLength, len(data)) + } + if ci.CaptureLength > ci.Length { + return fmt.Errorf("invalid capture info %+v: capture length > length", ci) + } + if err := w.writePacketHeader(ci); err != nil { + return fmt.Errorf("error writing packet header: %v", err) + } + _, err := w.w.Write(data) + return err +} diff --git a/vendor/github.com/google/gopacket/pcapgo/write_test.go b/vendor/github.com/google/gopacket/pcapgo/write_test.go new file mode 100644 index 00000000..5b87d6a5 --- /dev/null +++ b/vendor/github.com/google/gopacket/pcapgo/write_test.go @@ -0,0 +1,71 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pcapgo + +import ( + "bytes" + "github.com/google/gopacket" + "testing" + "time" +) + +func TestWriteHeader(t *testing.T) { + var buf bytes.Buffer + w := NewWriter(&buf) + w.WriteFileHeader(0x1234, 0x56) + want := []byte{ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x12, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + } + if got := buf.Bytes(); !bytes.Equal(got, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, got) + } +} + +func TestWritePacket(t *testing.T) { + ci := gopacket.CaptureInfo{ + Timestamp: time.Unix(0x01020304, 0xAA*1000), + Length: 0xABCD, + CaptureLength: 10, + } + data := []byte{9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + var buf bytes.Buffer + w := NewWriter(&buf) + w.WritePacket(ci, data) + want := []byte{ + 0x04, 0x03, 0x02, 0x01, 0xAA, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0xCD, 0xAB, 0x00, 0x00, + 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + } + if got := buf.Bytes(); !bytes.Equal(got, want) { + t.Errorf("buf mismatch:\nwant: %+v\ngot: %+v", want, got) + } +} + +func TestCaptureInfoErrors(t *testing.T) { + data := []byte{1, 2, 3, 4} + ts := time.Unix(0, 0) + for _, test := range []gopacket.CaptureInfo{ + gopacket.CaptureInfo{ + Timestamp: ts, + Length: 5, + CaptureLength: 5, + }, + gopacket.CaptureInfo{ + Timestamp: ts, + Length: 3, + CaptureLength: 4, + }, + } { + var buf bytes.Buffer + w := NewWriter(&buf) + if err := w.WritePacket(test, data); err == nil { + t.Errorf("CaptureInfo %+v should have error", test) + } + } +} diff --git a/vendor/github.com/google/gopacket/pfring/doc.go b/vendor/github.com/google/gopacket/pfring/doc.go new file mode 100644 index 00000000..32baaf67 --- /dev/null +++ b/vendor/github.com/google/gopacket/pfring/doc.go @@ -0,0 +1,58 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/*Package pfring wraps the PF_RING C library for Go. + +PF_RING is a high-performance packet capture library written by ntop.org (see +http://www.ntop.org/products/pf_ring/). This library allows you to utilize the +PF_RING library with gopacket to read packet data and decode it. + +This package is meant to be used with its parent, +http://github.com/google/gopacket, although it can also be used independently +if you just want to get packet data from the wire. + +Simple Example + +This is probably the simplest code you can use to start getting packets through +pfring: + + if ring, err := pfring.NewRing("eth0", 65536, pfring.FlagPromisc); err != nil { + panic(err) + } else if err := ring.SetBPFFilter("tcp and port 80"); err != nil { // optional + panic(err) + } else if err := ring.Enable(); err != nil { // Must do this!, or you get no packets! + panic(err) + } else { + packetSource := gopacket.NewPacketSource(ring, layers.LinkTypeEthernet) + for packet := range packetSource.Packets() { + handlePacket(packet) // Do something with a packet here. + } + } + +Pfring Tweaks + +PF_RING has a ton of optimizations and tweaks to make sure you get just the +packets you want. For example, if you're only using pfring to read packets, +consider running: + + ring.SetSocketMode(pfring.ReadOnly) + +If you only care about packets received on your interface (not those transmitted +by the interface), you can run: + + ring.SetDirection(pfring.ReceiveOnly) + +Pfring Clusters + +PF_RING has an idea of 'clusters', where multiple applications can all read from +the same cluster, and PF_RING will multiplex packets over that cluster such that +only one application receives each packet. We won't discuss this mechanism in +too much more detail (see the ntop.org docs for more info), but here's how to +utilize this with the pfring go library: + + ring.SetCluster(1, pfring.ClusterPerFlow5Tuple) +*/ +package pfring diff --git a/vendor/github.com/google/gopacket/pfring/pfring.go b/vendor/github.com/google/gopacket/pfring/pfring.go new file mode 100644 index 00000000..1d2fa686 --- /dev/null +++ b/vendor/github.com/google/gopacket/pfring/pfring.go @@ -0,0 +1,343 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package pfring + +/* +#cgo LDFLAGS: -lpfring -lpcap +#include +#include +#include + +int pfring_readpacketdatato_wrapper( + pfring* ring, + u_char* buffer, + u_int buffer_len, + struct pfring_pkthdr* hdr) { + // We can't pass a Go pointer to a Go pointer which means we can't pass + // buffer as a uchar**, like pfring_recv wants, for ReadPacketDataTo. So, + // this wrapper does the pointer conversion in C code. Since this isn't + // zero-copy, it turns out that the pointer-to-pointer part of things isn't + // actually used anyway. + return pfring_recv(ring, &buffer, buffer_len, hdr, 1); +} +*/ +import "C" + +// NOTE: If you install PF_RING with non-standard options, you may also need +// to use LDFLAGS -lnuma and/or -lrt. Both have been reported necessary if +// PF_RING is configured with --disable-bpf. + +import ( + "fmt" + "net" + "os" + "strconv" + "sync" + "time" + "unsafe" + + "github.com/google/gopacket" +) + +const errorBufferSize = 256 + +// Ring provides a handle to a pf_ring. +type Ring struct { + // cptr is the handle for the actual pcap C object. + cptr *C.pfring + snaplen int + useExtendedPacketHeader bool + interfaceIndex int + mu sync.Mutex + // Since pointers to these objects are passed into a C function, if + // they're declared locally then the Go compiler thinks they may have + // escaped into C-land, so it allocates them on the heap. This causes a + // huge memory hit, so to handle that we store them here instead. + pkthdr C.struct_pfring_pkthdr + bufPtr *C.u_char +} + +// Flag provides a set of boolean flags to use when creating a new ring. +type Flag uint32 + +// Set of flags that can be passed (OR'd together) to NewRing. +const ( + FlagReentrant Flag = C.PF_RING_REENTRANT + FlagLongHeader Flag = C.PF_RING_LONG_HEADER + FlagPromisc Flag = C.PF_RING_PROMISC + FlagDNASymmetricRSS Flag = C.PF_RING_DNA_SYMMETRIC_RSS + FlagTimestamp Flag = C.PF_RING_TIMESTAMP + FlagHWTimestamp Flag = C.PF_RING_HW_TIMESTAMP +) + +// NewRing creates a new PFRing. Note that when the ring is initially created, +// it is disabled. The caller must call Enable to start receiving packets. +// The caller should call Close on the given ring when finished with it. +func NewRing(device string, snaplen uint32, flags Flag) (ring *Ring, _ error) { + dev := C.CString(device) + defer C.free(unsafe.Pointer(dev)) + + cptr, err := C.pfring_open(dev, C.u_int32_t(snaplen), C.u_int32_t(flags)) + if cptr == nil || err != nil { + return nil, fmt.Errorf("pfring NewRing error: %v", err) + } + ring = &Ring{cptr: cptr, snaplen: int(snaplen)} + + if flags&FlagLongHeader == FlagLongHeader { + ring.useExtendedPacketHeader = true + } else { + ifc, err := net.InterfaceByName(device) + if err == nil { + ring.interfaceIndex = ifc.Index + } + } + ring.SetApplicationName(os.Args[0]) + return +} + +// Close closes the given Ring. After this call, the Ring should no longer be +// used. +func (r *Ring) Close() { + C.pfring_close(r.cptr) +} + +// NextResult is the return code from a call to Next. +type NextResult int32 + +// Set of results that could be returned from a call to get another packet. +const ( + NextNoPacketNonblocking NextResult = 0 + NextError NextResult = -1 + NextOk NextResult = 1 + NextNotEnabled NextResult = -7 +) + +// NextResult implements the error interface. +func (n NextResult) Error() string { + switch n { + case NextNoPacketNonblocking: + return "No packet available, nonblocking socket" + case NextError: + return "Generic error" + case NextOk: + return "Success (not an error)" + case NextNotEnabled: + return "Ring not enabled" + } + return strconv.Itoa(int(n)) +} + +// ReadPacketDataTo reads packet data into a user-supplied buffer. +// This function ignores snaplen and instead reads up to the length of the +// passed-in slice. +// The number of bytes read into data will be returned in ci.CaptureLength. +func (r *Ring) ReadPacketDataTo(data []byte) (ci gopacket.CaptureInfo, err error) { + // This tricky bufPtr points to the start of our slice data, so pfring_recv + // will actually write directly into our Go slice. Nice! + r.mu.Lock() + r.bufPtr = (*C.u_char)(unsafe.Pointer(&data[0])) + result := NextResult(C.pfring_readpacketdatato_wrapper(r.cptr, r.bufPtr, C.u_int(len(data)), &r.pkthdr)) + if result != NextOk { + err = result + r.mu.Unlock() + return + } + ci.Timestamp = time.Unix(int64(r.pkthdr.ts.tv_sec), + int64(r.pkthdr.ts.tv_usec)*1000) // convert micros to nanos + ci.CaptureLength = int(r.pkthdr.caplen) + ci.Length = int(r.pkthdr.len) + if r.useExtendedPacketHeader { + ci.InterfaceIndex = int(r.pkthdr.extended_hdr.if_index) + } else { + ci.InterfaceIndex = r.interfaceIndex + } + r.mu.Unlock() + return +} + +// ReadPacketData returns the next packet read from the pcap handle, along with an error +// code associated with that packet. If the packet is read successfully, the +// returned error is nil. +func (r *Ring) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) { + data = make([]byte, r.snaplen) + ci, err = r.ReadPacketDataTo(data) + if err != nil { + data = nil + return + } + data = data[:ci.CaptureLength] + return +} + +// ClusterType is a type of clustering used when balancing across multiple +// rings. +type ClusterType C.cluster_type + +const ( + // ClusterPerFlow clusters by + ClusterPerFlow ClusterType = C.cluster_per_flow + // ClusterRoundRobin round-robins packets between applications, ignoring + // packet information. + ClusterRoundRobin ClusterType = C.cluster_round_robin + // ClusterPerFlow2Tuple clusters by + ClusterPerFlow2Tuple ClusterType = C.cluster_per_flow_2_tuple + // ClusterPerFlow4Tuple clusters by + ClusterPerFlow4Tuple ClusterType = C.cluster_per_flow_4_tuple + // ClusterPerFlow5Tuple clusters by + ClusterPerFlow5Tuple ClusterType = C.cluster_per_flow_5_tuple + // ClusterPerFlowTCP5Tuple acts like ClusterPerFlow5Tuple for TCP packets and + // like ClusterPerFlow2Tuple for all other packets. + ClusterPerFlowTCP5Tuple ClusterType = C.cluster_per_flow_tcp_5_tuple +) + +// SetCluster sets which cluster the ring should be part of, and the cluster +// type to use. +func (r *Ring) SetCluster(cluster int, typ ClusterType) error { + if rv := C.pfring_set_cluster(r.cptr, C.u_int(cluster), C.cluster_type(typ)); rv != 0 { + return fmt.Errorf("Unable to set cluster, got error code %d", rv) + } + return nil +} + +// RemoveFromCluster removes the ring from the cluster it was put in with +// SetCluster. +func (r *Ring) RemoveFromCluster() error { + if rv := C.pfring_remove_from_cluster(r.cptr); rv != 0 { + return fmt.Errorf("Unable to remove from cluster, got error code %d", rv) + } + return nil +} + +// SetSamplingRate sets the sampling rate to 1/. +func (r *Ring) SetSamplingRate(rate int) error { + if rv := C.pfring_set_sampling_rate(r.cptr, C.u_int32_t(rate)); rv != 0 { + return fmt.Errorf("Unable to set sampling rate, got error code %d", rv) + } + return nil +} + +// SetBPFFilter sets the BPF filter for the ring. +func (r *Ring) SetBPFFilter(bpfFilter string) error { + filter := C.CString(bpfFilter) + defer C.free(unsafe.Pointer(filter)) + if rv := C.pfring_set_bpf_filter(r.cptr, filter); rv != 0 { + return fmt.Errorf("Unable to set BPF filter, got error code %d", rv) + } + return nil +} + +// RemoveBPFFilter removes the BPF filter from the ring. +func (r *Ring) RemoveBPFFilter() error { + if rv := C.pfring_remove_bpf_filter(r.cptr); rv != 0 { + return fmt.Errorf("Unable to remove BPF filter, got error code %d", rv) + } + return nil +} + +// WritePacketData uses the ring to send raw packet data to the interface. +func (r *Ring) WritePacketData(data []byte) error { + buf := (*C.char)(unsafe.Pointer(&data[0])) + if rv := C.pfring_send(r.cptr, buf, C.u_int(len(data)), 1); rv < 0 { + return fmt.Errorf("Unable to send packet data, got error code %d", rv) + } + return nil +} + +// Enable enables the given ring. This function MUST be called on each new +// ring after it has been set up, or that ring will NOT receive packets. +func (r *Ring) Enable() error { + if rv := C.pfring_enable_ring(r.cptr); rv != 0 { + return fmt.Errorf("Unable to enable ring, got error code %d", rv) + } + return nil +} + +// Disable disables the given ring. After this call, it will no longer receive +// packets. +func (r *Ring) Disable() error { + if rv := C.pfring_disable_ring(r.cptr); rv != 0 { + return fmt.Errorf("Unable to disable ring, got error code %d", rv) + } + return nil +} + +// Stats provides simple statistics on a ring. +type Stats struct { + Received, Dropped uint64 +} + +// Stats returns statistsics for the ring. +func (r *Ring) Stats() (s Stats, err error) { + var stats C.pfring_stat + if rv := C.pfring_stats(r.cptr, &stats); rv != 0 { + err = fmt.Errorf("Unable to get ring stats, got error code %d", rv) + return + } + s.Received = uint64(stats.recv) + s.Dropped = uint64(stats.drop) + return +} + +// Direction is a simple enum to set which packets (TX, RX, or both) a ring +// captures. +type Direction C.packet_direction + +const ( + // TransmitOnly will only capture packets transmitted by the ring's + // interface(s). + TransmitOnly Direction = C.tx_only_direction + // ReceiveOnly will only capture packets received by the ring's + // interface(s). + ReceiveOnly Direction = C.rx_only_direction + // ReceiveAndTransmit will capture both received and transmitted packets on + // the ring's interface(s). + ReceiveAndTransmit Direction = C.rx_and_tx_direction +) + +// SetDirection sets which packets should be captured by the ring. +func (r *Ring) SetDirection(d Direction) error { + if rv := C.pfring_set_direction(r.cptr, C.packet_direction(d)); rv != 0 { + return fmt.Errorf("Unable to set ring direction, got error code %d", rv) + } + return nil +} + +// SocketMode is an enum for setting whether a ring should read, write, or both. +type SocketMode C.socket_mode + +const ( + // WriteOnly sets up the ring to only send packets (Inject), not read them. + WriteOnly SocketMode = C.send_only_mode + // ReadOnly sets up the ring to only receive packets (ReadPacketData), not + // send them. + ReadOnly SocketMode = C.recv_only_mode + // WriteAndRead sets up the ring to both send and receive packets. + WriteAndRead SocketMode = C.send_and_recv_mode +) + +// SetSocketMode sets the mode of the ring socket to send, receive, or both. +func (r *Ring) SetSocketMode(s SocketMode) error { + if rv := C.pfring_set_socket_mode(r.cptr, C.socket_mode(s)); rv != 0 { + return fmt.Errorf("Unable to set socket mode, got error code %d", rv) + } + return nil +} + +// SetApplicationName sets a string name to the ring. This name is available in +// /proc stats for pf_ring. By default, NewRing automatically calls this with +// argv[0]. +func (r *Ring) SetApplicationName(name string) error { + buf := C.CString(name) + defer C.free(unsafe.Pointer(buf)) + if rv := C.pfring_set_application_name(r.cptr, buf); rv != 0 { + return fmt.Errorf("Unable to set ring application name, got error code %d", rv) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/reassembly/cap2test.go b/vendor/github.com/google/gopacket/reassembly/cap2test.go new file mode 100644 index 00000000..1d43f1df --- /dev/null +++ b/vendor/github.com/google/gopacket/reassembly/cap2test.go @@ -0,0 +1,105 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build ignore + +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" + "strings" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" +) + +var input = flag.String("i", "", "Input filename") + +func main() { + var handler *pcap.Handle + var err error + flag.Parse() + if *input == "" { + log.Fatalf("Please specify input filename") + } + if handler, err = pcap.OpenOffline(*input); err != nil { + log.Fatalf("Failed to open: %s: %s", *input, err) + } + args := flag.Args() + if len(args) > 0 { + filter := strings.Join(args, " ") + if err := handler.SetBPFFilter(filter); err != nil { + log.Fatalf("Failed to set BPF filter \"%s\": %s", filter, err) + } + handler.Stats() + } + var decoder gopacket.Decoder + var ok bool + linkType := fmt.Sprintf("%s", handler.LinkType()) + if decoder, ok = gopacket.DecodersByLayerName[linkType]; !ok { + log.Fatalf("Failed to find decoder to pcap's linktype %s", linkType) + } + source := gopacket.NewPacketSource(handler, decoder) + count := uint64(0) + pktNonTcp := uint64(0) + pktTcp := uint64(0) + fmt.Println("test([]testSequence{") + for packet := range source.Packets() { + count++ + tcp := packet.Layer(layers.LayerTypeTCP) + if tcp == nil { + pktNonTcp++ + continue + } else { + pktTcp++ + tcp := tcp.(*layers.TCP) + //fmt.Printf("packet: %s\n", tcp) + var b bytes.Buffer + b.WriteString("{\n") + // TCP + b.WriteString("tcp: layers.TCP{\n") + if tcp.SYN { + b.WriteString(" SYN: true,\n") + } + if tcp.ACK { + b.WriteString(" ACK: true,\n") + } + if tcp.RST { + b.WriteString(" RST: true,\n") + } + if tcp.FIN { + b.WriteString(" FIN: true,\n") + } + b.WriteString(fmt.Sprintf(" SrcPort: %d,\n", tcp.SrcPort)) + b.WriteString(fmt.Sprintf(" DstPort: %d,\n", tcp.DstPort)) + b.WriteString(fmt.Sprintf(" Seq: %d,\n", tcp.Seq)) + b.WriteString(fmt.Sprintf(" Ack: %d,\n", tcp.Ack)) + b.WriteString(" BaseLayer: layers.BaseLayer{Payload: []byte{") + for _, p := range tcp.Payload { + b.WriteString(fmt.Sprintf("%d,", p)) + } + b.WriteString("}},\n") + b.WriteString("},\n") + // CaptureInfo + b.WriteString("ci: gopacket.CaptureInfo{\n") + ts := packet.Metadata().CaptureInfo.Timestamp + b.WriteString(fmt.Sprintf(" Timestamp: time.Unix(%d,%d),\n", ts.Unix(), ts.Nanosecond())) + b.WriteString("},\n") + // Struct + b.WriteString("},\n") + fmt.Print(b.String()) + } + + } + fmt.Println("})") + + fmt.Fprintf(os.Stderr, "Total: %d, TCP: %d, non-TCP: %d\n", count, pktTcp, pktNonTcp) +} diff --git a/vendor/github.com/google/gopacket/reassembly/memory.go b/vendor/github.com/google/gopacket/reassembly/memory.go new file mode 100644 index 00000000..c1b2ae75 --- /dev/null +++ b/vendor/github.com/google/gopacket/reassembly/memory.go @@ -0,0 +1,254 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package reassembly + +import ( + "flag" + "log" + "sync" + "time" + + "github.com/google/gopacket/layers" +) + +var memLog = flag.Bool("assembly_memuse_log", defaultDebug, "If true, the github.com/google/gopacket/reassembly library will log information regarding its memory use every once in a while.") + +/* + * pageCache + */ +// pageCache is a concurrency-unsafe store of page objects we use to avoid +// memory allocation as much as we can. +type pageCache struct { + free []*page + pcSize int + size, used int + pageRequests int64 + ops int + nextShrink int +} + +const initialAllocSize = 1024 + +func newPageCache() *pageCache { + pc := &pageCache{ + free: make([]*page, 0, initialAllocSize), + pcSize: initialAllocSize, + } + pc.grow() + return pc +} + +// grow exponentially increases the size of our page cache as much as necessary. +func (c *pageCache) grow() { + pages := make([]page, c.pcSize) + c.size += c.pcSize + for i := range pages { + c.free = append(c.free, &pages[i]) + } + if *memLog { + log.Println("PageCache: created", c.pcSize, "new pages, size:", c.size, "cap:", cap(c.free), "len:", len(c.free)) + } + // control next shrink attempt + c.nextShrink = c.pcSize + c.ops = 0 + // prepare for next alloc + c.pcSize *= 2 +} + +// Remove references to unused pages to let GC collect them +// Note: memory used by c.free itself it not collected. +func (c *pageCache) tryShrink() { + var min = c.pcSize / 2 + if min < initialAllocSize { + min = initialAllocSize + } + if len(c.free) <= min { + return + } + for i := range c.free[min:] { + c.free[min+i] = nil + } + c.size -= len(c.free) - min + c.free = c.free[:min] + c.pcSize = min +} + +// next returns a clean, ready-to-use page object. +func (c *pageCache) next(ts time.Time) (p *page) { + if *memLog { + c.pageRequests++ + if c.pageRequests&0xFFFF == 0 { + log.Println("PageCache:", c.pageRequests, "requested,", c.used, "used,", len(c.free), "free") + } + } + if len(c.free) == 0 { + c.grow() + } + i := len(c.free) - 1 + p, c.free = c.free[i], c.free[:i] + p.seen = ts + p.bytes = p.buf[:0] + c.used++ + if *memLog { + log.Printf("allocator returns %s\n", p) + } + c.ops++ + if c.ops > c.nextShrink { + c.ops = 0 + c.tryShrink() + } + + return p +} + +// replace replaces a page into the pageCache. +func (c *pageCache) replace(p *page) { + c.used-- + if *memLog { + log.Printf("replacing %s\n", p) + } + p.prev = nil + p.next = nil + c.free = append(c.free, p) +} + +/* + * StreamPool + */ + +// StreamPool stores all streams created by Assemblers, allowing multiple +// assemblers to work together on stream processing while enforcing the fact +// that a single stream receives its data serially. It is safe +// for concurrency, usable by multiple Assemblers at once. +// +// StreamPool handles the creation and storage of Stream objects used by one or +// more Assembler objects. When a new TCP stream is found by an Assembler, it +// creates an associated Stream by calling its StreamFactory's New method. +// Thereafter (until the stream is closed), that Stream object will receive +// assembled TCP data via Assembler's calls to the stream's Reassembled +// function. +// +// Like the Assembler, StreamPool attempts to minimize allocation. Unlike the +// Assembler, though, it does have to do some locking to make sure that the +// connection objects it stores are accessible to multiple Assemblers. +type StreamPool struct { + conns map[key]*connection + users int + mu sync.RWMutex + factory StreamFactory + free []*connection + all [][]connection + nextAlloc int + newConnectionCount int64 +} + +func (p *StreamPool) grow() { + conns := make([]connection, p.nextAlloc) + p.all = append(p.all, conns) + for i := range conns { + p.free = append(p.free, &conns[i]) + } + if *memLog { + log.Println("StreamPool: created", p.nextAlloc, "new connections") + } + p.nextAlloc *= 2 +} + +// Dump logs all connections +func (p *StreamPool) Dump() { + p.mu.Lock() + defer p.mu.Unlock() + log.Printf("Remaining %d connections: ", len(p.conns)) + for _, conn := range p.conns { + log.Printf("%v %s", conn.key, conn) + } +} + +func (p *StreamPool) remove(conn *connection) { + p.mu.Lock() + if _, ok := p.conns[conn.key]; ok { + delete(p.conns, conn.key) + p.free = append(p.free, conn) + } + p.mu.Unlock() +} + +// NewStreamPool creates a new connection pool. Streams will +// be created as necessary using the passed-in StreamFactory. +func NewStreamPool(factory StreamFactory) *StreamPool { + return &StreamPool{ + conns: make(map[key]*connection, initialAllocSize), + free: make([]*connection, 0, initialAllocSize), + factory: factory, + nextAlloc: initialAllocSize, + } +} + +func (p *StreamPool) connections() []*connection { + p.mu.RLock() + conns := make([]*connection, 0, len(p.conns)) + for _, conn := range p.conns { + conns = append(conns, conn) + } + p.mu.RUnlock() + return conns +} + +func (p *StreamPool) newConnection(k key, s Stream, ts time.Time) (c *connection, h *halfconnection, r *halfconnection) { + if *memLog { + p.newConnectionCount++ + if p.newConnectionCount&0x7FFF == 0 { + log.Println("StreamPool:", p.newConnectionCount, "requests,", len(p.conns), "used,", len(p.free), "free") + } + } + if len(p.free) == 0 { + p.grow() + } + index := len(p.free) - 1 + c, p.free = p.free[index], p.free[:index] + c.reset(k, s, ts) + return c, &c.c2s, &c.s2c +} + +func (p *StreamPool) getHalf(k key) (*connection, *halfconnection, *halfconnection) { + conn := p.conns[k] + if conn != nil { + return conn, &conn.c2s, &conn.s2c + } + rk := k.Reverse() + conn = p.conns[rk] + if conn != nil { + return conn, &conn.s2c, &conn.c2s + } + return nil, nil, nil +} + +// getConnection returns a connection. If end is true and a connection +// does not already exist, returns nil. This allows us to check for a +// connection without actually creating one if it doesn't already exist. +func (p *StreamPool) getConnection(k key, end bool, ts time.Time, tcp *layers.TCP, ac AssemblerContext) (*connection, *halfconnection, *halfconnection) { + p.mu.RLock() + conn, half, rev := p.getHalf(k) + p.mu.RUnlock() + if end || conn != nil { + return conn, half, rev + } + s := p.factory.New(k[0], k[1], tcp, ac) + p.mu.Lock() + defer p.mu.Unlock() + conn, half, rev = p.newConnection(k, s, ts) + conn2, half2, rev2 := p.getHalf(k) + if conn2 != nil { + if conn2.key != k { + panic("FIXME: other dir added in the meantime...") + } + // FIXME: delete s ? + return conn2, half2, rev2 + } + p.conns[k] = conn + return conn, half, rev +} diff --git a/vendor/github.com/google/gopacket/reassembly/tcpassembly.go b/vendor/github.com/google/gopacket/reassembly/tcpassembly.go new file mode 100644 index 00000000..bdf0deb7 --- /dev/null +++ b/vendor/github.com/google/gopacket/reassembly/tcpassembly.go @@ -0,0 +1,1311 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package reassembly provides TCP stream re-assembly. +// +// The reassembly package implements uni-directional TCP reassembly, for use in +// packet-sniffing applications. The caller reads packets off the wire, then +// presents them to an Assembler in the form of gopacket layers.TCP packets +// (github.com/google/gopacket, github.com/google/gopacket/layers). +// +// The Assembler uses a user-supplied +// StreamFactory to create a user-defined Stream interface, then passes packet +// data in stream order to that object. A concurrency-safe StreamPool keeps +// track of all current Streams being reassembled, so multiple Assemblers may +// run at once to assemble packets while taking advantage of multiple cores. +// +// TODO: Add simplest example +package reassembly + +import ( + "encoding/hex" + "flag" + "fmt" + "log" + "sync" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +// TODO: +// - push to Stream on Ack +// - implement chunked (cheap) reads and Reader() interface +// - better organize file: split files: 'mem', 'misc' (seq + flow) + +var defaultDebug = false + +var debugLog = flag.Bool("assembly_debug_log", defaultDebug, "If true, the github.com/google/gopacket/reassembly library will log verbose debugging information (at least one line per packet)") + +const invalidSequence = -1 +const uint32Max = 0xFFFFFFFF + +// Sequence is a TCP sequence number. It provides a few convenience functions +// for handling TCP wrap-around. The sequence should always be in the range +// [0,0xFFFFFFFF]... its other bits are simply used in wrap-around calculations +// and should never be set. +type Sequence int64 + +// Difference defines an ordering for comparing TCP sequences that's safe for +// roll-overs. It returns: +// > 0 : if t comes after s +// < 0 : if t comes before s +// 0 : if t == s +// The number returned is the sequence difference, so 4.Difference(8) will +// return 4. +// +// It handles rollovers by considering any sequence in the first quarter of the +// uint32 space to be after any sequence in the last quarter of that space, thus +// wrapping the uint32 space. +func (s Sequence) Difference(t Sequence) int { + if s > uint32Max-uint32Max/4 && t < uint32Max/4 { + t += uint32Max + } else if t > uint32Max-uint32Max/4 && s < uint32Max/4 { + s += uint32Max + } + return int(t - s) +} + +// Add adds an integer to a sequence and returns the resulting sequence. +func (s Sequence) Add(t int) Sequence { + return (s + Sequence(t)) & uint32Max +} + +// TCPAssemblyStats provides some figures for a ScatterGather +type TCPAssemblyStats struct { + // For this ScatterGather + Chunks int + Packets int + // For the half connection, since last call to ReassembledSG() + QueuedBytes int + QueuedPackets int + OverlapBytes int + OverlapPackets int +} + +// ScatterGather is used to pass reassembled data and metadata of reassembled +// packets to a Stream via ReassembledSG +type ScatterGather interface { + // Returns the length of available bytes and saved bytes + Lengths() (int, int) + // Returns the bytes up to length (shall be <= available bytes) + Fetch(length int) []byte + // Tell to keep from offset + KeepFrom(offset int) + // Return CaptureInfo of packet corresponding to given offset + CaptureInfo(offset int) gopacket.CaptureInfo + // Return some info about the reassembled chunks + Info() (direction TCPFlowDirection, start bool, end bool, skip int) + // Return some stats regarding the state of the stream + Stats() TCPAssemblyStats +} + +// byteContainer is either a page or a livePacket +type byteContainer interface { + getBytes() []byte + length() int + convertToPages(*pageCache, int, AssemblerContext) (*page, *page, int) + captureInfo() gopacket.CaptureInfo + assemblerContext() AssemblerContext + release(*pageCache) int + isStart() bool + isEnd() bool + getSeq() Sequence + isPacket() bool +} + +// Implements a ScatterGather +type reassemblyObject struct { + all []byteContainer + Skip int + Direction TCPFlowDirection + saved int + toKeep int + // stats + queuedBytes int + queuedPackets int + overlapBytes int + overlapPackets int +} + +func (rl *reassemblyObject) Lengths() (int, int) { + l := 0 + for _, r := range rl.all { + l += r.length() + } + return l, rl.saved +} + +func (rl *reassemblyObject) Fetch(l int) []byte { + if l <= rl.all[0].length() { + return rl.all[0].getBytes()[:l] + } + bytes := make([]byte, 0, l) + for _, bc := range rl.all { + bytes = append(bytes, bc.getBytes()...) + } + return bytes[:l] +} + +func (rl *reassemblyObject) KeepFrom(offset int) { + rl.toKeep = offset +} + +func (rl *reassemblyObject) CaptureInfo(offset int) gopacket.CaptureInfo { + current := 0 + for _, r := range rl.all { + if current >= offset { + return r.captureInfo() + } + current += r.length() + } + // Invalid offset + return gopacket.CaptureInfo{} +} + +func (rl *reassemblyObject) Info() (TCPFlowDirection, bool, bool, int) { + return rl.Direction, rl.all[0].isStart(), rl.all[len(rl.all)-1].isEnd(), rl.Skip +} + +func (rl *reassemblyObject) Stats() TCPAssemblyStats { + packets := int(0) + for _, r := range rl.all { + if r.isPacket() { + packets++ + } + } + return TCPAssemblyStats{ + Chunks: len(rl.all), + Packets: packets, + QueuedBytes: rl.queuedBytes, + QueuedPackets: rl.queuedPackets, + OverlapBytes: rl.overlapBytes, + OverlapPackets: rl.overlapPackets, + } +} + +const pageBytes = 1900 + +// TCPFlowDirection distinguish the two half-connections directions. +// +// TCPDirClientToServer is assigned to half-connection for the first received +// packet, hence might be wrong if packets are not received in order. +// It's up to the caller (e.g. in Accept()) to decide if the direction should +// be interpretted differently. +type TCPFlowDirection bool + +// Value are not really useful +const ( + TCPDirClientToServer TCPFlowDirection = false + TCPDirServerToClient TCPFlowDirection = true +) + +func (dir TCPFlowDirection) String() string { + switch dir { + case TCPDirClientToServer: + return "client->server" + case TCPDirServerToClient: + return "server->client" + } + return "" +} + +// Reverse returns the reversed direction +func (dir TCPFlowDirection) Reverse() TCPFlowDirection { + return !dir +} + +/* page: implements a byteContainer */ + +// page is used to store TCP data we're not ready for yet (out-of-order +// packets). Unused pages are stored in and returned from a pageCache, which +// avoids memory allocation. Used pages are stored in a doubly-linked list in +// a connection. +type page struct { + bytes []byte + seq Sequence + prev, next *page + buf [pageBytes]byte + ac AssemblerContext // only set for the first page of a packet + seen time.Time + start, end bool +} + +func (p *page) getBytes() []byte { + return p.bytes +} +func (p *page) captureInfo() gopacket.CaptureInfo { + return p.ac.GetCaptureInfo() +} +func (p *page) assemblerContext() AssemblerContext { + return p.ac +} +func (p *page) convertToPages(pc *pageCache, skip int, ac AssemblerContext) (*page, *page, int) { + if skip != 0 { + p.bytes = p.bytes[skip:] + p.seq = p.seq.Add(skip) + } + p.prev, p.next = nil, nil + return p, p, 1 +} +func (p *page) length() int { + return len(p.bytes) +} +func (p *page) release(pc *pageCache) int { + pc.replace(p) + return 1 +} +func (p *page) isStart() bool { + return p.start +} +func (p *page) isEnd() bool { + return p.end +} +func (p *page) getSeq() Sequence { + return p.seq +} +func (p *page) isPacket() bool { + return p.ac != nil +} +func (p *page) String() string { + return fmt.Sprintf("page@%p{seq: %v, bytes:%d, -> nextSeq:%v} (prev:%p, next:%p)", p, p.seq, len(p.bytes), p.seq+Sequence(len(p.bytes)), p.prev, p.next) +} + +/* livePacket: implements a byteContainer */ +type livePacket struct { + bytes []byte + start bool + end bool + ci gopacket.CaptureInfo + ac AssemblerContext + seq Sequence +} + +func (lp *livePacket) getBytes() []byte { + return lp.bytes +} +func (lp *livePacket) captureInfo() gopacket.CaptureInfo { + return lp.ci +} +func (lp *livePacket) assemblerContext() AssemblerContext { + return lp.ac +} +func (lp *livePacket) length() int { + return len(lp.bytes) +} +func (lp *livePacket) isStart() bool { + return lp.start +} +func (lp *livePacket) isEnd() bool { + return lp.end +} +func (lp *livePacket) getSeq() Sequence { + return lp.seq +} +func (lp *livePacket) isPacket() bool { + return true +} + +// Creates a page (or set of pages) from a TCP packet: returns the first and last +// page in its doubly-linked list of new pages. +func (lp *livePacket) convertToPages(pc *pageCache, skip int, ac AssemblerContext) (*page, *page, int) { + ts := lp.ci.Timestamp + first := pc.next(ts) + current := first + current.prev = nil + first.ac = ac + numPages := 1 + seq, bytes := lp.seq.Add(skip), lp.bytes[skip:] + for { + length := min(len(bytes), pageBytes) + current.bytes = current.buf[:length] + copy(current.bytes, bytes) + current.seq = seq + bytes = bytes[length:] + if len(bytes) == 0 { + current.end = lp.isEnd() + current.next = nil + break + } + seq = seq.Add(length) + current.next = pc.next(ts) + current.next.prev = current + current = current.next + current.ac = nil + numPages++ + } + return first, current, numPages +} +func (lp *livePacket) estimateNumberOfPages() int { + return (len(lp.bytes) + pageBytes + 1) / pageBytes +} + +func (lp *livePacket) release(*pageCache) int { + return 0 +} + +// Stream is implemented by the caller to handle incoming reassembled +// TCP data. Callers create a StreamFactory, then StreamPool uses +// it to create a new Stream for every TCP stream. +// +// assembly will, in order: +// 1) Create the stream via StreamFactory.New +// 2) Call ReassembledSG 0 or more times, passing in reassembled TCP data in order +// 3) Call ReassemblyComplete one time, after which the stream is dereferenced by assembly. +type Stream interface { + // Tell whether the TCP packet should be accepted, start could be modified to force a start even if no SYN have been seen + Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, ackSeq Sequence, start *bool, ac AssemblerContext) bool + + // ReassembledSG is called zero or more times. + // ScatterGather is reused after each Reassembled call, + // so it's important to copy anything you need out of it, + // especially bytes (or use KeepFrom()) + ReassembledSG(sg ScatterGather, ac AssemblerContext) + + // ReassemblyComplete is called when assembly decides there is + // no more data for this Stream, either because a FIN or RST packet + // was seen, or because the stream has timed out without any new + // packet data (due to a call to FlushCloseOlderThan). + // It should return true if the connection should be removed from the pool + // It can return false if it want to see subsequent packets with Accept(), e.g. to + // see FIN-ACK, for deeper state-machine analysis. + ReassemblyComplete(ac AssemblerContext) bool +} + +// StreamFactory is used by assembly to create a new stream for each +// new TCP session. +type StreamFactory interface { + // New should return a new stream for the given TCP key. + New(netFlow, tcpFlow gopacket.Flow, tcp *layers.TCP, ac AssemblerContext) Stream +} + +type key [2]gopacket.Flow + +func (k *key) String() string { + return fmt.Sprintf("%s:%s", k[0], k[1]) +} + +func (k *key) Reverse() key { + return key{ + k[0].Reverse(), + k[1].Reverse(), + } +} + +const assemblerReturnValueInitialSize = 16 + +/* one-way connection, i.e. halfconnection */ +type halfconnection struct { + dir TCPFlowDirection + pages int // Number of pages used (both in first/last and saved) + saved *page // Doubly-linked list of in-order pages (seq < nextSeq) already given to Stream who told us to keep + first, last *page // Doubly-linked list of out-of-order pages (seq > nextSeq) + nextSeq Sequence // sequence number of in-order received bytes + ackSeq Sequence + created, lastSeen time.Time + stream Stream + closed bool + // for stats + queuedBytes int + queuedPackets int + overlapBytes int + overlapPackets int +} + +func (half *halfconnection) String() string { + closed := "" + if half.closed { + closed = "closed " + } + return fmt.Sprintf("%screated:%v, last:%v", closed, half.created, half.lastSeen) +} + +// Dump returns a string (crypticly) describing the halfconnction +func (half *halfconnection) Dump() string { + s := fmt.Sprintf("pages: %d\n"+ + "nextSeq: %d\n"+ + "ackSeq: %d\n"+ + "Seen : %s\n"+ + "dir: %s\n", half.pages, half.nextSeq, half.ackSeq, half.lastSeen, half.dir) + nb := 0 + for p := half.first; p != nil; p = p.next { + s += fmt.Sprintf(" Page[%d] %s len: %d\n", nb, p, len(p.bytes)) + nb++ + } + return s +} + +/* Bi-directionnal connection */ + +type connection struct { + key key // client->server + c2s, s2c halfconnection + mu sync.Mutex +} + +func (c *connection) reset(k key, s Stream, ts time.Time) { + c.key = k + base := halfconnection{ + nextSeq: invalidSequence, + ackSeq: invalidSequence, + created: ts, + lastSeen: ts, + stream: s, + } + c.c2s, c.s2c = base, base + c.c2s.dir, c.s2c.dir = TCPDirClientToServer, TCPDirServerToClient +} + +func (c *connection) String() string { + return fmt.Sprintf("c2s: %s, s2c: %s", &c.c2s, &c.s2c) +} + +/* + * Assembler + */ + +// DefaultAssemblerOptions provides default options for an assembler. +// These options are used by default when calling NewAssembler, so if +// modified before a NewAssembler call they'll affect the resulting Assembler. +// +// Note that the default options can result in ever-increasing memory usage +// unless one of the Flush* methods is called on a regular basis. +var DefaultAssemblerOptions = AssemblerOptions{ + MaxBufferedPagesPerConnection: 0, // unlimited + MaxBufferedPagesTotal: 0, // unlimited +} + +// AssemblerOptions controls the behavior of each assembler. Modify the +// options of each assembler you create to change their behavior. +type AssemblerOptions struct { + // MaxBufferedPagesTotal is an upper limit on the total number of pages to + // buffer while waiting for out-of-order packets. Once this limit is + // reached, the assembler will degrade to flushing every connection it + // gets a packet for. If <= 0, this is ignored. + MaxBufferedPagesTotal int + // MaxBufferedPagesPerConnection is an upper limit on the number of pages + // buffered for a single connection. Should this limit be reached for a + // particular connection, the smallest sequence number will be flushed, along + // with any contiguous data. If <= 0, this is ignored. + MaxBufferedPagesPerConnection int +} + +// Assembler handles reassembling TCP streams. It is not safe for +// concurrency... after passing a packet in via the Assemble call, the caller +// must wait for that call to return before calling Assemble again. Callers can +// get around this by creating multiple assemblers that share a StreamPool. In +// that case, each individual stream will still be handled serially (each stream +// has an individual mutex associated with it), however multiple assemblers can +// assemble different connections concurrently. +// +// The Assembler provides (hopefully) fast TCP stream re-assembly for sniffing +// applications written in Go. The Assembler uses the following methods to be +// as fast as possible, to keep packet processing speedy: +// +// Avoids Lock Contention +// +// Assemblers locks connections, but each connection has an individual lock, and +// rarely will two Assemblers be looking at the same connection. Assemblers +// lock the StreamPool when looking up connections, but they use Reader +// locks initially, and only force a write lock if they need to create a new +// connection or close one down. These happen much less frequently than +// individual packet handling. +// +// Each assembler runs in its own goroutine, and the only state shared between +// goroutines is through the StreamPool. Thus all internal Assembler state +// can be handled without any locking. +// +// NOTE: If you can guarantee that packets going to a set of Assemblers will +// contain information on different connections per Assembler (for example, +// they're already hashed by PF_RING hashing or some other hashing mechanism), +// then we recommend you use a seperate StreamPool per Assembler, thus +// avoiding all lock contention. Only when different Assemblers could receive +// packets for the same Stream should a StreamPool be shared between them. +// +// Avoids Memory Copying +// +// In the common case, handling of a single TCP packet should result in zero +// memory allocations. The Assembler will look up the connection, figure out +// that the packet has arrived in order, and immediately pass that packet on to +// the appropriate connection's handling code. Only if a packet arrives out of +// order is its contents copied and stored in memory for later. +// +// Avoids Memory Allocation +// +// Assemblers try very hard to not use memory allocation unless absolutely +// necessary. Packet data for sequential packets is passed directly to streams +// with no copying or allocation. Packet data for out-of-order packets is +// copied into reusable pages, and new pages are only allocated rarely when the +// page cache runs out. Page caches are Assembler-specific, thus not used +// concurrently and requiring no locking. +// +// Internal representations for connection objects are also reused over time. +// Because of this, the most common memory allocation done by the Assembler is +// generally what's done by the caller in StreamFactory.New. If no allocation +// is done there, then very little allocation is done ever, mostly to handle +// large increases in bandwidth or numbers of connections. +// +// TODO: The page caches used by an Assembler will grow to the size necessary +// to handle a workload, and currently will never shrink. This means that +// traffic spikes can result in large memory usage which isn't garbage +// collected when typical traffic levels return. +type Assembler struct { + AssemblerOptions + ret []byteContainer + pc *pageCache + connPool *StreamPool + cacheLP livePacket + cacheSG reassemblyObject + start bool +} + +// NewAssembler creates a new assembler. Pass in the StreamPool +// to use, may be shared across assemblers. +// +// This sets some sane defaults for the assembler options, +// see DefaultAssemblerOptions for details. +func NewAssembler(pool *StreamPool) *Assembler { + pool.mu.Lock() + pool.users++ + pool.mu.Unlock() + return &Assembler{ + ret: make([]byteContainer, assemblerReturnValueInitialSize), + pc: newPageCache(), + connPool: pool, + AssemblerOptions: DefaultAssemblerOptions, + } +} + +// Dump returns a short string describing the page usage of the Assembler +func (a *Assembler) Dump() string { + s := "" + s += fmt.Sprintf("pageCache: used: %d, size: %d, free: %d", a.pc.used, a.pc.size, len(a.pc.free)) + return s +} + +// AssemblerContext provides method to get metadata +type AssemblerContext interface { + GetCaptureInfo() gopacket.CaptureInfo +} + +// Implements AssemblerContext for Assemble() +type assemblerSimpleContext gopacket.CaptureInfo + +func (asc *assemblerSimpleContext) GetCaptureInfo() gopacket.CaptureInfo { + return gopacket.CaptureInfo(*asc) +} + +// Assemble calls AssembleWithContext with the current timestamp, useful for +// packets being read directly off the wire. +func (a *Assembler) Assemble(netFlow gopacket.Flow, t *layers.TCP) { + ctx := assemblerSimpleContext(gopacket.CaptureInfo{Timestamp: time.Now()}) + a.AssembleWithContext(netFlow, t, &ctx) +} + +type assemblerAction struct { + nextSeq Sequence + queue bool +} + +// AssembleWithContext reassembles the given TCP packet into its appropriate +// stream. +// +// The timestamp passed in must be the timestamp the packet was seen. +// For packets read off the wire, time.Now() should be fine. For packets read +// from PCAP files, CaptureInfo.Timestamp should be passed in. This timestamp +// will affect which streams are flushed by a call to FlushCloseOlderThan. +// +// Each AssembleWithContext call results in, in order: +// +// zero or one call to StreamFactory.New, creating a stream +// zero or one call to ReassembledSG on a single stream +// zero or one call to ReassemblyComplete on the same stream +func (a *Assembler) AssembleWithContext(netFlow gopacket.Flow, t *layers.TCP, ac AssemblerContext) { + var conn *connection + var half *halfconnection + var rev *halfconnection + + a.ret = a.ret[:0] + key := key{netFlow, t.TransportFlow()} + ci := ac.GetCaptureInfo() + timestamp := ci.Timestamp + + conn, half, rev = a.connPool.getConnection(key, false, timestamp, t, ac) + if conn == nil { + if *debugLog { + log.Printf("%v got empty packet on otherwise empty connection", key) + } + return + } + conn.mu.Lock() + defer conn.mu.Unlock() + if half.lastSeen.Before(timestamp) { + half.lastSeen = timestamp + } + a.start = half.nextSeq == invalidSequence && t.SYN + if !half.stream.Accept(t, ci, half.dir, rev.ackSeq, &a.start, ac) { + if *debugLog { + log.Printf("Ignoring packet") + } + return + } + if half.closed { + // this way is closed + return + } + + seq, ack, bytes := Sequence(t.Seq), Sequence(t.Ack), t.Payload + if t.ACK { + half.ackSeq = ack + } + // TODO: push when Ack is seen ?? + action := assemblerAction{ + nextSeq: Sequence(invalidSequence), + queue: true, + } + a.dump("AssembleWithContext()", half) + if half.nextSeq == invalidSequence { + if t.SYN { + if *debugLog { + log.Printf("%v saw first SYN packet, returning immediately, seq=%v", key, seq) + } + seq = seq.Add(1) + half.nextSeq = seq + action.queue = false + } else if a.start { + if *debugLog { + log.Printf("%v start forced", key) + } + half.nextSeq = seq + action.queue = false + } else { + if *debugLog { + log.Printf("%v waiting for start, storing into connection", key) + } + } + } else { + diff := half.nextSeq.Difference(seq) + if diff > 0 { + if *debugLog { + log.Printf("%v gap in sequence numbers (%v, %v) diff %v, storing into connection", key, half.nextSeq, seq, diff) + } + } else { + if *debugLog { + log.Printf("%v found contiguous data (%v, %v), returning immediately: len:%d", key, seq, half.nextSeq, len(bytes)) + } + action.queue = false + } + } + + action = a.handleBytes(bytes, seq, half, ci, t.SYN, t.RST || t.FIN, action, ac) + if len(a.ret) > 0 { + action.nextSeq = a.sendToConnection(conn, half, ac) + } + if action.nextSeq != invalidSequence { + half.nextSeq = action.nextSeq + if t.FIN { + half.nextSeq = half.nextSeq.Add(1) + } + } + if *debugLog { + log.Printf("%v nextSeq:%d", key, half.nextSeq) + } +} + +// Overlap strategies: +// - new packet overlaps with sent packets: +// 1) discard new overlapping part +// 2) overwrite old overlapped (TODO) +// - new packet overlaps existing queued packets: +// a) consider "age" by timestamp (TODO) +// b) consider "age" by being present +// Then +// 1) discard new overlapping part +// 2) overwrite queued part + +func (a *Assembler) checkOverlap(half *halfconnection, queue bool, ac AssemblerContext) { + var next *page + cur := half.last + bytes := a.cacheLP.bytes + start := a.cacheLP.seq + end := start.Add(len(bytes)) + + a.dump("before checkOverlap", half) + + // [s6 : e6] + // [s1:e1][s2:e2] -- [s3:e3] -- [s4:e4][s5:e5] + // [s <--ds-- : --de--> e] + for cur != nil { + + if *debugLog { + log.Printf("cur = %p (%s)\n", cur, cur) + } + + // end < cur.start: continue (5) + if end.Difference(cur.seq) > 0 { + if *debugLog { + log.Printf("case 5\n") + } + next = cur + cur = cur.prev + continue + } + + curEnd := cur.seq.Add(len(cur.bytes)) + // start > cur.end: stop (1) + if start.Difference(curEnd) <= 0 { + if *debugLog { + log.Printf("case 1\n") + } + break + } + + diffStart := start.Difference(cur.seq) + diffEnd := end.Difference(curEnd) + + // end > cur.end && start < cur.start: drop (3) + if diffEnd <= 0 && diffStart >= 0 { + if *debugLog { + log.Printf("case 3\n") + } + if cur.isPacket() { + half.overlapPackets++ + } + half.overlapBytes += len(cur.bytes) + // update links + if cur.prev != nil { + cur.prev.next = cur.next + } else { + half.first = cur.next + } + if cur.next != nil { + cur.next.prev = cur.prev + } else { + half.last = cur.prev + } + tmp := cur.prev + half.pages -= cur.release(a.pc) + cur = tmp + continue + } + + // end > cur.end && start < cur.end: drop cur's end (2) + if diffEnd < 0 && start.Difference(curEnd) > 0 { + if *debugLog { + log.Printf("case 2\n") + } + cur.bytes = cur.bytes[:-start.Difference(cur.seq)] + break + } else + + // start < cur.start && end > cur.start: drop cur's start (4) + if diffStart > 0 && end.Difference(cur.seq) < 0 { + if *debugLog { + log.Printf("case 4\n") + } + cur.bytes = cur.bytes[-end.Difference(cur.seq):] + cur.seq = cur.seq.Add(-end.Difference(cur.seq)) + next = cur + } else + + // end < cur.end && start > cur.start: replace bytes inside cur (6) + if diffEnd > 0 && diffStart < 0 { + if *debugLog { + log.Printf("case 6\n") + } + copy(cur.bytes[-diffStart:-diffStart+len(bytes)], bytes) + bytes = bytes[:0] + } else { + if *debugLog { + log.Printf("no overlap\n") + } + next = cur + } + cur = cur.prev + } + + // Split bytes into pages, and insert in queue + a.cacheLP.bytes = bytes + a.cacheLP.seq = start + if len(bytes) > 0 && queue { + p, p2, numPages := a.cacheLP.convertToPages(a.pc, 0, ac) + half.queuedPackets++ + half.queuedBytes += len(bytes) + half.pages += numPages + if cur != nil { + if *debugLog { + log.Printf("adding %s after %s", p, cur) + } + cur.next = p + p.prev = cur + } else { + if *debugLog { + log.Printf("adding %s as first", p) + } + half.first = p + } + if next != nil { + if *debugLog { + log.Printf("setting %s as next of new %s", next, p2) + } + p2.next = next + next.prev = p2 + } else { + if *debugLog { + log.Printf("setting %s as last", p2) + } + half.last = p2 + } + } + a.dump("After checkOverlap", half) +} + +// Warning: this is a low-level dumper, i.e. a.ret or a.cacheSG might +// be strange, but it could be ok. +func (a *Assembler) dump(text string, half *halfconnection) { + if !*debugLog { + return + } + log.Printf("%s: dump\n", text) + if half != nil { + p := half.first + if p == nil { + log.Printf(" * half.first = %p, no chunks queued\n", p) + } else { + s := 0 + nb := 0 + log.Printf(" * half.first = %p, queued chunks:", p) + for p != nil { + log.Printf("\t%s bytes:%s\n", p, hex.EncodeToString(p.bytes)) + s += len(p.bytes) + nb++ + p = p.next + } + log.Printf("\t%d chunks for %d bytes", nb, s) + } + log.Printf(" * half.last = %p\n", half.last) + log.Printf(" * half.saved = %p\n", half.saved) + p = half.saved + for p != nil { + log.Printf("\tseq:%d %s bytes:%s\n", p.getSeq(), p, hex.EncodeToString(p.bytes)) + p = p.next + } + } + log.Printf(" * a.ret\n") + for i, r := range a.ret { + log.Printf("\t%d: %s b:%s\n", i, r.captureInfo(), hex.EncodeToString(r.getBytes())) + } + log.Printf(" * a.cacheSG.all\n") + for i, r := range a.cacheSG.all { + log.Printf("\t%d: %s b:%s\n", i, r.captureInfo(), hex.EncodeToString(r.getBytes())) + } +} + +func (a *Assembler) overlapExisting(half *halfconnection, start, end Sequence, bytes []byte) ([]byte, Sequence) { + if half.nextSeq == invalidSequence { + // no start yet + return bytes, start + } + diff := start.Difference(half.nextSeq) + if diff == 0 { + return bytes, start + } + s := 0 + e := len(bytes) + // TODO: depending on strategy, we might want to shrink half.saved if possible + if e != 0 { + if *debugLog { + log.Printf("Overlap detected: ignoring current packet's first %d bytes", diff) + } + half.overlapPackets++ + half.overlapBytes += diff + } + start = start.Add(diff) + s += diff + if s >= e { + // Completely included in sent + s = e + } + bytes = bytes[s:] + e -= diff + return bytes, start +} + +// Prepare send or queue +func (a *Assembler) handleBytes(bytes []byte, seq Sequence, half *halfconnection, ci gopacket.CaptureInfo, start bool, end bool, action assemblerAction, ac AssemblerContext) assemblerAction { + a.cacheLP.bytes = bytes + a.cacheLP.start = start + a.cacheLP.end = end + a.cacheLP.seq = seq + a.cacheLP.ci = ci + a.cacheLP.ac = ac + + if action.queue { + a.checkOverlap(half, true, ac) + if (a.MaxBufferedPagesPerConnection > 0 && half.pages >= a.MaxBufferedPagesPerConnection) || + (a.MaxBufferedPagesTotal > 0 && a.pc.used >= a.MaxBufferedPagesTotal) { + if *debugLog { + log.Printf("hit max buffer size: %+v, %v, %v", a.AssemblerOptions, half.pages, a.pc.used) + } + action.queue = false + a.addNextFromConn(half) + } + a.dump("handleBytes after queue", half) + } else { + a.cacheLP.bytes, a.cacheLP.seq = a.overlapExisting(half, seq, seq.Add(len(bytes)), a.cacheLP.bytes) + a.checkOverlap(half, false, ac) + if len(a.cacheLP.bytes) != 0 || end || start { + a.ret = append(a.ret, &a.cacheLP) + } + a.dump("handleBytes after no queue", half) + } + return action +} + +func (a *Assembler) setStatsToSG(half *halfconnection) { + a.cacheSG.queuedBytes = half.queuedBytes + half.queuedBytes = 0 + a.cacheSG.queuedPackets = half.queuedPackets + half.queuedPackets = 0 + a.cacheSG.overlapBytes = half.overlapBytes + half.overlapBytes = 0 + a.cacheSG.overlapPackets = half.overlapPackets + half.overlapPackets = 0 +} + +// Build the ScatterGather object, i.e. prepend saved bytes and +// append continuous bytes. +func (a *Assembler) buildSG(half *halfconnection) (bool, Sequence) { + // find if there are skipped bytes + skip := -1 + if half.nextSeq != invalidSequence { + skip = half.nextSeq.Difference(a.ret[0].getSeq()) + } + last := a.ret[0].getSeq().Add(a.ret[0].length()) + // Prepend saved bytes + saved := a.addPending(half, a.ret[0].getSeq()) + // Append continuous bytes + nextSeq := a.addContiguous(half, last) + a.cacheSG.all = a.ret + a.cacheSG.Direction = half.dir + a.cacheSG.Skip = skip + a.cacheSG.saved = saved + a.cacheSG.toKeep = -1 + a.setStatsToSG(half) + a.dump("after buildSG", half) + return a.ret[len(a.ret)-1].isEnd(), nextSeq +} + +func (a *Assembler) cleanSG(half *halfconnection, ac AssemblerContext) { + cur := 0 + ndx := 0 + skip := 0 + + a.dump("cleanSG(start)", half) + + var r byteContainer + // Find first page to keep + if a.cacheSG.toKeep < 0 { + ndx = len(a.cacheSG.all) + } else { + skip = a.cacheSG.toKeep + found := false + for ndx, r = range a.cacheSG.all { + if a.cacheSG.toKeep < cur+r.length() { + found = true + break + } + cur += r.length() + if skip >= r.length() { + skip -= r.length() + } + } + if !found { + ndx++ + } + } + // Release consumed pages + for _, r := range a.cacheSG.all[:ndx] { + if r == half.saved { + if half.saved.next != nil { + half.saved.next.prev = nil + } + half.saved = half.saved.next + } else if r == half.first { + if half.first.next != nil { + half.first.next.prev = nil + } + if half.first == half.last { + half.first, half.last = nil, nil + } else { + half.first = half.first.next + } + } + half.pages -= r.release(a.pc) + } + a.dump("after consumed release", half) + // Keep un-consumed pages + nbKept := 0 + half.saved = nil + var saved *page + for _, r := range a.cacheSG.all[ndx:] { + first, last, nb := r.convertToPages(a.pc, skip, ac) + if half.saved == nil { + half.saved = first + } else { + saved.next = first + first.prev = saved + } + saved = last + nbKept += nb + } + if *debugLog { + log.Printf("Remaining %d chunks in SG\n", nbKept) + log.Printf("%s\n", a.Dump()) + a.dump("after cleanSG()", half) + } +} + +// sendToConnection sends the current values in a.ret to the connection, closing +// the connection if the last thing sent had End set. +func (a *Assembler) sendToConnection(conn *connection, half *halfconnection, ac AssemblerContext) Sequence { + if *debugLog { + log.Printf("sendToConnection\n") + } + end, nextSeq := a.buildSG(half) + half.stream.ReassembledSG(&a.cacheSG, ac) + a.cleanSG(half, ac) + if end { + a.closeHalfConnection(conn, half) + } + if *debugLog { + log.Printf("after sendToConnection: nextSeq: %d\n", nextSeq) + } + return nextSeq +} + +// +func (a *Assembler) addPending(half *halfconnection, firstSeq Sequence) int { + if half.saved == nil { + return 0 + } + s := 0 + ret := []byteContainer{} + for p := half.saved; p != nil; p = p.next { + if *debugLog { + log.Printf("adding pending @%p %s (%s)\n", p, p, hex.EncodeToString(p.bytes)) + } + ret = append(ret, p) + s += len(p.bytes) + } + if half.saved.seq.Add(s) != firstSeq { + // non-continuous saved: drop them + var next *page + for p := half.saved; p != nil; p = next { + next = p.next + p.release(a.pc) + } + half.saved = nil + ret = []byteContainer{} + s = 0 + } + + a.ret = append(ret, a.ret...) + return s +} + +// addContiguous adds contiguous byte-sets to a connection. +func (a *Assembler) addContiguous(half *halfconnection, lastSeq Sequence) Sequence { + page := half.first + if page == nil { + if *debugLog { + log.Printf("addContiguous(%d): no pages\n", lastSeq) + } + return lastSeq + } + if lastSeq == invalidSequence { + lastSeq = page.seq + } + for page != nil && lastSeq.Difference(page.seq) == 0 { + if *debugLog { + log.Printf("addContiguous: lastSeq: %d, first.seq=%d, page.seq=%d\n", half.nextSeq, half.first.seq, page.seq) + } + lastSeq = lastSeq.Add(len(page.bytes)) + a.ret = append(a.ret, page) + half.first = page.next + if half.first == nil { + half.last = nil + } + if page.next != nil { + page.next.prev = nil + } + page = page.next + } + return lastSeq +} + +// skipFlush skips the first set of bytes we're waiting for and returns the +// first set of bytes we have. If we have no bytes saved, it closes the +// connection. +func (a *Assembler) skipFlush(conn *connection, half *halfconnection) { + if *debugLog { + log.Printf("skipFlush %v\n", half.nextSeq) + } + // Well, it's embarassing it there is still something in half.saved + // FIXME: change API to give back saved + new/no packets + if half.first == nil { + a.closeHalfConnection(conn, half) + return + } + a.ret = a.ret[:0] + a.addNextFromConn(half) + nextSeq := a.sendToConnection(conn, half, a.ret[0].assemblerContext()) + if nextSeq != invalidSequence { + half.nextSeq = nextSeq + } +} + +func (a *Assembler) closeHalfConnection(conn *connection, half *halfconnection) { + if *debugLog { + log.Printf("%v closing", conn) + } + half.closed = true + for p := half.first; p != nil; p = p.next { + // FIXME: it should be already empty + a.pc.replace(p) + half.pages-- + } + if conn.s2c.closed && conn.c2s.closed { + if half.stream.ReassemblyComplete(nil) { //FIXME: which context to pass ? + a.connPool.remove(conn) + } + } +} + +// addNextFromConn pops the first page from a connection off and adds it to the +// return array. +func (a *Assembler) addNextFromConn(conn *halfconnection) { + if conn.first == nil { + return + } + if *debugLog { + log.Printf(" adding from conn (%v, %v) %v (%d)\n", conn.first.seq, conn.nextSeq, conn.nextSeq-conn.first.seq, len(conn.first.bytes)) + } + a.ret = append(a.ret, conn.first) + conn.first = conn.first.next + if conn.first != nil { + conn.first.prev = nil + } else { + conn.last = nil + } +} + +// FlushOptions provide options for flushing connections. +type FlushOptions struct { + T time.Time // If nonzero, only connections with data older than T are flushed + TC time.Time // If nonzero, only connections with data older than TC are closed (if no FIN/RST received) +} + +// FlushWithOptions finds any streams waiting for packets older than +// the given time T, and pushes through the data they have (IE: tells +// them to stop waiting and skip the data they're waiting for). +// +// It also closes streams older than TC (that can be set to zero, to keep +// long-lived stream alive, but to flush data anyway). +// +// Each Stream maintains a list of zero or more sets of bytes it has received +// out-of-order. For example, if it has processed up through sequence number +// 10, it might have bytes [15-20), [20-25), [30,50) in its list. Each set of +// bytes also has the timestamp it was originally viewed. A flush call will +// look at the smallest subsequent set of bytes, in this case [15-20), and if +// its timestamp is older than the passed-in time, it will push it and all +// contiguous byte-sets out to the Stream's Reassembled function. In this case, +// it will push [15-20), but also [20-25), since that's contiguous. It will +// only push [30-50) if its timestamp is also older than the passed-in time, +// otherwise it will wait until the next FlushCloseOlderThan to see if bytes +// [25-30) come in. +// +// Returns the number of connections flushed, and of those, the number closed +// because of the flush. +func (a *Assembler) FlushWithOptions(opt FlushOptions) (flushed, closed int) { + conns := a.connPool.connections() + closes := 0 + flushes := 0 + for _, conn := range conns { + remove := false + conn.mu.Lock() + for _, half := range []*halfconnection{&conn.s2c, &conn.c2s} { + flushed, closed := a.flushClose(conn, half, opt.T, opt.TC) + if flushed { + flushes++ + } + if closed { + closes++ + } + } + if conn.s2c.closed && conn.c2s.closed && conn.s2c.lastSeen.Before(opt.TC) && conn.c2s.lastSeen.Before(opt.TC) { + remove = true + } + conn.mu.Unlock() + if remove { + a.connPool.remove(conn) + } + } + return flushes, closes +} + +// FlushCloseOlderThan flushes and closes streams older than given time +func (a *Assembler) FlushCloseOlderThan(t time.Time) (flushed, closed int) { + return a.FlushWithOptions(FlushOptions{T: t, TC: t}) +} + +func (a *Assembler) flushClose(conn *connection, half *halfconnection, t time.Time, tc time.Time) (bool, bool) { + flushed, closed := false, false + if half.closed { + return flushed, closed + } + for half.first != nil && half.first.seen.Before(t) { + flushed = true + a.skipFlush(conn, half) + if half.closed { + closed = true + } + } + if !half.closed && half.first == nil && half.lastSeen.Before(tc) { + a.closeHalfConnection(conn, half) + closed = true + } + return flushed, closed +} + +// FlushAll flushes all remaining data into all remaining connections and closes +// those connections. It returns the total number of connections flushed/closed +// by the call. +func (a *Assembler) FlushAll() (closed int) { + conns := a.connPool.connections() + closed = len(conns) + for _, conn := range conns { + conn.mu.Lock() + for _, half := range []*halfconnection{&conn.s2c, &conn.c2s} { + for !half.closed { + a.skipFlush(conn, half) + } + if !half.closed { + a.closeHalfConnection(conn, half) + } + } + conn.mu.Unlock() + } + return +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/vendor/github.com/google/gopacket/reassembly/tcpassembly_test.go b/vendor/github.com/google/gopacket/reassembly/tcpassembly_test.go new file mode 100644 index 00000000..b29cf2f0 --- /dev/null +++ b/vendor/github.com/google/gopacket/reassembly/tcpassembly_test.go @@ -0,0 +1,1660 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package reassembly + +import ( + "encoding/hex" + "fmt" + "net" + "reflect" + "runtime" + "testing" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +var netFlow gopacket.Flow + +var testDebug = false + +func init() { + netFlow, _ = gopacket.FlowFromEndpoints( + layers.NewIPEndpoint(net.IP{1, 2, 3, 4}), + layers.NewIPEndpoint(net.IP{5, 6, 7, 8})) +} + +type Reassembly struct { + Bytes []byte + Start bool + End bool + Skip int +} + +type testSequence struct { + in layers.TCP + want []Reassembly +} + +/* For benchmark: do nothing */ +type testFactoryBench struct { +} + +func (t *testFactoryBench) New(a, b gopacket.Flow, tcp *layers.TCP, ac AssemblerContext) Stream { + return t +} +func (t *testFactoryBench) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, seq Sequence, start *bool, ac AssemblerContext) bool { + return true +} +func (t *testFactoryBench) ReassembledSG(sg ScatterGather, ac AssemblerContext) { +} +func (t *testFactoryBench) ReassemblyComplete(ac AssemblerContext) bool { + return true +} + +/* For tests: append bytes */ +type testFactory struct { + reassembly []Reassembly +} + +func (t *testFactory) New(a, b gopacket.Flow, tcp *layers.TCP, ac AssemblerContext) Stream { + return t +} +func (t *testFactory) Reassembled(r []Reassembly) { + t.reassembly = r + for i := 0; i < len(r); i++ { + //t.reassembly[i].Seen = time.Time{} + } +} +func (t *testFactory) ReassembledSG(sg ScatterGather, ac AssemblerContext) { + _, start, end, skip := sg.Info() + l, _ := sg.Lengths() + t.reassembly = append(t.reassembly, Reassembly{ + Bytes: sg.Fetch(l), + Skip: skip, + Start: start, + End: end, + }) +} + +func (t *testFactory) ReassemblyComplete(ac AssemblerContext) bool { + return true +} + +func (t *testFactory) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, seq Sequence, start *bool, ac AssemblerContext) bool { + return true +} + +/* For memory checks: counts bytes */ +type testMemoryFactory struct { + bytes int +} + +func (tf *testMemoryFactory) New(a, b gopacket.Flow, tcp *layers.TCP, ac AssemblerContext) Stream { + return tf +} +func (tf *testMemoryFactory) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, seq Sequence, start *bool, ac AssemblerContext) bool { + return true +} +func (tf *testMemoryFactory) ReassembledSG(sg ScatterGather, ac AssemblerContext) { + bytes, _ := sg.Lengths() + tf.bytes += bytes +} +func (tf *testMemoryFactory) ReassemblyComplete(ac AssemblerContext) bool { + return true +} + +/* + * Tests + */ + +func test(t *testing.T, s []testSequence) { + fact := &testFactory{} + p := NewStreamPool(fact) + a := NewAssembler(p) + a.MaxBufferedPagesPerConnection = 4 + for i, test := range s { + fact.reassembly = []Reassembly{} + if testDebug { + fmt.Printf("#### test: #%d: sending:%s\n", i, hex.EncodeToString(test.in.BaseLayer.Payload)) + } + a.Assemble(netFlow, &test.in) + final := []Reassembly{} + if len(test.want) > 0 { + final = append(final, Reassembly{}) + for _, w := range test.want { + final[0].Bytes = append(final[0].Bytes, w.Bytes...) + if w.End { + final[0].End = true + } + if w.Start { + final[0].Start = true + } + if w.Skip != 0 { + final[0].Skip = w.Skip + } + } + } + if !reflect.DeepEqual(fact.reassembly, final) { + t.Fatalf("test %v:\nwant: %v\n got: %v\n", i, final, fact.reassembly) + } + if testDebug { + fmt.Printf("test %v passing...(%s)\n", i, final) + } + } +} + +func TestReorder(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1001, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1004, + BaseLayer: layers.BaseLayer{Payload: []byte{4, 5, 6}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{10, 11, 12}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9}}, + }, + want: []Reassembly{ + Reassembly{ + Skip: -1, + Bytes: []byte{1, 2, 3}, + }, + Reassembly{ + Bytes: []byte{4, 5, 6}, + }, + Reassembly{ + Bytes: []byte{7, 8, 9}, + }, + Reassembly{ + Bytes: []byte{10, 11, 12}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1016, + BaseLayer: layers.BaseLayer{Payload: []byte{2, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1019, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1013, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{1, 2, 3}, + }, + Reassembly{ + Bytes: []byte{2, 2, 3}, + }, + Reassembly{ + Bytes: []byte{3, 2, 3}, + }, + }, + }, + }) +} + +func TestMaxPerSkip(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1000, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{4, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1013, + BaseLayer: layers.BaseLayer{Payload: []byte{5, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1016, + BaseLayer: layers.BaseLayer{Payload: []byte{6, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Skip: 3, + Bytes: []byte{3, 2, 3}, + }, + Reassembly{ + Bytes: []byte{4, 2, 3}, + }, + Reassembly{ + Bytes: []byte{5, 2, 3}, + }, + Reassembly{ + Bytes: []byte{6, 2, 3}, + }, + }, + }, + }) +} + +func TestReorderFast(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1004, + BaseLayer: layers.BaseLayer{Payload: []byte{2, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{2, 2, 3}, + }, + Reassembly{ + Bytes: []byte{3, 2, 3}, + }, + }, + }, + }) +} + +func TestOverlap(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0, 1, 2, 3, 4, 5}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{1, 2, 3, 4, 5}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{6, 7}, + }, + }, + }, + }) +} + +func TestBufferedOverlap1(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0, 1, 2, 3, 4, 5}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + Reassembly{ + Bytes: []byte{1, 2, 3, 4, 5}, + }, + Reassembly{ + Bytes: []byte{6, 7}, + }, + }, + }, + }) +} + +func TestBufferedOverlapCase6(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0, 1, 2, 3, 4, 5}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{10, 11, 12, 13, 14}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + Reassembly{ + Bytes: []byte{11, 12, 13, 14, 5}, + }, + }, + }, + }) +} + +func TestBufferedOverlapExisting(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1000, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1005, + BaseLayer: layers.BaseLayer{Payload: []byte{5, 6, 7, 8, 9, 10}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{8, 9, 10}, + }, + }, + }, + }) +} + +func TestBufferedOverlapReemit(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1000, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1003, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 4, 5}}, + }, + want: []Reassembly{}, + }, + }) +} + +func TestReorderRetransmission2(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1001, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{2, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{2, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{10, 11}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1004, + BaseLayer: layers.BaseLayer{Payload: []byte{6, 6, 6, 2, 2}}, + }, + want: []Reassembly{ + Reassembly{ + Skip: -1, + Bytes: []byte{1, 2, 3}, + }, + Reassembly{ + Bytes: []byte{6, 6, 6}, + }, + Reassembly{ + Bytes: []byte{2, 2, 3}, + }, + Reassembly{ + Bytes: []byte{10, 11}, + }, + }, + }, + }) +} + +func TestOverrun1(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 0xFFFFFFFF, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 10, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{1, 2, 3, 4}, + }, + }, + }, + }) +} + +func TestOverrun2(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 10, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 0xFFFFFFFF, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + Reassembly{ + Bytes: []byte{1, 2, 3, 4}, + }, + }, + }, + }) +} + +func TestCacheLargePacket(t *testing.T) { + data := make([]byte, pageBytes*3) + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1001, + BaseLayer: layers.BaseLayer{Payload: data}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1000, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{}, + }, + Reassembly{ + Bytes: data[:pageBytes], + }, + Reassembly{ + Bytes: data[pageBytes : pageBytes*2], + }, + Reassembly{ + Bytes: data[pageBytes*2 : pageBytes*3], + }, + }, + }, + }) +} + +/* + * Keep + */ +type testKeepFactory struct { + keep int + bytes []byte + skipped int + t *testing.T +} + +func (tkf *testKeepFactory) New(a, b gopacket.Flow, tcp *layers.TCP, ac AssemblerContext) Stream { + return tkf +} +func (tkf *testKeepFactory) ReassembledSG(sg ScatterGather, ac AssemblerContext) { + l, _ := sg.Lengths() + _, _, _, tkf.skipped = sg.Info() + tkf.bytes = sg.Fetch(l) + sg.KeepFrom(tkf.keep) +} +func (tkf *testKeepFactory) ReassemblyComplete(ac AssemblerContext) bool { + return true +} + +func (tkf *testKeepFactory) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, seq Sequence, start *bool, ac AssemblerContext) bool { + return true +} + +type testKeepSequence struct { + tcp layers.TCP + keep int + want []byte + skipped int +} + +func testKeep(t *testing.T, s []testKeepSequence) { + fact := &testKeepFactory{t: t} + p := NewStreamPool(fact) + a := NewAssembler(p) + a.MaxBufferedPagesPerConnection = 4 + port := layers.TCPPort(0) + for i, test := range s { + // Fake some values according to ports + flow := netFlow + dir := TCPDirClientToServer + if port == 0 { + port = test.tcp.SrcPort + } + if port != test.tcp.SrcPort { + dir = dir.Reverse() + flow = flow.Reverse() + } + test.tcp.SetInternalPortsForTesting() + fact.keep = test.keep + fact.bytes = []byte{} + if testDebug { + fmt.Printf("#### testKeep: #%d: sending:%s\n", i, hex.EncodeToString(test.tcp.BaseLayer.Payload)) + } + a.Assemble(flow, &test.tcp) + if !reflect.DeepEqual(fact.bytes, test.want) { + t.Fatalf("#%d: invalid bytes: got %v, expected %v", i, fact.bytes, test.want) + } + if fact.skipped != test.skipped { + t.Fatalf("#%d: expecting %d skipped bytes, got %d", i, test.skipped, fact.skipped) + } + if testDebug { + fmt.Printf("#### testKeep: #%d: bytes: %s\n", i, hex.EncodeToString(fact.bytes)) + } + } +} + +func TestKeepSimpleOnBoundary(t *testing.T) { + testKeep(t, []testKeepSequence{ + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + keep: 0, + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0, 1, 2, 3, 4, 5}}, + }, + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5}, + }, + }) +} + +func TestKeepSimpleNotBoundaryLive(t *testing.T) { + testKeep(t, []testKeepSequence{ + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + keep: 1, + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0, 1, 2, 3, 4, 5}}, + }, + want: []byte{2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5}, + }, + }) +} + +func TestKeepSimpleNotBoundaryAlreadyKept(t *testing.T) { + testKeep(t, []testKeepSequence{ + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0x10}}, + }, + keep: 0, // 1→10 + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0x10}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15}}, + }, + keep: 11, // 12→15 + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1016, + BaseLayer: layers.BaseLayer{Payload: []byte{0x16, 0x17, 0x18}}, + }, + want: []byte{0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}, + }, + }) +} + +func TestKeepLonger(t *testing.T) { + testKeep(t, []testKeepSequence{ + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + }, + keep: 0, + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 10, 11, 12, 13, 14, 15}}, + }, + keep: 0, + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{10, 11, 12, 13, 14, 15, 16, 17}}, + }, + want: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, + }, + }) +} + +func TestKeepWithFlush(t *testing.T) { + testKeep(t, []testKeepSequence{ + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1}}, + }, + keep: 1, + want: []byte{1}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1003, + BaseLayer: layers.BaseLayer{Payload: []byte{3}}, + }, + keep: 0, + want: []byte{}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1004, + BaseLayer: layers.BaseLayer{Payload: []byte{4}}, + }, + keep: 0, + want: []byte{}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1006, + BaseLayer: layers.BaseLayer{Payload: []byte{6}}, + }, + keep: 0, + want: []byte{}, + }, + // Exceeding 4 pages: flushing first continuous pages + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1008, + BaseLayer: layers.BaseLayer{Payload: []byte{8}}, + }, + keep: 0, + skipped: 1, + want: []byte{3, 4}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{10}}, + }, + keep: 0, + skipped: 1, + want: []byte{6}, + }, + { + tcp: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1012, + BaseLayer: layers.BaseLayer{Payload: []byte{12}}, + }, + keep: 0, + skipped: 1, + want: []byte{8}, + }, + }) +} + +/* + * FSM tests + */ +/* For FSM: bump nb on accepted packet */ +type testFSMFactory struct { + nb int + fsm TCPSimpleFSM +} + +func (t *testFSMFactory) New(a, b gopacket.Flow, tcp *layers.TCP, ac AssemblerContext) Stream { + return t +} +func (t *testFSMFactory) ReassembledSG(sg ScatterGather, ac AssemblerContext) { +} +func (t *testFSMFactory) ReassemblyComplete(ac AssemblerContext) bool { + return false +} + +func (t *testFSMFactory) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, seq Sequence, start *bool, ac AssemblerContext) bool { + ok := t.fsm.CheckState(tcp, dir) + if ok { + t.nb++ + } + return ok +} + +type testFSMSequence struct { + tcp layers.TCP + ci gopacket.CaptureInfo + nb int +} + +func (seq *testFSMSequence) GetCaptureInfo() gopacket.CaptureInfo { + return seq.ci +} + +func testFSM(t *testing.T, s []testFSMSequence) { + fact := &testFSMFactory{} + p := NewStreamPool(fact) + a := NewAssembler(p) + //a.MaxBufferedPagesPerConnection = 4 + fact.nb = 0 + port := layers.TCPPort(0) + for i, test := range s { + // Fake some values according to ports + flow := netFlow + dir := TCPDirClientToServer + if port == 0 { + port = test.tcp.SrcPort + } + if port != test.tcp.SrcPort { + dir = dir.Reverse() + flow = flow.Reverse() + } + test.tcp.SetInternalPortsForTesting() + a.AssembleWithContext(flow, &test.tcp, &test) + if fact.nb != test.nb { + t.Fatalf("#%d: packet rejected: got %d, expected %d", i, fact.nb, test.nb) + } + } +} + +func TestFSMnormalFlow(t *testing.T) { + testFSM(t, []testFSMSequence{ + { + tcp: layers.TCP{ + SYN: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511116, + Ack: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 566690000), + }, + nb: 1, + }, + { + tcp: layers.TCP{ + SYN: true, + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787765, + Ack: 374511117, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590332000), + }, + nb: 2, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590346000), + }, + nb: 3, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 31, 104, 196, 0, 32, 0, 1, 0, 0, 0, 0, 0, 1, 2, 85, 83, 0, 0, 6, 0, 1, 0, 0, 41, 16, 0, 0, 0, 128, 0, 0, 0}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + }, + nb: 4, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787766, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 613687000), + }, + nb: 5, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787766, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{8, 133, 104, 196, 132, 0, 0, 1, 0, 2, 0, 7, 0, 19, 2, 85, 83, 0, 0, 6, 0, 1, 2, 117, 115, 0, 0, 6, 0, 1, 0, 0, 3, 132, 0, 54, 1, 97, 5, 99, 99, 116, 108, 100, 192, 20, 10, 104, 111, 115, 116, 109, 97, 115, 116, 101, 114, 7, 110, 101, 117, 115, 116, 97, 114, 3, 98, 105, 122, 0, 120, 18, 40, 205, 0, 0, 3, 132, 0, 0, 3, 132, 0, 9, 58, 128, 0, 1, 81, 128, 192, 20, 0, 46, 0, 1, 0, 0, 3, 132, 0, 150, 0, 6, 5, 1, 0, 0, 3, 132, 85, 138, 90, 146, 85, 98, 191, 130, 27, 78, 2, 117, 115, 0, 69, 13, 35, 189, 141, 225, 107, 238, 108, 182, 207, 44, 105, 31, 212, 103, 32, 93, 217, 108, 20, 231, 188, 28, 241, 237, 104, 182, 117, 121, 195, 112, 64, 96, 237, 248, 6, 181, 186, 96, 60, 6, 18, 29, 188, 96, 201, 140, 251, 61, 71, 177, 108, 156, 9, 83, 125, 172, 188, 75, 81, 67, 218, 55, 93, 131, 243, 15, 190, 75, 4, 165, 226, 124, 49, 67, 142, 131, 239, 240, 76, 225, 10, 242, 68, 88, 240, 200, 27, 97, 102, 73, 92, 73, 133, 170, 175, 198, 99, 109, 90, 16, 162, 101, 95, 96, 102, 250, 91, 74, 80, 3, 87, 167, 50, 230, 9, 213, 7, 222, 197, 87, 183, 190, 148, 247, 207, 204, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 10, 1, 102, 5, 99, 99, 116, 108, 100, 192, 12, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 97, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 98, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 99, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 101, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 107, 193, 8, 192, 118, 0, 46, 0, 1, 0, 7, 233, 0, 0, 150, 0, 2, 5, 1, 0, 7, 233, 0, 85, 127, 33, 92, 85, 87, 134, 98, 27, 78, 2, 117, 115, 0, 19, 227, 175, 75, 88, 245, 164, 158, 150, 198, 57, 253, 150, 179, 161, 52, 24, 56, 229, 176, 175, 40, 45, 232, 188, 171, 131, 197, 107, 125, 218, 192, 78, 221, 146, 33, 114, 55, 43, 12, 131, 213, 51, 98, 37, 2, 102, 161, 232, 115, 177, 210, 51, 169, 215, 133, 56, 190, 91, 75, 8, 222, 231, 202, 139, 28, 187, 249, 72, 21, 23, 56, 63, 72, 126, 142, 242, 195, 242, 64, 208, 134, 100, 157, 197, 159, 43, 148, 20, 70, 117, 152, 159, 35, 200, 220, 49, 234, 173, 210, 91, 34, 210, 192, 7, 197, 112, 117, 208, 234, 42, 49, 133, 237, 197, 14, 244, 149, 191, 142, 36, 252, 42, 48, 182, 189, 9, 68, 1, 65, 5, 67, 67, 84, 76, 68, 193, 126, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 124, 70, 1, 66, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 125, 70, 194, 26, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 3, 209, 174, 255, 255, 255, 255, 255, 255, 255, 255, 255, 126, 1, 67, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 127, 70, 1, 69, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 126, 70, 1, 70, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 209, 173, 58, 70, 194, 108, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 0, 54, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 1, 75, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 128, 70, 194, 154, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 3, 226, 57, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 194, 2, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 230, 49, 85, 73, 83, 2, 27, 78, 2, 117, 115, 0, 82, 36, 11, 141, 74, 85, 70, 98, 179, 63, 173, 83, 8, 70, 155, 41, 102, 166, 140, 62, 71, 178, 130, 38, 171, 200, 180, 68, 2, 215, 45, 6, 43, 59, 171, 146, 223, 215, 9, 77, 5, 104, 167, 42, 237, 170, 30, 114, 205, 129, 59, 225, 152, 224, 79, 1, 65, 68, 208, 153, 121, 237, 199, 87, 2, 251, 100, 105, 59, 24, 73, 226, 169, 121, 250, 91, 41, 124, 14, 23, 135, 52, 2, 86, 72, 224, 100, 135, 70, 216, 16, 107, 84, 59, 13, 168, 58, 187, 54, 98, 230, 167, 246, 42, 46, 156, 206, 238, 120, 199, 25, 144, 98, 249, 70, 162, 34, 43, 145, 114, 186, 233, 47, 42, 75, 95, 152, 235, 194, 26, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 160, 95, 100, 37, 167, 82, 93, 165, 126, 247, 147, 173, 238, 154, 206, 174, 96, 175, 209, 7, 8, 169, 171, 223, 29, 201, 161, 177, 98, 54, 94, 62, 70, 127, 142, 109, 206, 42, 179, 109, 156, 160, 156, 20, 59, 24, 147, 164, 13, 121, 192, 84, 157, 26, 56, 177, 151, 210, 7, 197, 229, 110, 60, 58, 224, 42, 77, 5, 59, 80, 216, 221, 248, 19, 66, 102, 74, 199, 238, 120, 231, 201, 187, 29, 11, 46, 195, 164, 8, 221, 128, 25, 205, 42, 247, 152, 112, 176, 14, 117, 150, 223, 245, 32, 212, 107, 4, 245, 27, 126, 224, 216, 0, 89, 106, 238, 185, 206, 44, 56, 204, 175, 7, 139, 233, 228, 127, 175, 194, 26, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 28, 5, 3, 0, 0, 28, 32, 85, 108, 217, 174, 85, 69, 70, 242, 27, 78, 2, 117, 115, 0, 172, 117, 89, 89, 73, 249, 245, 211, 100, 127, 48, 135, 224, 97, 172, 146, 128, 30, 190, 72, 199, 170, 97, 179, 136, 109, 86, 110, 235, 214, 47, 50, 115, 11, 226, 168, 56, 198, 24, 212, 205, 207, 2, 116, 104, 112, 99, 234, 236, 44, 70, 19, 19, 215, 127, 200, 162, 215, 142, 45, 135, 91, 219, 217, 86, 231, 154, 87, 222, 161, 32, 66, 196, 55, 117, 20, 186, 9, 134, 252, 249, 219, 9, 196, 128, 8, 222, 201, 131, 210, 182, 232, 142, 72, 160, 171, 95, 231, 232, 156, 28, 34, 54, 94, 73, 183, 38, 160, 123, 175, 157, 21, 163, 8, 214, 155, 172, 237, 169, 28, 15, 138, 105, 107, 251, 109, 131, 240, 194, 72, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 77, 207, 197, 130, 236, 138, 192, 241, 225, 114, 8, 22, 76, 54, 43, 121, 42, 44, 9, 92, 56, 253, 224, 179, 191, 131, 40, 176, 94, 61, 33, 12, 43, 82, 156, 236, 211, 29, 187, 100, 220, 243, 24, 134, 42, 204, 46, 161, 214, 91, 68, 119, 40, 252, 53, 54, 146, 136, 196, 168, 204, 195, 131, 110, 6, 73, 16, 161, 86, 35, 150, 153, 162, 185, 227, 65, 228, 160, 203, 42, 250, 121, 14, 42, 115, 221, 232, 96, 99, 164, 230, 29, 195, 149, 85, 206, 41, 1, 252, 77, 188, 88, 8, 182, 37, 249, 6, 158, 6, 244, 158, 254, 141, 203, 6, 158, 198, 103, 130, 98, 123, 34, 245, 44, 126, 77, 24, 187, 194, 90, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 108, 194, 203, 85, 69, 51, 125, 27, 78, 2, 117, 115, 0, 86, 26, 187, 56, 252, 194, 199, 140, 229, 133, 186, 187, 20, 174, 26, 48, 212, 129, 10, 20, 167, 179, 53, 72, 176, 92, 153, 48, 146, 15, 163, 182, 80, 138, 181, 135, 98, 129, 17, 66, 55, 184, 76, 225, 72, 104, 7, 221, 40, 71, 41, 202, 246, 154, 166, 199, 74, 175, 146, 54, 25, 56, 115, 243}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 621198000), + }, + nb: 6, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511150, + Ack: 3465789226, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 621220000), + }, + nb: 7, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465789226, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{153, 141, 101, 187, 110, 15, 63, 42, 81, 100, 95, 68, 241, 85, 160, 227, 3, 1, 12, 80, 166, 1, 98, 2, 44, 98, 63, 203, 70, 164, 99, 195, 23, 152, 223, 253, 208, 10, 12, 19, 66, 121, 9, 158, 205, 96, 218, 0, 80, 70, 58, 95, 41, 124, 216, 13, 122, 135, 102, 200, 181, 233, 129, 174, 194, 108, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 108, 223, 157, 85, 69, 74, 55, 27, 78, 2, 117, 115, 0, 149, 71, 215, 149, 16, 165, 115, 229, 141, 136, 187, 158, 88, 225, 131, 231, 182, 218, 235, 27, 48, 65, 244, 77, 186, 135, 72, 18, 87, 52, 180, 128, 130, 67, 75, 173, 160, 243, 104, 178, 103, 117, 96, 209, 36, 51, 108, 47, 232, 214, 254, 15, 208, 182, 218, 174, 248, 237, 88, 150, 35, 190, 239, 249, 171, 151, 9, 236, 2, 252, 255, 13, 79, 190, 147, 36, 161, 210, 202, 80, 209, 136, 167, 180, 186, 68, 246, 249, 48, 123, 46, 11, 132, 103, 132, 207, 186, 68, 110, 133, 142, 109, 194, 19, 122, 57, 203, 217, 120, 93, 67, 168, 91, 252, 87, 38, 33, 228, 229, 162, 190, 170, 23, 188, 89, 15, 241, 71, 194, 108, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 28, 5, 3, 0, 0, 28, 32, 85, 108, 217, 174, 85, 69, 70, 242, 27, 78, 2, 117, 115, 0, 206, 97, 120, 37, 255, 252, 7, 156, 162, 192, 43, 84, 105, 94, 125, 55, 13, 247, 234, 9, 25, 100, 246, 25, 77, 168, 199, 208, 187, 209, 164, 123, 234, 138, 238, 15, 86, 45, 163, 108, 162, 117, 247, 128, 3, 187, 100, 185, 193, 191, 134, 86, 161, 254, 236, 99, 66, 66, 35, 173, 91, 243, 175, 3, 175, 94, 79, 68, 246, 109, 200, 154, 209, 185, 11, 210, 50, 147, 136, 213, 158, 81, 111, 17, 149, 239, 110, 114, 25, 234, 247, 158, 233, 33, 36, 181, 66, 84, 189, 37, 207, 58, 9, 171, 143, 66, 69, 137, 192, 6, 187, 59, 16, 51, 80, 56, 89, 170, 12, 195, 69, 133, 188, 110, 171, 17, 17, 213, 194, 154, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 123, 36, 154, 4, 158, 41, 96, 252, 116, 114, 16, 137, 28, 177, 206, 33, 192, 88, 89, 1, 69, 252, 206, 88, 89, 152, 210, 179, 248, 44, 202, 239, 95, 131, 126, 147, 249, 93, 57, 166, 215, 184, 211, 164, 196, 71, 170, 3, 25, 18, 177, 214, 94, 147, 181, 148, 197, 11, 171, 219, 107, 48, 105, 81, 239, 110, 249, 140, 68, 127, 193, 146, 176, 161, 246, 108, 75, 141, 205, 211, 73, 247, 125, 205, 120, 156, 82, 55, 130, 250, 26, 15, 44, 214, 91, 115, 11, 103, 22, 83, 184, 96, 107, 138, 2, 127, 168, 191, 92, 102, 137, 161, 63, 225, 134, 17, 178, 242, 11, 43, 8, 30, 164, 28, 140, 195, 83, 121, 194, 154, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 28, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 189, 98, 234, 251, 237, 24, 143, 210, 30, 242, 97, 66, 50, 211, 47, 109, 110, 121, 244, 239, 89, 0, 39, 92, 218, 155, 71, 5, 23, 136, 231, 107, 95, 52, 231, 118, 253, 206, 250, 178, 209, 136, 13, 36, 36, 54, 157, 237, 35, 110, 134, 253, 80, 237, 162, 163, 38, 21, 54, 241, 240, 253, 73, 33, 191, 128, 32, 6, 198, 165, 35, 203, 244, 15, 166, 250, 159, 67, 149, 56, 19, 243, 230, 87, 6, 44, 150, 90, 79, 107, 18, 121, 112, 23, 176, 104, 50, 110, 176, 138, 250, 6, 209, 22, 41, 73, 234, 4, 124, 233, 208, 218, 236, 117, 232, 217, 10, 172, 18, 215, 143, 119, 193, 113, 10, 59, 255, 221, 0, 0, 41, 16, 0, 0, 0, 128, 0, 0, 0}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 622508000), + }, + nb: 8, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511150, + Ack: 3465789949, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 622531000), + }, + nb: 9, + }, + { + tcp: layers.TCP{ + ACK: true, + FIN: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511150, + Ack: 3465789949, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 622907000), + }, + nb: 10, + }, + { + tcp: layers.TCP{ + ACK: true, + FIN: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465789949, + Ack: 374511151, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 652784000), + }, + nb: 11, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511151, + Ack: 3465789950, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 652809000), + }, + nb: 12, + }, + }) +} + +func TestFSMearlyRST(t *testing.T) { + testFSM(t, []testFSMSequence{ + { + tcp: layers.TCP{ + SYN: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511116, + Ack: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 566690000), + }, + nb: 1, + }, + { + tcp: layers.TCP{ + SYN: true, + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787765, + Ack: 374511117, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590332000), + }, + nb: 2, + }, + { + tcp: layers.TCP{ + RST: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590346000), + }, + nb: 3, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 31, 104, 196, 0, 32, 0, 1, 0, 0, 0, 0, 0, 1, 2, 85, 83, 0, 0, 6, 0, 1, 0, 0, 41, 16, 0, 0, 0, 128, 0, 0, 0}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + }, + nb: 3, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787766, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 613687000), + }, + nb: 3, + }, + }) +} + +func TestFSMestablishedThenRST(t *testing.T) { + testFSM(t, []testFSMSequence{ + { + tcp: layers.TCP{ + SYN: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511116, + Ack: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 566690000), + }, + nb: 1, + }, + { + tcp: layers.TCP{ + SYN: true, + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787765, + Ack: 374511117, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590332000), + }, + nb: 2, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590346000), + }, + nb: 3, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 31, 104, 196, 0, 32, 0, 1, 0, 0, 0, 0, 0, 1, 2, 85, 83, 0, 0, 6, 0, 1, 0, 0, 41, 16, 0, 0, 0, 128, 0, 0, 0}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + }, + nb: 4, + }, + { + tcp: layers.TCP{ + RST: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787766, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 613687000), + }, + nb: 5, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787766, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{8, 133, 104, 196, 132, 0, 0, 1, 0, 2, 0, 7, 0, 19, 2, 85, 83, 0, 0, 6, 0, 1, 2, 117, 115, 0, 0, 6, 0, 1, 0, 0, 3, 132, 0, 54, 1, 97, 5, 99, 99, 116, 108, 100, 192, 20, 10, 104, 111, 115, 116, 109, 97, 115, 116, 101, 114, 7, 110, 101, 117, 115, 116, 97, 114, 3, 98, 105, 122, 0, 120, 18, 40, 205, 0, 0, 3, 132, 0, 0, 3, 132, 0, 9, 58, 128, 0, 1, 81, 128, 192, 20, 0, 46, 0, 1, 0, 0, 3, 132, 0, 150, 0, 6, 5, 1, 0, 0, 3, 132, 85, 138, 90, 146, 85, 98, 191, 130, 27, 78, 2, 117, 115, 0, 69, 13, 35, 189, 141, 225, 107, 238, 108, 182, 207, 44, 105, 31, 212, 103, 32, 93, 217, 108, 20, 231, 188, 28, 241, 237, 104, 182, 117, 121, 195, 112, 64, 96, 237, 248, 6, 181, 186, 96, 60, 6, 18, 29, 188, 96, 201, 140, 251, 61, 71, 177, 108, 156, 9, 83, 125, 172, 188, 75, 81, 67, 218, 55, 93, 131, 243, 15, 190, 75, 4, 165, 226, 124, 49, 67, 142, 131, 239, 240, 76, 225, 10, 242, 68, 88, 240, 200, 27, 97, 102, 73, 92, 73, 133, 170, 175, 198, 99, 109, 90, 16, 162, 101, 95, 96, 102, 250, 91, 74, 80, 3, 87, 167, 50, 230, 9, 213, 7, 222, 197, 87, 183, 190, 148, 247, 207, 204, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 10, 1, 102, 5, 99, 99, 116, 108, 100, 192, 12, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 97, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 98, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 99, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 101, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 107, 193, 8, 192, 118, 0, 46, 0, 1, 0, 7, 233, 0, 0, 150, 0, 2, 5, 1, 0, 7, 233, 0, 85, 127, 33, 92, 85, 87, 134, 98, 27, 78, 2, 117, 115, 0, 19, 227, 175, 75, 88, 245, 164, 158, 150, 198, 57, 253, 150, 179, 161, 52, 24, 56, 229, 176, 175, 40, 45, 232, 188, 171, 131, 197, 107, 125, 218, 192, 78, 221, 146, 33, 114, 55, 43, 12, 131, 213, 51, 98, 37, 2, 102, 161, 232, 115, 177, 210, 51, 169, 215, 133, 56, 190, 91, 75, 8, 222, 231, 202, 139, 28, 187, 249, 72, 21, 23, 56, 63, 72, 126, 142, 242, 195, 242, 64, 208, 134, 100, 157, 197, 159, 43, 148, 20, 70, 117, 152, 159, 35, 200, 220, 49, 234, 173, 210, 91, 34, 210, 192, 7, 197, 112, 117, 208, 234, 42, 49, 133, 237, 197, 14, 244, 149, 191, 142, 36, 252, 42, 48, 182, 189, 9, 68, 1, 65, 5, 67, 67, 84, 76, 68, 193, 126, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 124, 70, 1, 66, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 125, 70, 194, 26, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 3, 209, 174, 255, 255, 255, 255, 255, 255, 255, 255, 255, 126, 1, 67, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 127, 70, 1, 69, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 126, 70, 1, 70, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 209, 173, 58, 70, 194, 108, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 0, 54, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 1, 75, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 128, 70, 194, 154, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 3, 226, 57, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 194, 2, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 230, 49, 85, 73, 83, 2, 27, 78, 2, 117, 115, 0, 82, 36, 11, 141, 74, 85, 70, 98, 179, 63, 173, 83, 8, 70, 155, 41, 102, 166, 140, 62, 71, 178, 130, 38, 171, 200, 180, 68, 2, 215, 45, 6, 43, 59, 171, 146, 223, 215, 9, 77, 5, 104, 167, 42, 237, 170, 30, 114, 205, 129, 59, 225, 152, 224, 79, 1, 65, 68, 208, 153, 121, 237, 199, 87, 2, 251, 100, 105, 59, 24, 73, 226, 169, 121, 250, 91, 41, 124, 14, 23, 135, 52, 2, 86, 72, 224, 100, 135, 70, 216, 16, 107, 84, 59, 13, 168, 58, 187, 54, 98, 230, 167, 246, 42, 46, 156, 206, 238, 120, 199, 25, 144, 98, 249, 70, 162, 34, 43, 145, 114, 186, 233, 47, 42, 75, 95, 152, 235, 194, 26, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 160, 95, 100, 37, 167, 82, 93, 165, 126, 247, 147, 173, 238, 154, 206, 174, 96, 175, 209, 7, 8, 169, 171, 223, 29, 201, 161, 177, 98, 54, 94, 62, 70, 127, 142, 109, 206, 42, 179, 109, 156, 160, 156, 20, 59, 24, 147, 164, 13, 121, 192, 84, 157, 26, 56, 177, 151, 210, 7, 197, 229, 110, 60, 58, 224, 42, 77, 5, 59, 80, 216, 221, 248, 19, 66, 102, 74, 199, 238, 120, 231, 201, 187, 29, 11, 46, 195, 164, 8, 221, 128, 25, 205, 42, 247, 152, 112, 176, 14, 117, 150, 223, 245, 32, 212, 107, 4, 245, 27, 126, 224, 216, 0, 89, 106, 238, 185, 206, 44, 56, 204, 175, 7, 139, 233, 228, 127, 175, 194, 26, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 28, 5, 3, 0, 0, 28, 32, 85, 108, 217, 174, 85, 69, 70, 242, 27, 78, 2, 117, 115, 0, 172, 117, 89, 89, 73, 249, 245, 211, 100, 127, 48, 135, 224, 97, 172, 146, 128, 30, 190, 72, 199, 170, 97, 179, 136, 109, 86, 110, 235, 214, 47, 50, 115, 11, 226, 168, 56, 198, 24, 212, 205, 207, 2, 116, 104, 112, 99, 234, 236, 44, 70, 19, 19, 215, 127, 200, 162, 215, 142, 45, 135, 91, 219, 217, 86, 231, 154, 87, 222, 161, 32, 66, 196, 55, 117, 20, 186, 9, 134, 252, 249, 219, 9, 196, 128, 8, 222, 201, 131, 210, 182, 232, 142, 72, 160, 171, 95, 231, 232, 156, 28, 34, 54, 94, 73, 183, 38, 160, 123, 175, 157, 21, 163, 8, 214, 155, 172, 237, 169, 28, 15, 138, 105, 107, 251, 109, 131, 240, 194, 72, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 77, 207, 197, 130, 236, 138, 192, 241, 225, 114, 8, 22, 76, 54, 43, 121, 42, 44, 9, 92, 56, 253, 224, 179, 191, 131, 40, 176, 94, 61, 33, 12, 43, 82, 156, 236, 211, 29, 187, 100, 220, 243, 24, 134, 42, 204, 46, 161, 214, 91, 68, 119, 40, 252, 53, 54, 146, 136, 196, 168, 204, 195, 131, 110, 6, 73, 16, 161, 86, 35, 150, 153, 162, 185, 227, 65, 228, 160, 203, 42, 250, 121, 14, 42, 115, 221, 232, 96, 99, 164, 230, 29, 195, 149, 85, 206, 41, 1, 252, 77, 188, 88, 8, 182, 37, 249, 6, 158, 6, 244, 158, 254, 141, 203, 6, 158, 198, 103, 130, 98, 123, 34, 245, 44, 126, 77, 24, 187, 194, 90, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 108, 194, 203, 85, 69, 51, 125, 27, 78, 2, 117, 115, 0, 86, 26, 187, 56, 252, 194, 199, 140, 229, 133, 186, 187, 20, 174, 26, 48, 212, 129, 10, 20, 167, 179, 53, 72, 176, 92, 153, 48, 146, 15, 163, 182, 80, 138, 181, 135, 98, 129, 17, 66, 55, 184, 76, 225, 72, 104, 7, 221, 40, 71, 41, 202, 246, 154, 166, 199, 74, 175, 146, 54, 25, 56, 115, 243}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 621198000), + }, + nb: 5, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511150, + Ack: 3465789226, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 621220000), + }, + nb: 5, + }, + }) +} + +func TestFSMmissingSYNACK(t *testing.T) { + testFSM(t, []testFSMSequence{ + { + tcp: layers.TCP{ + SYN: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511116, + Ack: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 566690000), + }, + nb: 1, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590346000), + }, + nb: 1, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 31, 104, 196, 0, 32, 0, 1, 0, 0, 0, 0, 0, 1, 2, 85, 83, 0, 0, 6, 0, 1, 0, 0, 41, 16, 0, 0, 0, 128, 0, 0, 0}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + }, + nb: 1, + }, + }) +} + +/* + * Memory test + */ +func TestMemoryShrink(t *testing.T) { + tcp := layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 999, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactoryBench{})) + var before runtime.MemStats + runtime.GC() + runtime.ReadMemStats(&before) + run := 1050 + // Allocate > initial + for i := 0; i < run; i++ { + a.Assemble(netFlow, &tcp) + if tcp.SYN { + tcp.SYN = false + tcp.Seq += 1 + 1 + } + tcp.Seq += 10 + } + var after runtime.MemStats + a.FlushAll() + runtime.GC() + runtime.ReadMemStats(&after) + if after.HeapAlloc < before.HeapAlloc { + t.Fatalf("Nothing allocated for %d run: before: %d, after: %d", run, before.HeapAlloc, after.HeapAlloc) + } + before = after + // Do ~ initial allocs+free() + run *= 2 + for i := 0; i < run; i++ { + a.Assemble(netFlow, &tcp) + if i%50 == 0 { + a.FlushAll() + } + tcp.Seq += 10 + } + runtime.GC() + runtime.ReadMemStats(&after) + if after.HeapAlloc >= before.HeapAlloc { + t.Fatalf("Nothing freed for %d run: before: %d, after: %d", run, before.HeapAlloc, after.HeapAlloc) + } +} + +/* + * Benchmark tests + */ +func BenchmarkSingleStreamNo(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactoryBench{})) + for i := 0; i < b.N; i++ { + a.Assemble(netFlow, &t) + if t.SYN { + t.SYN = false + t.Seq++ + } + t.Seq += 10 + } +} + +func BenchmarkSingleStreamSkips(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactoryBench{})) + skipped := false + for i := 0; i < b.N; i++ { + if i%10 == 9 { + t.Seq += 10 + skipped = true + } else if skipped { + t.Seq -= 20 + } + a.Assemble(netFlow, &t) + if t.SYN { + t.SYN = false + t.Seq++ + } + t.Seq += 10 + if skipped { + t.Seq += 10 + skipped = false + } + } +} + +func BenchmarkSingleStreamLoss(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactoryBench{})) + for i := 0; i < b.N; i++ { + a.Assemble(netFlow, &t) + t.SYN = false + t.Seq += 11 + } +} + +func BenchmarkMultiStreamGrow(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactoryBench{})) + for i := 0; i < b.N; i++ { + t.SrcPort = layers.TCPPort(i) + a.Assemble(netFlow, &t) + t.Seq += 10 + } +} + +func BenchmarkMultiStreamConn(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 0, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactoryBench{})) + for i := 0; i < b.N; i++ { + t.SrcPort = layers.TCPPort(i) + a.Assemble(netFlow, &t) + if i%65536 == 65535 { + if t.SYN { + t.SYN = false + t.Seq++ + } + t.Seq += 10 + } + } +} + +type testMemoryContext struct{} + +func (t *testMemoryContext) GetCaptureInfo() gopacket.CaptureInfo { + return gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + } +} + +func TestFullyOrderedAndCompleteStreamDoesNotAlloc(t *testing.T) { + c2s := layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 0, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + s2c := layers.TCP{ + SrcPort: c2s.DstPort, + DstPort: c2s.SrcPort, + Seq: 0, + SYN: true, + ACK: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + tf := testMemoryFactory{} + a := NewAssembler(NewStreamPool(&tf)) + + ctx := &testMemoryContext{} + // First packet + a.AssembleWithContext(netFlow, &c2s, ctx) + a.AssembleWithContext(netFlow.Reverse(), &s2c, ctx) + c2s.SYN, s2c.SYN = false, false + c2s.ACK = true + c2s.Seq++ + s2c.Seq++ + N := 1000 + if n := testing.AllocsPerRun(N, func() { + c2s.Seq += 10 + s2c.Seq += 10 + c2s.Ack += 10 + s2c.Ack += 10 + a.AssembleWithContext(netFlow, &c2s, ctx) + a.AssembleWithContext(netFlow.Reverse(), &s2c, ctx) + }); n > 0 { + t.Error(n, "mallocs for normal TCP stream") + } + // Ensure all bytes have been through the stream + // +1 for first packet and +1 because AllocsPerRun seems to run fun N+1 times. + if tf.bytes != 10*2*(N+1+1) { + t.Error(tf.bytes, "bytes handled, expected", 10*2*(N+1+1)) + } +} diff --git a/vendor/github.com/google/gopacket/reassembly/tcpcheck.go b/vendor/github.com/google/gopacket/reassembly/tcpcheck.go new file mode 100644 index 00000000..4b52abab --- /dev/null +++ b/vendor/github.com/google/gopacket/reassembly/tcpcheck.go @@ -0,0 +1,246 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package reassembly + +import ( + "encoding/binary" + "fmt" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +/* + * Check TCP packet against options (window, MSS) + */ + +type tcpStreamOptions struct { + mss int + scale int + receiveWindow uint +} + +// TCPOptionCheck contains options for the two directions +type TCPOptionCheck struct { + options [2]tcpStreamOptions +} + +func (t *TCPOptionCheck) getOptions(dir TCPFlowDirection) *tcpStreamOptions { + if dir == TCPDirClientToServer { + return &t.options[0] + } + return &t.options[1] +} + +// NewTCPOptionCheck creates default options +func NewTCPOptionCheck() TCPOptionCheck { + return TCPOptionCheck{ + options: [2]tcpStreamOptions{ + tcpStreamOptions{ + mss: 0, + scale: -1, + receiveWindow: 0, + }, tcpStreamOptions{ + mss: 0, + scale: -1, + receiveWindow: 0, + }, + }, + } +} + +// Accept checks whether the packet should be accepted by checking TCP options +func (t *TCPOptionCheck) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir TCPFlowDirection, acked Sequence, start *bool) error { + options := t.getOptions(dir) + if tcp.SYN { + mss := -1 + scale := -1 + for _, o := range tcp.Options { + // MSS + if o.OptionType == 2 { + if len(o.OptionData) != 2 { + return fmt.Errorf("MSS option data length expected 2, got %d", len(o.OptionData)) + } + mss = int(binary.BigEndian.Uint16(o.OptionData[:2])) + } + // Window scaling + if o.OptionType == 3 { + if len(o.OptionData) != 1 { + return fmt.Errorf("Window scaling length expected: 1, got %d", len(o.OptionData)) + } + scale = int(o.OptionData[0]) + } + } + options.mss = mss + options.scale = scale + } else { + if acked != invalidSequence { + revOptions := t.getOptions(dir.Reverse()) + length := len(tcp.Payload) + + // Check packet is in the correct window + diff := acked.Difference(Sequence(tcp.Seq)) + if diff == -1 && (length == 1 || length == 0) { + // This is probably a Keep-alive + // TODO: check byte is ok + } else if diff < 0 { + return fmt.Errorf("Re-emitted packet (diff:%d,seq:%d,rev-ack:%d)", diff, + tcp.Seq, acked) + } else if revOptions.mss > 0 && length > revOptions.mss { + return fmt.Errorf("%d > mss (%d)", length, revOptions.mss) + } else if revOptions.receiveWindow != 0 && revOptions.scale < 0 && diff > int(revOptions.receiveWindow) { + return fmt.Errorf("%d > receiveWindow(%d)", diff, revOptions.receiveWindow) + } + } + } + // Compute receiveWindow + options.receiveWindow = uint(tcp.Window) + if options.scale > 0 { + options.receiveWindow = options.receiveWindow << (uint(options.scale)) + } + return nil +} + +// TCPSimpleFSM implements a very simple TCP state machine +// +// Usage: +// When implementing a Stream interface and to avoid to consider packets that +// would be rejected due to client/server's TCP stack, the Accept() can call +// TCPSimpleFSM.CheckState(). +// +// Limitations: +// - packet should be received in-order. +// - no check on sequence number is performed +// - no RST +type TCPSimpleFSM struct { + dir TCPFlowDirection + state int + options TCPSimpleFSMOptions +} + +// TCPSimpleFSMOptions holds options for TCPSimpleFSM +type TCPSimpleFSMOptions struct { + SupportMissingEstablishment bool // Allow missing SYN, SYN+ACK, ACK +} + +// Internal values of state machine +const ( + TCPStateClosed = 0 + TCPStateSynSent = 1 + TCPStateEstablished = 2 + TCPStateCloseWait = 3 + TCPStateLastAck = 4 + TCPStateReset = 5 +) + +// NewTCPSimpleFSM creates a new TCPSimpleFSM +func NewTCPSimpleFSM(options TCPSimpleFSMOptions) *TCPSimpleFSM { + return &TCPSimpleFSM{ + state: TCPStateClosed, + options: options, + } +} + +func (t *TCPSimpleFSM) String() string { + switch t.state { + case TCPStateClosed: + return "Closed" + case TCPStateSynSent: + return "SynSent" + case TCPStateEstablished: + return "Established" + case TCPStateCloseWait: + return "CloseWait" + case TCPStateLastAck: + return "LastAck" + case TCPStateReset: + return "Reset" + } + return "?" +} + +// CheckState returns false if tcp is invalid wrt current state or update the state machine's state +func (t *TCPSimpleFSM) CheckState(tcp *layers.TCP, dir TCPFlowDirection) bool { + if t.state == TCPStateClosed && t.options.SupportMissingEstablishment && !(tcp.SYN && !tcp.ACK) { + /* try to figure out state */ + switch true { + case tcp.SYN && tcp.ACK: + t.state = TCPStateSynSent + t.dir = dir.Reverse() + case tcp.FIN && !tcp.ACK: + t.state = TCPStateEstablished + case tcp.FIN && tcp.ACK: + t.state = TCPStateCloseWait + t.dir = dir.Reverse() + default: + t.state = TCPStateEstablished + } + } + + switch t.state { + /* openning connection */ + case TCPStateClosed: + if tcp.SYN && !tcp.ACK { + t.dir = dir + t.state = TCPStateSynSent + return true + } + case TCPStateSynSent: + if tcp.RST { + t.state = TCPStateReset + return true + } + + if tcp.SYN && tcp.ACK && dir == t.dir.Reverse() { + t.state = TCPStateEstablished + return true + } + if tcp.SYN && !tcp.ACK && dir == t.dir { + // re-transmission + return true + } + /* established */ + case TCPStateEstablished: + if tcp.RST { + t.state = TCPStateReset + return true + } + + if tcp.FIN { + t.state = TCPStateCloseWait + t.dir = dir + return true + } + // accept any packet + return true + /* closing connection */ + case TCPStateCloseWait: + if tcp.RST { + t.state = TCPStateReset + return true + } + + if tcp.FIN && tcp.ACK && dir == t.dir.Reverse() { + t.state = TCPStateLastAck + return true + } + if tcp.ACK { + return true + } + case TCPStateLastAck: + if tcp.RST { + t.state = TCPStateReset + return true + } + + if tcp.ACK && t.dir == dir { + t.state = TCPStateClosed + return true + } + } + return false +} diff --git a/vendor/github.com/google/gopacket/reassembly/tcpcheck_test.go b/vendor/github.com/google/gopacket/reassembly/tcpcheck_test.go new file mode 100644 index 00000000..4c2391db --- /dev/null +++ b/vendor/github.com/google/gopacket/reassembly/tcpcheck_test.go @@ -0,0 +1,249 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package reassembly + +import ( + "testing" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +// netFlow declared in tcpassembly_test + +/* + * FSM tests + */ + +type testCheckFSMSequence struct { + tcp layers.TCP + ci gopacket.CaptureInfo + expected bool +} + +func testCheckFSM(t *testing.T, options TCPSimpleFSMOptions, s []testCheckFSMSequence) { + fsm := NewTCPSimpleFSM(options) + port := layers.TCPPort(0) + for i, test := range s { + // Fake some values according to ports + flow := netFlow + dir := TCPDirClientToServer + if port == 0 { + port = test.tcp.SrcPort + } + if port != test.tcp.SrcPort { + dir = dir.Reverse() + flow = flow.Reverse() + } + res := fsm.CheckState(&test.tcp, dir) + if res != test.expected { + t.Fatalf("#%d: packet rejected (%s): got %s, expected %s. State:%s", i, gopacket.LayerDump(&test.tcp), res, test.expected, fsm.String()) + } + } +} + +func TestCheckFSM(t *testing.T) { + testCheckFSM(t, TCPSimpleFSMOptions{}, []testCheckFSMSequence{ + { + tcp: layers.TCP{ + SYN: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511116, + Ack: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 566690000), + }, + expected: true, + }, + { + tcp: layers.TCP{ + SYN: true, + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787765, + Ack: 374511117, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590332000), + }, + expected: true, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590346000), + }, + expected: true, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 31, 104, 196, 0, 32, 0, 1, 0, 0, 0, 0, 0, 1, 2, 85, 83, 0, 0, 6, 0, 1, 0, 0, 41, 16, 0, 0, 0, 128, 0, 0, 0}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + }, + expected: true, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787766, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 613687000), + }, + expected: true, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 3465787766, + Ack: 374511150, + BaseLayer: layers.BaseLayer{Payload: []byte{8, 133, 104, 196, 132, 0, 0, 1, 0, 2, 0, 7, 0, 19, 2, 85, 83, 0, 0, 6, 0, 1, 2, 117, 115, 0, 0, 6, 0, 1, 0, 0, 3, 132, 0, 54, 1, 97, 5, 99, 99, 116, 108, 100, 192, 20, 10, 104, 111, 115, 116, 109, 97, 115, 116, 101, 114, 7, 110, 101, 117, 115, 116, 97, 114, 3, 98, 105, 122, 0, 120, 18, 40, 205, 0, 0, 3, 132, 0, 0, 3, 132, 0, 9, 58, 128, 0, 1, 81, 128, 192, 20, 0, 46, 0, 1, 0, 0, 3, 132, 0, 150, 0, 6, 5, 1, 0, 0, 3, 132, 85, 138, 90, 146, 85, 98, 191, 130, 27, 78, 2, 117, 115, 0, 69, 13, 35, 189, 141, 225, 107, 238, 108, 182, 207, 44, 105, 31, 212, 103, 32, 93, 217, 108, 20, 231, 188, 28, 241, 237, 104, 182, 117, 121, 195, 112, 64, 96, 237, 248, 6, 181, 186, 96, 60, 6, 18, 29, 188, 96, 201, 140, 251, 61, 71, 177, 108, 156, 9, 83, 125, 172, 188, 75, 81, 67, 218, 55, 93, 131, 243, 15, 190, 75, 4, 165, 226, 124, 49, 67, 142, 131, 239, 240, 76, 225, 10, 242, 68, 88, 240, 200, 27, 97, 102, 73, 92, 73, 133, 170, 175, 198, 99, 109, 90, 16, 162, 101, 95, 96, 102, 250, 91, 74, 80, 3, 87, 167, 50, 230, 9, 213, 7, 222, 197, 87, 183, 190, 148, 247, 207, 204, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 10, 1, 102, 5, 99, 99, 116, 108, 100, 192, 12, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 97, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 98, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 99, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 101, 193, 8, 192, 118, 0, 2, 0, 1, 0, 7, 233, 0, 0, 4, 1, 107, 193, 8, 192, 118, 0, 46, 0, 1, 0, 7, 233, 0, 0, 150, 0, 2, 5, 1, 0, 7, 233, 0, 85, 127, 33, 92, 85, 87, 134, 98, 27, 78, 2, 117, 115, 0, 19, 227, 175, 75, 88, 245, 164, 158, 150, 198, 57, 253, 150, 179, 161, 52, 24, 56, 229, 176, 175, 40, 45, 232, 188, 171, 131, 197, 107, 125, 218, 192, 78, 221, 146, 33, 114, 55, 43, 12, 131, 213, 51, 98, 37, 2, 102, 161, 232, 115, 177, 210, 51, 169, 215, 133, 56, 190, 91, 75, 8, 222, 231, 202, 139, 28, 187, 249, 72, 21, 23, 56, 63, 72, 126, 142, 242, 195, 242, 64, 208, 134, 100, 157, 197, 159, 43, 148, 20, 70, 117, 152, 159, 35, 200, 220, 49, 234, 173, 210, 91, 34, 210, 192, 7, 197, 112, 117, 208, 234, 42, 49, 133, 237, 197, 14, 244, 149, 191, 142, 36, 252, 42, 48, 182, 189, 9, 68, 1, 65, 5, 67, 67, 84, 76, 68, 193, 126, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 124, 70, 1, 66, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 125, 70, 194, 26, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 3, 209, 174, 255, 255, 255, 255, 255, 255, 255, 255, 255, 126, 1, 67, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 127, 70, 1, 69, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 126, 70, 1, 70, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 209, 173, 58, 70, 194, 108, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 0, 54, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 1, 75, 194, 4, 0, 1, 0, 1, 0, 0, 28, 32, 0, 4, 156, 154, 128, 70, 194, 154, 0, 28, 0, 1, 0, 0, 28, 32, 0, 16, 32, 1, 5, 3, 226, 57, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 194, 2, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 230, 49, 85, 73, 83, 2, 27, 78, 2, 117, 115, 0, 82, 36, 11, 141, 74, 85, 70, 98, 179, 63, 173, 83, 8, 70, 155, 41, 102, 166, 140, 62, 71, 178, 130, 38, 171, 200, 180, 68, 2, 215, 45, 6, 43, 59, 171, 146, 223, 215, 9, 77, 5, 104, 167, 42, 237, 170, 30, 114, 205, 129, 59, 225, 152, 224, 79, 1, 65, 68, 208, 153, 121, 237, 199, 87, 2, 251, 100, 105, 59, 24, 73, 226, 169, 121, 250, 91, 41, 124, 14, 23, 135, 52, 2, 86, 72, 224, 100, 135, 70, 216, 16, 107, 84, 59, 13, 168, 58, 187, 54, 98, 230, 167, 246, 42, 46, 156, 206, 238, 120, 199, 25, 144, 98, 249, 70, 162, 34, 43, 145, 114, 186, 233, 47, 42, 75, 95, 152, 235, 194, 26, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 160, 95, 100, 37, 167, 82, 93, 165, 126, 247, 147, 173, 238, 154, 206, 174, 96, 175, 209, 7, 8, 169, 171, 223, 29, 201, 161, 177, 98, 54, 94, 62, 70, 127, 142, 109, 206, 42, 179, 109, 156, 160, 156, 20, 59, 24, 147, 164, 13, 121, 192, 84, 157, 26, 56, 177, 151, 210, 7, 197, 229, 110, 60, 58, 224, 42, 77, 5, 59, 80, 216, 221, 248, 19, 66, 102, 74, 199, 238, 120, 231, 201, 187, 29, 11, 46, 195, 164, 8, 221, 128, 25, 205, 42, 247, 152, 112, 176, 14, 117, 150, 223, 245, 32, 212, 107, 4, 245, 27, 126, 224, 216, 0, 89, 106, 238, 185, 206, 44, 56, 204, 175, 7, 139, 233, 228, 127, 175, 194, 26, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 28, 5, 3, 0, 0, 28, 32, 85, 108, 217, 174, 85, 69, 70, 242, 27, 78, 2, 117, 115, 0, 172, 117, 89, 89, 73, 249, 245, 211, 100, 127, 48, 135, 224, 97, 172, 146, 128, 30, 190, 72, 199, 170, 97, 179, 136, 109, 86, 110, 235, 214, 47, 50, 115, 11, 226, 168, 56, 198, 24, 212, 205, 207, 2, 116, 104, 112, 99, 234, 236, 44, 70, 19, 19, 215, 127, 200, 162, 215, 142, 45, 135, 91, 219, 217, 86, 231, 154, 87, 222, 161, 32, 66, 196, 55, 117, 20, 186, 9, 134, 252, 249, 219, 9, 196, 128, 8, 222, 201, 131, 210, 182, 232, 142, 72, 160, 171, 95, 231, 232, 156, 28, 34, 54, 94, 73, 183, 38, 160, 123, 175, 157, 21, 163, 8, 214, 155, 172, 237, 169, 28, 15, 138, 105, 107, 251, 109, 131, 240, 194, 72, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 112, 190, 140, 85, 73, 36, 78, 27, 78, 2, 117, 115, 0, 77, 207, 197, 130, 236, 138, 192, 241, 225, 114, 8, 22, 76, 54, 43, 121, 42, 44, 9, 92, 56, 253, 224, 179, 191, 131, 40, 176, 94, 61, 33, 12, 43, 82, 156, 236, 211, 29, 187, 100, 220, 243, 24, 134, 42, 204, 46, 161, 214, 91, 68, 119, 40, 252, 53, 54, 146, 136, 196, 168, 204, 195, 131, 110, 6, 73, 16, 161, 86, 35, 150, 153, 162, 185, 227, 65, 228, 160, 203, 42, 250, 121, 14, 42, 115, 221, 232, 96, 99, 164, 230, 29, 195, 149, 85, 206, 41, 1, 252, 77, 188, 88, 8, 182, 37, 249, 6, 158, 6, 244, 158, 254, 141, 203, 6, 158, 198, 103, 130, 98, 123, 34, 245, 44, 126, 77, 24, 187, 194, 90, 0, 46, 0, 1, 0, 0, 28, 32, 0, 150, 0, 1, 5, 3, 0, 0, 28, 32, 85, 108, 194, 203, 85, 69, 51, 125, 27, 78, 2, 117, 115, 0, 86, 26, 187, 56, 252, 194, 199, 140, 229, 133, 186, 187, 20, 174, 26, 48, 212, 129, 10, 20, 167, 179, 53, 72, 176, 92, 153, 48, 146, 15, 163, 182, 80, 138, 181, 135, 98, 129, 17, 66, 55, 184, 76, 225, 72, 104, 7, 221, 40, 71, 41, 202, 246, 154, 166, 199, 74, 175, 146, 54, 25, 56, 115, 243}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 621198000), + }, + expected: true, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511150, + Ack: 3465789226, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 621220000), + }, + expected: true, + }, + }) +} + +func TestCheckFSMmissingSYNACK(t *testing.T) { + testCheckFSM(t, TCPSimpleFSMOptions{}, []testCheckFSMSequence{ + { + tcp: layers.TCP{ + SYN: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511116, + Ack: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 566690000), + }, + expected: true, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590346000), + }, + expected: false, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 374511117, + Ack: 3465787766, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 31, 104, 196, 0, 32, 0, 1, 0, 0, 0, 0, 0, 1, 2, 85, 83, 0, 0, 6, 0, 1, 0, 0, 41, 16, 0, 0, 0, 128, 0, 0, 0}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + }, + expected: false, + }, + }) +} + +// Support packets w/o SYN (+SYN+ACK) depending on option +func TestCheckFSMmissingSYN(t *testing.T) { + for _, val := range []bool{false, true} { + testCheckFSM(t, TCPSimpleFSMOptions{SupportMissingEstablishment: val}, []testCheckFSMSequence{ + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 54842, + DstPort: 53, + Seq: 12, + Ack: 1012, + BaseLayer: layers.BaseLayer{Payload: []byte{1}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 566690000), + }, + expected: val, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 1012, + Ack: 13, + BaseLayer: layers.BaseLayer{Payload: []byte{2}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590346000), + }, + expected: val, + }, + { + tcp: layers.TCP{ + ACK: true, + SrcPort: 53, + DstPort: 54842, + Seq: 1013, + Ack: 13, + BaseLayer: layers.BaseLayer{Payload: []byte{3}}, + }, + ci: gopacket.CaptureInfo{ + Timestamp: time.Unix(1432538521, 590387000), + }, + expected: val, + }, + }) + } +} diff --git a/vendor/github.com/google/gopacket/routing/common.go b/vendor/github.com/google/gopacket/routing/common.go new file mode 100644 index 00000000..a6746d49 --- /dev/null +++ b/vendor/github.com/google/gopacket/routing/common.go @@ -0,0 +1,36 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package routing + +import ( + "net" +) + +// Router implements simple IPv4/IPv6 routing based on the kernel's routing +// table. This routing library has very few features and may actually route +// incorrectly in some cases, but it should work the majority of the time. +type Router interface { + // Route returns where to route a packet based on the packet's source + // and destination IP address. + // + // Callers may pass in nil for src, in which case the src is treated as + // either 0.0.0.0 or ::, depending on whether dst is a v4 or v6 address. + // + // It returns the interface on which to send the packet, the gateway IP + // to send the packet to (if necessary), the preferred src IP to use (if + // available). If the preferred src address is not given in the routing + // table, the first IP address of the interface is provided. + // + // If an error is encountered, iface, geteway, and + // preferredSrc will be nil, and err will be set. + Route(dst net.IP) (iface *net.Interface, gateway, preferredSrc net.IP, err error) + + // RouteWithSrc routes based on source information as well as destination + // information. Either or both of input/src can be nil. If both are, this + // should behave exactly like Route(dst) + RouteWithSrc(input net.HardwareAddr, src, dst net.IP) (iface *net.Interface, gateway, preferredSrc net.IP, err error) +} diff --git a/vendor/github.com/google/gopacket/routing/other.go b/vendor/github.com/google/gopacket/routing/other.go new file mode 100644 index 00000000..b53fea94 --- /dev/null +++ b/vendor/github.com/google/gopacket/routing/other.go @@ -0,0 +1,15 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build !linux + +// Package routing is currently only supported in Linux, but the build system requires a valid go file for all architectures. + +package routing + +func New() (Router, error) { + panic("router only implemented in linux") +} diff --git a/vendor/github.com/google/gopacket/routing/routing.go b/vendor/github.com/google/gopacket/routing/routing.go new file mode 100644 index 00000000..a7a8cfde --- /dev/null +++ b/vendor/github.com/google/gopacket/routing/routing.go @@ -0,0 +1,242 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// +build linux + +// Package routing provides a very basic but mostly functional implementation of +// a routing table for IPv4/IPv6 addresses. It uses a routing table pulled from +// the kernel via netlink to find the correct interface, gateway, and preferred +// source IP address for packets destined to a particular location. +// +// The routing package is meant to be used with applications that are sending +// raw packet data, which don't have the benefit of having the kernel route +// packets for them. +package routing + +import ( + "bytes" + "errors" + "fmt" + "net" + "sort" + "strings" + "syscall" + "unsafe" +) + +// Pulled from http://man7.org/linux/man-pages/man7/rtnetlink.7.html +// See the section on RTM_NEWROUTE, specifically 'struct rtmsg'. +type routeInfoInMemory struct { + Family byte + DstLen byte + SrcLen byte + TOS byte + + Table byte + Protocol byte + Scope byte + Type byte + + Flags uint32 +} + +// rtInfo contains information on a single route. +type rtInfo struct { + Src, Dst *net.IPNet + Gateway, PrefSrc net.IP + // We currently ignore the InputIface. + InputIface, OutputIface uint32 + Priority uint32 +} + +// routeSlice implements sort.Interface to sort routes by Priority. +type routeSlice []*rtInfo + +func (r routeSlice) Len() int { + return len(r) +} +func (r routeSlice) Less(i, j int) bool { + return r[i].Priority < r[j].Priority +} +func (r routeSlice) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +type router struct { + ifaces []net.Interface + addrs []ipAddrs + v4, v6 routeSlice +} + +func (r *router) String() string { + strs := []string{"ROUTER", "--- V4 ---"} + for _, route := range r.v4 { + strs = append(strs, fmt.Sprintf("%+v", *route)) + } + strs = append(strs, "--- V6 ---") + for _, route := range r.v6 { + strs = append(strs, fmt.Sprintf("%+v", *route)) + } + return strings.Join(strs, "\n") +} + +type ipAddrs struct { + v4, v6 net.IP +} + +func (r *router) Route(dst net.IP) (iface *net.Interface, gateway, preferredSrc net.IP, err error) { + return r.RouteWithSrc(nil, nil, dst) +} + +func (r *router) RouteWithSrc(input net.HardwareAddr, src, dst net.IP) (iface *net.Interface, gateway, preferredSrc net.IP, err error) { + length := len(dst) + var ifaceIndex int + switch length { + case 4: + ifaceIndex, gateway, preferredSrc, err = r.route(r.v4, input, src, dst) + case 16: + ifaceIndex, gateway, preferredSrc, err = r.route(r.v6, input, src, dst) + default: + err = errors.New("IP length is not 4 or 16") + return + } + + // Interfaces are 1-indexed, but we store them in a 0-indexed array. + ifaceIndex-- + + iface = &r.ifaces[ifaceIndex] + if preferredSrc == nil { + switch length { + case 4: + preferredSrc = r.addrs[ifaceIndex].v4 + case 16: + preferredSrc = r.addrs[ifaceIndex].v6 + } + } + return +} + +func (r *router) route(routes routeSlice, input net.HardwareAddr, src, dst net.IP) (iface int, gateway, preferredSrc net.IP, err error) { + var inputIndex uint32 + if input != nil { + for i, iface := range r.ifaces { + if bytes.Equal(input, iface.HardwareAddr) { + // Convert from zero- to one-indexed. + inputIndex = uint32(i + 1) + break + } + } + } + for _, rt := range routes { + if rt.InputIface != 0 && rt.InputIface != inputIndex { + continue + } + if rt.Src != nil && !rt.Src.Contains(src) { + continue + } + if rt.Dst != nil && !rt.Dst.Contains(dst) { + continue + } + return int(rt.OutputIface), rt.Gateway, rt.PrefSrc, nil + } + err = fmt.Errorf("no route found for %v", dst) + return +} + +// New creates a new router object. The router returned by New currently does +// not update its routes after construction... care should be taken for +// long-running programs to call New() regularly to take into account any +// changes to the routing table which have occurred since the last New() call. +func New() (Router, error) { + rtr := &router{} + tab, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC) + if err != nil { + return nil, err + } + msgs, err := syscall.ParseNetlinkMessage(tab) + if err != nil { + return nil, err + } +loop: + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + break loop + case syscall.RTM_NEWROUTE: + rt := (*routeInfoInMemory)(unsafe.Pointer(&m.Data[0])) + routeInfo := rtInfo{} + attrs, err := syscall.ParseNetlinkRouteAttr(&m) + if err != nil { + return nil, err + } + switch rt.Family { + case syscall.AF_INET: + rtr.v4 = append(rtr.v4, &routeInfo) + case syscall.AF_INET6: + rtr.v6 = append(rtr.v6, &routeInfo) + default: + continue loop + } + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_DST: + routeInfo.Dst = &net.IPNet{ + IP: net.IP(attr.Value), + Mask: net.CIDRMask(int(rt.DstLen), len(attr.Value)*8), + } + case syscall.RTA_SRC: + routeInfo.Src = &net.IPNet{ + IP: net.IP(attr.Value), + Mask: net.CIDRMask(int(rt.SrcLen), len(attr.Value)*8), + } + case syscall.RTA_GATEWAY: + routeInfo.Gateway = net.IP(attr.Value) + case syscall.RTA_PREFSRC: + routeInfo.PrefSrc = net.IP(attr.Value) + case syscall.RTA_IIF: + routeInfo.InputIface = *(*uint32)(unsafe.Pointer(&attr.Value[0])) + case syscall.RTA_OIF: + routeInfo.OutputIface = *(*uint32)(unsafe.Pointer(&attr.Value[0])) + case syscall.RTA_PRIORITY: + routeInfo.Priority = *(*uint32)(unsafe.Pointer(&attr.Value[0])) + } + } + } + } + sort.Sort(rtr.v4) + sort.Sort(rtr.v6) + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + for i, iface := range ifaces { + if i != iface.Index-1 { + return nil, fmt.Errorf("out of order iface %d = %v", i, iface) + } + rtr.ifaces = append(rtr.ifaces, iface) + var addrs ipAddrs + ifaceAddrs, err := iface.Addrs() + if err != nil { + return nil, err + } + for _, addr := range ifaceAddrs { + if inet, ok := addr.(*net.IPNet); ok { + // Go has a nasty habit of giving you IPv4s as ::ffff:1.2.3.4 instead of 1.2.3.4. + // We want to use mapped v4 addresses as v4 preferred addresses, never as v6 + // preferred addresses. + if v4 := inet.IP.To4(); v4 != nil { + if addrs.v4 == nil { + addrs.v4 = v4 + } + } else if addrs.v6 == nil { + addrs.v6 = inet.IP + } + } + } + rtr.addrs = append(rtr.addrs, addrs) + } + return rtr, nil +} diff --git a/vendor/github.com/google/gopacket/tcpassembly/assembly.go b/vendor/github.com/google/gopacket/tcpassembly/assembly.go new file mode 100644 index 00000000..50f64874 --- /dev/null +++ b/vendor/github.com/google/gopacket/tcpassembly/assembly.go @@ -0,0 +1,788 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package tcpassembly provides TCP stream re-assembly. +// +// The tcpassembly package implements uni-directional TCP reassembly, for use in +// packet-sniffing applications. The caller reads packets off the wire, then +// presents them to an Assembler in the form of gopacket layers.TCP packets +// (github.com/google/gopacket, github.com/google/gopacket/layers). +// +// The Assembler uses a user-supplied +// StreamFactory to create a user-defined Stream interface, then passes packet +// data in stream order to that object. A concurrency-safe StreamPool keeps +// track of all current Streams being reassembled, so multiple Assemblers may +// run at once to assemble packets while taking advantage of multiple cores. +package tcpassembly + +import ( + "flag" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "log" + "sync" + "time" +) + +var memLog = flag.Bool("assembly_memuse_log", false, "If true, the github.com/google/gopacket/tcpassembly library will log information regarding its memory use every once in a while.") +var debugLog = flag.Bool("assembly_debug_log", false, "If true, the github.com/google/gopacket/tcpassembly library will log verbose debugging information (at least one line per packet)") + +const invalidSequence = -1 +const uint32Max = 0xFFFFFFFF + +// Sequence is a TCP sequence number. It provides a few convenience functions +// for handling TCP wrap-around. The sequence should always be in the range +// [0,0xFFFFFFFF]... its other bits are simply used in wrap-around calculations +// and should never be set. +type Sequence int64 + +// Difference defines an ordering for comparing TCP sequences that's safe for +// roll-overs. It returns: +// > 0 : if t comes after s +// < 0 : if t comes before s +// 0 : if t == s +// The number returned is the sequence difference, so 4.Difference(8) will +// return 4. +// +// It handles rollovers by considering any sequence in the first quarter of the +// uint32 space to be after any sequence in the last quarter of that space, thus +// wrapping the uint32 space. +func (s Sequence) Difference(t Sequence) int { + if s > uint32Max-uint32Max/4 && t < uint32Max/4 { + t += uint32Max + } else if t > uint32Max-uint32Max/4 && s < uint32Max/4 { + s += uint32Max + } + return int(t - s) +} + +// Add adds an integer to a sequence and returns the resulting sequence. +func (s Sequence) Add(t int) Sequence { + return (s + Sequence(t)) & uint32Max +} + +// Reassembly objects are passed by an Assembler into Streams using the +// Reassembled call. Callers should not need to create these structs themselves +// except for testing. +type Reassembly struct { + // Bytes is the next set of bytes in the stream. May be empty. + Bytes []byte + // Skip is set to non-zero if bytes were skipped between this and the + // last Reassembly. If this is the first packet in a connection and we + // didn't see the start, we have no idea how many bytes we skipped, so + // we set it to -1. Otherwise, it's set to the number of bytes skipped. + Skip int + // Start is set if this set of bytes has a TCP SYN accompanying it. + Start bool + // End is set if this set of bytes has a TCP FIN or RST accompanying it. + End bool + // Seen is the timestamp this set of bytes was pulled off the wire. + Seen time.Time +} + +const pageBytes = 1900 + +// page is used to store TCP data we're not ready for yet (out-of-order +// packets). Unused pages are stored in and returned from a pageCache, which +// avoids memory allocation. Used pages are stored in a doubly-linked list in +// a connection. +type page struct { + Reassembly + seq Sequence + index int + prev, next *page + buf [pageBytes]byte +} + +// pageCache is a concurrency-unsafe store of page objects we use to avoid +// memory allocation as much as we can. It grows but never shrinks. +type pageCache struct { + free []*page + pcSize int + size, used int + pages [][]page + pageRequests int64 +} + +const initialAllocSize = 1024 + +func newPageCache() *pageCache { + pc := &pageCache{ + free: make([]*page, 0, initialAllocSize), + pcSize: initialAllocSize, + } + pc.grow() + return pc +} + +// grow exponentially increases the size of our page cache as much as necessary. +func (c *pageCache) grow() { + pages := make([]page, c.pcSize) + c.pages = append(c.pages, pages) + c.size += c.pcSize + for i := range pages { + c.free = append(c.free, &pages[i]) + } + if *memLog { + log.Println("PageCache: created", c.pcSize, "new pages") + } + c.pcSize *= 2 +} + +// next returns a clean, ready-to-use page object. +func (c *pageCache) next(ts time.Time) (p *page) { + if *memLog { + c.pageRequests++ + if c.pageRequests&0xFFFF == 0 { + log.Println("PageCache:", c.pageRequests, "requested,", c.used, "used,", len(c.free), "free") + } + } + if len(c.free) == 0 { + c.grow() + } + i := len(c.free) - 1 + p, c.free = c.free[i], c.free[:i] + p.prev = nil + p.next = nil + p.Seen = ts + p.Bytes = p.buf[:0] + c.used++ + return p +} + +// replace replaces a page into the pageCache. +func (c *pageCache) replace(p *page) { + c.used-- + c.free = append(c.free, p) +} + +// Stream is implemented by the caller to handle incoming reassembled +// TCP data. Callers create a StreamFactory, then StreamPool uses +// it to create a new Stream for every TCP stream. +// +// assembly will, in order: +// 1) Create the stream via StreamFactory.New +// 2) Call Reassembled 0 or more times, passing in reassembled TCP data in order +// 3) Call ReassemblyComplete one time, after which the stream is dereferenced by assembly. +type Stream interface { + // Reassembled is called zero or more times. assembly guarantees + // that the set of all Reassembly objects passed in during all + // calls are presented in the order they appear in the TCP stream. + // Reassembly objects are reused after each Reassembled call, + // so it's important to copy anything you need out of them + // (specifically out of Reassembly.Bytes) that you need to stay + // around after you return from the Reassembled call. + Reassembled([]Reassembly) + // ReassemblyComplete is called when assembly decides there is + // no more data for this Stream, either because a FIN or RST packet + // was seen, or because the stream has timed out without any new + // packet data (due to a call to FlushOlderThan). + ReassemblyComplete() +} + +// StreamFactory is used by assembly to create a new stream for each +// new TCP session. +type StreamFactory interface { + // New should return a new stream for the given TCP key. + New(netFlow, tcpFlow gopacket.Flow) Stream +} + +func (p *StreamPool) connections() []*connection { + p.mu.RLock() + conns := make([]*connection, 0, len(p.conns)) + for _, conn := range p.conns { + conns = append(conns, conn) + } + p.mu.RUnlock() + return conns +} + +// FlushOptions provide options for flushing connections. +type FlushOptions struct { + T time.Time // If nonzero, only connections with data older than T are flushed + CloseAll bool // If true, ALL connections are closed post flush, not just those that correctly see FIN/RST. +} + +// FlushWithOptions finds any streams waiting for packets older than +// the given time, and pushes through the data they have (IE: tells +// them to stop waiting and skip the data they're waiting for). +// +// Each Stream maintains a list of zero or more sets of bytes it has received +// out-of-order. For example, if it has processed up through sequence number +// 10, it might have bytes [15-20), [20-25), [30,50) in its list. Each set of +// bytes also has the timestamp it was originally viewed. A flush call will +// look at the smallest subsequent set of bytes, in this case [15-20), and if +// its timestamp is older than the passed-in time, it will push it and all +// contiguous byte-sets out to the Stream's Reassembled function. In this case, +// it will push [15-20), but also [20-25), since that's contiguous. It will +// only push [30-50) if its timestamp is also older than the passed-in time, +// otherwise it will wait until the next FlushOlderThan to see if bytes [25-30) +// come in. +// +// If it pushes all bytes (or there were no sets of bytes to begin with) +// AND the connection has not received any bytes since the passed-in time, +// the connection will be closed. +// +// If CloseAll is set, it will close out connections that have been drained. +// Regardless of the CloseAll setting, connections stale for the specified +// time will be closed. +// +// Returns the number of connections flushed, and of those, the number closed +// because of the flush. +func (a *Assembler) FlushWithOptions(opt FlushOptions) (flushed, closed int) { + conns := a.connPool.connections() + closes := 0 + flushes := 0 + for _, conn := range conns { + flushed := false + conn.mu.Lock() + if conn.closed { + // Already closed connection, nothing to do here. + conn.mu.Unlock() + continue + } + for conn.first != nil && conn.first.Seen.Before(opt.T) { + a.skipFlush(conn) + flushed = true + if conn.closed { + closes++ + break + } + } + if opt.CloseAll && !conn.closed && conn.first == nil && conn.lastSeen.Before(opt.T) { + flushed = true + a.closeConnection(conn) + closes++ + } + if flushed { + flushes++ + } + conn.mu.Unlock() + } + return flushes, closes +} + +// FlushOlderThan calls FlushWithOptions with the CloseAll option set to true. +func (a *Assembler) FlushOlderThan(t time.Time) (flushed, closed int) { + return a.FlushWithOptions(FlushOptions{CloseAll: true, T: t}) +} + +// FlushAll flushes all remaining data into all remaining connections, closing +// those connections. It returns the total number of connections flushed/closed +// by the call. +func (a *Assembler) FlushAll() (closed int) { + conns := a.connPool.connections() + closed = len(conns) + for _, conn := range conns { + conn.mu.Lock() + for !conn.closed { + a.skipFlush(conn) + } + conn.mu.Unlock() + } + return +} + +type key [2]gopacket.Flow + +func (k *key) String() string { + return fmt.Sprintf("%s:%s", k[0], k[1]) +} + +// StreamPool stores all streams created by Assemblers, allowing multiple +// assemblers to work together on stream processing while enforcing the fact +// that a single stream receives its data serially. It is safe +// for concurrency, usable by multiple Assemblers at once. +// +// StreamPool handles the creation and storage of Stream objects used by one or +// more Assembler objects. When a new TCP stream is found by an Assembler, it +// creates an associated Stream by calling its StreamFactory's New method. +// Thereafter (until the stream is closed), that Stream object will receive +// assembled TCP data via Assembler's calls to the stream's Reassembled +// function. +// +// Like the Assembler, StreamPool attempts to minimize allocation. Unlike the +// Assembler, though, it does have to do some locking to make sure that the +// connection objects it stores are accessible to multiple Assemblers. +type StreamPool struct { + conns map[key]*connection + users int + mu sync.RWMutex + factory StreamFactory + free []*connection + all [][]connection + nextAlloc int + newConnectionCount int64 +} + +func (p *StreamPool) grow() { + conns := make([]connection, p.nextAlloc) + p.all = append(p.all, conns) + for i := range conns { + p.free = append(p.free, &conns[i]) + } + if *memLog { + log.Println("StreamPool: created", p.nextAlloc, "new connections") + } + p.nextAlloc *= 2 +} + +// NewStreamPool creates a new connection pool. Streams will +// be created as necessary using the passed-in StreamFactory. +func NewStreamPool(factory StreamFactory) *StreamPool { + return &StreamPool{ + conns: make(map[key]*connection, initialAllocSize), + free: make([]*connection, 0, initialAllocSize), + factory: factory, + nextAlloc: initialAllocSize, + } +} + +const assemblerReturnValueInitialSize = 16 + +// NewAssembler creates a new assembler. Pass in the StreamPool +// to use, may be shared across assemblers. +// +// This sets some sane defaults for the assembler options, +// see DefaultAssemblerOptions for details. +func NewAssembler(pool *StreamPool) *Assembler { + pool.mu.Lock() + pool.users++ + pool.mu.Unlock() + return &Assembler{ + ret: make([]Reassembly, assemblerReturnValueInitialSize), + pc: newPageCache(), + connPool: pool, + AssemblerOptions: DefaultAssemblerOptions, + } +} + +// DefaultAssemblerOptions provides default options for an assembler. +// These options are used by default when calling NewAssembler, so if +// modified before a NewAssembler call they'll affect the resulting Assembler. +// +// Note that the default options can result in ever-increasing memory usage +// unless one of the Flush* methods is called on a regular basis. +var DefaultAssemblerOptions = AssemblerOptions{ + MaxBufferedPagesPerConnection: 0, // unlimited + MaxBufferedPagesTotal: 0, // unlimited +} + +type connection struct { + key key + pages int + first, last *page + nextSeq Sequence + created, lastSeen time.Time + stream Stream + closed bool + mu sync.Mutex +} + +func (c *connection) reset(k key, s Stream, ts time.Time) { + c.key = k + c.pages = 0 + c.first, c.last = nil, nil + c.nextSeq = invalidSequence + c.created = ts + c.stream = s + c.closed = false +} + +// AssemblerOptions controls the behavior of each assembler. Modify the +// options of each assembler you create to change their behavior. +type AssemblerOptions struct { + // MaxBufferedPagesTotal is an upper limit on the total number of pages to + // buffer while waiting for out-of-order packets. Once this limit is + // reached, the assembler will degrade to flushing every connection it + // gets a packet for. If <= 0, this is ignored. + MaxBufferedPagesTotal int + // MaxBufferedPagesPerConnection is an upper limit on the number of pages + // buffered for a single connection. Should this limit be reached for a + // particular connection, the smallest sequence number will be flushed, along + // with any contiguous data. If <= 0, this is ignored. + MaxBufferedPagesPerConnection int +} + +// Assembler handles reassembling TCP streams. It is not safe for +// concurrency... after passing a packet in via the Assemble call, the caller +// must wait for that call to return before calling Assemble again. Callers can +// get around this by creating multiple assemblers that share a StreamPool. In +// that case, each individual stream will still be handled serially (each stream +// has an individual mutex associated with it), however multiple assemblers can +// assemble different connections concurrently. +// +// The Assembler provides (hopefully) fast TCP stream re-assembly for sniffing +// applications written in Go. The Assembler uses the following methods to be +// as fast as possible, to keep packet processing speedy: +// +// Avoids Lock Contention +// +// Assemblers locks connections, but each connection has an individual lock, and +// rarely will two Assemblers be looking at the same connection. Assemblers +// lock the StreamPool when looking up connections, but they use Reader +// locks initially, and only force a write lock if they need to create a new +// connection or close one down. These happen much less frequently than +// individual packet handling. +// +// Each assembler runs in its own goroutine, and the only state shared between +// goroutines is through the StreamPool. Thus all internal Assembler state +// can be handled without any locking. +// +// NOTE: If you can guarantee that packets going to a set of Assemblers will +// contain information on different connections per Assembler (for example, +// they're already hashed by PF_RING hashing or some other hashing mechanism), +// then we recommend you use a seperate StreamPool per Assembler, thus +// avoiding all lock contention. Only when different Assemblers could receive +// packets for the same Stream should a StreamPool be shared between them. +// +// Avoids Memory Copying +// +// In the common case, handling of a single TCP packet should result in zero +// memory allocations. The Assembler will look up the connection, figure out +// that the packet has arrived in order, and immediately pass that packet on to +// the appropriate connection's handling code. Only if a packet arrives out of +// order is its contents copied and stored in memory for later. +// +// Avoids Memory Allocation +// +// Assemblers try very hard to not use memory allocation unless absolutely +// necessary. Packet data for sequential packets is passed directly to streams +// with no copying or allocation. Packet data for out-of-order packets is +// copied into reusable pages, and new pages are only allocated rarely when the +// page cache runs out. Page caches are Assembler-specific, thus not used +// concurrently and requiring no locking. +// +// Internal representations for connection objects are also reused over time. +// Because of this, the most common memory allocation done by the Assembler is +// generally what's done by the caller in StreamFactory.New. If no allocation +// is done there, then very little allocation is done ever, mostly to handle +// large increases in bandwidth or numbers of connections. +// +// TODO: The page caches used by an Assembler will grow to the size necessary +// to handle a workload, and currently will never shrink. This means that +// traffic spikes can result in large memory usage which isn't garbage +// collected when typical traffic levels return. +type Assembler struct { + AssemblerOptions + ret []Reassembly + pc *pageCache + connPool *StreamPool +} + +func (p *StreamPool) newConnection(k key, s Stream, ts time.Time) (c *connection) { + if *memLog { + p.newConnectionCount++ + if p.newConnectionCount&0x7FFF == 0 { + log.Println("StreamPool:", p.newConnectionCount, "requests,", len(p.conns), "used,", len(p.free), "free") + } + } + if len(p.free) == 0 { + p.grow() + } + index := len(p.free) - 1 + c, p.free = p.free[index], p.free[:index] + c.reset(k, s, ts) + return c +} + +// getConnection returns a connection. If end is true and a connection +// does not already exist, returns nil. This allows us to check for a +// connection without actually creating one if it doesn't already exist. +func (p *StreamPool) getConnection(k key, end bool, ts time.Time) *connection { + p.mu.RLock() + conn := p.conns[k] + p.mu.RUnlock() + if end || conn != nil { + return conn + } + s := p.factory.New(k[0], k[1]) + p.mu.Lock() + conn = p.newConnection(k, s, ts) + if conn2 := p.conns[k]; conn2 != nil { + p.mu.Unlock() + return conn2 + } + p.conns[k] = conn + p.mu.Unlock() + return conn +} + +// Assemble calls AssembleWithTimestamp with the current timestamp, useful for +// packets being read directly off the wire. +func (a *Assembler) Assemble(netFlow gopacket.Flow, t *layers.TCP) { + a.AssembleWithTimestamp(netFlow, t, time.Now()) +} + +// AssembleWithTimestamp reassembles the given TCP packet into its appropriate +// stream. +// +// The timestamp passed in must be the timestamp the packet was seen. +// For packets read off the wire, time.Now() should be fine. For packets read +// from PCAP files, CaptureInfo.Timestamp should be passed in. This timestamp +// will affect which streams are flushed by a call to FlushOlderThan. +// +// Each Assemble call results in, in order: +// +// zero or one calls to StreamFactory.New, creating a stream +// zero or one calls to Reassembled on a single stream +// zero or one calls to ReassemblyComplete on the same stream +func (a *Assembler) AssembleWithTimestamp(netFlow gopacket.Flow, t *layers.TCP, timestamp time.Time) { + // Ignore empty TCP packets + if !t.SYN && !t.FIN && !t.RST && len(t.LayerPayload()) == 0 { + if *debugLog { + log.Println("ignoring useless packet") + } + return + } + + a.ret = a.ret[:0] + key := key{netFlow, t.TransportFlow()} + var conn *connection + // This for loop handles a race condition where a connection will close, lock + // the connection pool, and remove itself, but before it locked the connection + // pool it's returned to another Assemble statement. This should loop 0-1 + // times for the VAST majority of cases. + for { + conn = a.connPool.getConnection( + key, !t.SYN && len(t.LayerPayload()) == 0, timestamp) + if conn == nil { + if *debugLog { + log.Printf("%v got empty packet on otherwise empty connection", key) + } + return + } + conn.mu.Lock() + if !conn.closed { + break + } + conn.mu.Unlock() + } + if conn.lastSeen.Before(timestamp) { + conn.lastSeen = timestamp + } + seq, bytes := Sequence(t.Seq), t.Payload + if conn.nextSeq == invalidSequence { + if t.SYN { + if *debugLog { + log.Printf("%v saw first SYN packet, returning immediately, seq=%v", key, seq) + } + a.ret = append(a.ret, Reassembly{ + Bytes: bytes, + Skip: 0, + Start: true, + Seen: timestamp, + }) + conn.nextSeq = seq.Add(len(bytes) + 1) + } else { + if *debugLog { + log.Printf("%v waiting for start, storing into connection", key) + } + a.insertIntoConn(t, conn, timestamp) + } + } else if diff := conn.nextSeq.Difference(seq); diff > 0 { + if *debugLog { + log.Printf("%v gap in sequence numbers (%v, %v) diff %v, storing into connection", key, conn.nextSeq, seq, diff) + } + a.insertIntoConn(t, conn, timestamp) + } else { + bytes, conn.nextSeq = byteSpan(conn.nextSeq, seq, bytes) + if *debugLog { + log.Printf("%v found contiguous data (%v, %v), returning immediately", key, seq, conn.nextSeq) + } + a.ret = append(a.ret, Reassembly{ + Bytes: bytes, + Skip: 0, + End: t.RST || t.FIN, + Seen: timestamp, + }) + } + if len(a.ret) > 0 { + a.sendToConnection(conn) + } + conn.mu.Unlock() +} + +func byteSpan(expected, received Sequence, bytes []byte) (toSend []byte, next Sequence) { + if expected == invalidSequence { + return bytes, received.Add(len(bytes)) + } + span := int(received.Difference(expected)) + if span <= 0 { + return bytes, received.Add(len(bytes)) + } else if len(bytes) < span { + return nil, expected + } + return bytes[span:], expected.Add(len(bytes) - span) +} + +// sendToConnection sends the current values in a.ret to the connection, closing +// the connection if the last thing sent had End set. +func (a *Assembler) sendToConnection(conn *connection) { + a.addContiguous(conn) + if conn.stream == nil { + panic("why?") + } + conn.stream.Reassembled(a.ret) + if a.ret[len(a.ret)-1].End { + a.closeConnection(conn) + } +} + +// addContiguous adds contiguous byte-sets to a connection. +func (a *Assembler) addContiguous(conn *connection) { + for conn.first != nil && conn.nextSeq.Difference(conn.first.seq) <= 0 { + a.addNextFromConn(conn) + } +} + +// skipFlush skips the first set of bytes we're waiting for and returns the +// first set of bytes we have. If we have no bytes pending, it closes the +// connection. +func (a *Assembler) skipFlush(conn *connection) { + if *debugLog { + log.Printf("%v skipFlush %v", conn.key, conn.nextSeq) + } + if conn.first == nil { + a.closeConnection(conn) + return + } + a.ret = a.ret[:0] + a.addNextFromConn(conn) + a.addContiguous(conn) + a.sendToConnection(conn) +} + +func (p *StreamPool) remove(conn *connection) { + p.mu.Lock() + delete(p.conns, conn.key) + p.free = append(p.free, conn) + p.mu.Unlock() +} + +func (a *Assembler) closeConnection(conn *connection) { + if *debugLog { + log.Printf("%v closing", conn.key) + } + conn.stream.ReassemblyComplete() + conn.closed = true + a.connPool.remove(conn) + for p := conn.first; p != nil; p = p.next { + a.pc.replace(p) + } +} + +// traverseConn traverses our doubly-linked list of pages for the correct +// position to put the given sequence number. Note that it traverses backwards, +// starting at the highest sequence number and going down, since we assume the +// common case is that TCP packets for a stream will appear in-order, with +// minimal loss or packet reordering. +func (c *connection) traverseConn(seq Sequence) (prev, current *page) { + prev = c.last + for prev != nil && prev.seq.Difference(seq) < 0 { + current = prev + prev = current.prev + } + return +} + +// pushBetween inserts the doubly-linked list first-...-last in between the +// nodes prev-next in another doubly-linked list. If prev is nil, makes first +// the new first page in the connection's list. If next is nil, makes last the +// new last page in the list. first/last may point to the same page. +func (c *connection) pushBetween(prev, next, first, last *page) { + // Maintain our doubly linked list + if next == nil || c.last == nil { + c.last = last + } else { + last.next = next + next.prev = last + } + if prev == nil || c.first == nil { + c.first = first + } else { + first.prev = prev + prev.next = first + } +} + +func (a *Assembler) insertIntoConn(t *layers.TCP, conn *connection, ts time.Time) { + if conn.first != nil && conn.first.seq == conn.nextSeq { + panic("wtf") + } + p, p2, numPages := a.pagesFromTCP(t, ts) + prev, current := conn.traverseConn(Sequence(t.Seq)) + conn.pushBetween(prev, current, p, p2) + conn.pages += numPages + if (a.MaxBufferedPagesPerConnection > 0 && conn.pages >= a.MaxBufferedPagesPerConnection) || + (a.MaxBufferedPagesTotal > 0 && a.pc.used >= a.MaxBufferedPagesTotal) { + if *debugLog { + log.Printf("%v hit max buffer size: %+v, %v, %v", conn.key, a.AssemblerOptions, conn.pages, a.pc.used) + } + a.addNextFromConn(conn) + } +} + +// pagesFromTCP creates a page (or set of pages) from a TCP packet. Note that +// it should NEVER receive a SYN packet, as it doesn't handle sequences +// correctly. +// +// It returns the first and last page in its doubly-linked list of new pages. +func (a *Assembler) pagesFromTCP(t *layers.TCP, ts time.Time) (p, p2 *page, numPages int) { + first := a.pc.next(ts) + current := first + numPages++ + seq, bytes := Sequence(t.Seq), t.Payload + for { + length := min(len(bytes), pageBytes) + current.Bytes = current.buf[:length] + copy(current.Bytes, bytes) + current.seq = seq + bytes = bytes[length:] + if len(bytes) == 0 { + break + } + seq = seq.Add(length) + current.next = a.pc.next(ts) + current.next.prev = current + current = current.next + numPages++ + } + current.End = t.RST || t.FIN + return first, current, numPages +} + +// addNextFromConn pops the first page from a connection off and adds it to the +// return array. +func (a *Assembler) addNextFromConn(conn *connection) { + if conn.nextSeq == invalidSequence { + conn.first.Skip = -1 + } else if diff := conn.nextSeq.Difference(conn.first.seq); diff > 0 { + conn.first.Skip = int(diff) + } + conn.first.Bytes, conn.nextSeq = byteSpan(conn.nextSeq, conn.first.seq, conn.first.Bytes) + if *debugLog { + log.Printf("%v adding from conn (%v, %v)", conn.key, conn.first.seq, conn.nextSeq) + } + a.ret = append(a.ret, conn.first.Reassembly) + a.pc.replace(conn.first) + if conn.first == conn.last { + conn.first = nil + conn.last = nil + } else { + conn.first = conn.first.next + conn.first.prev = nil + } + conn.pages-- +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/vendor/github.com/google/gopacket/tcpassembly/assembly_test.go b/vendor/github.com/google/gopacket/tcpassembly/assembly_test.go new file mode 100644 index 00000000..1bd2842e --- /dev/null +++ b/vendor/github.com/google/gopacket/tcpassembly/assembly_test.go @@ -0,0 +1,562 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package tcpassembly + +import ( + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "net" + "reflect" + "testing" + "time" +) + +var netFlow gopacket.Flow + +func init() { + netFlow, _ = gopacket.FlowFromEndpoints( + layers.NewIPEndpoint(net.IP{1, 2, 3, 4}), + layers.NewIPEndpoint(net.IP{5, 6, 7, 8})) +} + +type testSequence struct { + in layers.TCP + want []Reassembly +} + +type testFactory struct { + reassembly []Reassembly +} + +func (t *testFactory) New(a, b gopacket.Flow) Stream { + return t +} +func (t *testFactory) Reassembled(r []Reassembly) { + t.reassembly = r + for i := 0; i < len(r); i++ { + t.reassembly[i].Seen = time.Time{} + } +} +func (t *testFactory) ReassemblyComplete() { +} + +func test(t *testing.T, s []testSequence) { + fact := &testFactory{} + p := NewStreamPool(fact) + a := NewAssembler(p) + a.MaxBufferedPagesPerConnection = 4 + for i, test := range s { + fact.reassembly = []Reassembly{} + a.Assemble(netFlow, &test.in) + if !reflect.DeepEqual(fact.reassembly, test.want) { + t.Fatalf("test %v:\nwant: %v\n got: %v\n", i, test.want, fact.reassembly) + } + } +} + +func TestReorder(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1001, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1004, + BaseLayer: layers.BaseLayer{Payload: []byte{2, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{4, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Skip: -1, + Bytes: []byte{1, 2, 3}, + }, + Reassembly{ + Bytes: []byte{2, 2, 3}, + }, + Reassembly{ + Bytes: []byte{3, 2, 3}, + }, + Reassembly{ + Bytes: []byte{4, 2, 3}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1016, + BaseLayer: layers.BaseLayer{Payload: []byte{2, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1019, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1013, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{1, 2, 3}, + }, + Reassembly{ + Bytes: []byte{2, 2, 3}, + }, + Reassembly{ + Bytes: []byte{3, 2, 3}, + }, + }, + }, + }) +} + +func TestMaxPerSkip(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1000, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{4, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1013, + BaseLayer: layers.BaseLayer{Payload: []byte{5, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1016, + BaseLayer: layers.BaseLayer{Payload: []byte{6, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Skip: 3, + Bytes: []byte{3, 2, 3}, + }, + Reassembly{ + Bytes: []byte{4, 2, 3}, + }, + Reassembly{ + Bytes: []byte{5, 2, 3}, + }, + Reassembly{ + Bytes: []byte{6, 2, 3}, + }, + }, + }, + }) +} + +func TestReorderFast(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{3, 2, 3}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1004, + BaseLayer: layers.BaseLayer{Payload: []byte{2, 2, 3}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{2, 2, 3}, + }, + Reassembly{ + Bytes: []byte{3, 2, 3}, + }, + }, + }, + }) +} + +func TestOverlap(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0, 1, 2, 3, 4, 5}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{1, 2, 3, 4, 5}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{6, 7}, + }, + }, + }, + }) +} + +func TestBufferedOverlap(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1007, + BaseLayer: layers.BaseLayer{Payload: []byte{7, 8, 9, 0, 1, 2, 3, 4, 5}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1010, + BaseLayer: layers.BaseLayer{Payload: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + Reassembly{ + Bytes: []byte{1, 2, 3, 4, 5}, + }, + Reassembly{ + Bytes: []byte{6, 7}, + }, + }, + }, + }) +} + +func TestOverrun1(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 0xFFFFFFFF, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + }, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 10, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4}}, + }, + want: []Reassembly{ + Reassembly{ + Bytes: []byte{1, 2, 3, 4}, + }, + }, + }, + }) +} + +func TestOverrun2(t *testing.T) { + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 10, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4}}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 0xFFFFFFFF, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, + }, + Reassembly{ + Bytes: []byte{1, 2, 3, 4}, + }, + }, + }, + }) +} + +func TestCacheLargePacket(t *testing.T) { + data := make([]byte, pageBytes*3) + test(t, []testSequence{ + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1001, + BaseLayer: layers.BaseLayer{Payload: data}, + }, + want: []Reassembly{}, + }, + { + in: layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 1000, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{}}, + }, + want: []Reassembly{ + Reassembly{ + Start: true, + Bytes: []byte{}, + }, + Reassembly{ + Bytes: data[:pageBytes], + }, + Reassembly{ + Bytes: data[pageBytes : pageBytes*2], + }, + Reassembly{ + Bytes: data[pageBytes*2 : pageBytes*3], + }, + }, + }, + }) +} + +func BenchmarkSingleStream(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactory{})) + for i := 0; i < b.N; i++ { + a.Assemble(netFlow, &t) + if t.SYN { + t.SYN = false + t.Seq++ + } + t.Seq += 10 + } +} + +func BenchmarkSingleStreamSkips(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactory{})) + skipped := false + for i := 0; i < b.N; i++ { + if i%10 == 9 { + t.Seq += 10 + skipped = true + } else if skipped { + t.Seq -= 20 + } + a.Assemble(netFlow, &t) + if t.SYN { + t.SYN = false + t.Seq++ + } + t.Seq += 10 + if skipped { + t.Seq += 10 + skipped = false + } + } +} + +func BenchmarkSingleStreamLoss(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + SYN: true, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactory{})) + for i := 0; i < b.N; i++ { + a.Assemble(netFlow, &t) + t.SYN = false + t.Seq += 11 + } +} + +func BenchmarkMultiStreamGrow(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 0, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactory{})) + for i := 0; i < b.N; i++ { + t.SrcPort = layers.TCPPort(i) + a.Assemble(netFlow, &t) + t.Seq += 10 + } +} + +func BenchmarkMultiStreamConn(b *testing.B) { + t := layers.TCP{ + SrcPort: 1, + DstPort: 2, + Seq: 0, + SYN: true, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}}, + } + a := NewAssembler(NewStreamPool(&testFactory{})) + for i := 0; i < b.N; i++ { + t.SrcPort = layers.TCPPort(i) + a.Assemble(netFlow, &t) + if i%65536 == 65535 { + if t.SYN { + t.SYN = false + t.Seq++ + } + t.Seq += 10 + } + } +} diff --git a/vendor/github.com/google/gopacket/tcpassembly/tcpreader/reader.go b/vendor/github.com/google/gopacket/tcpassembly/tcpreader/reader.go new file mode 100644 index 00000000..092b8110 --- /dev/null +++ b/vendor/github.com/google/gopacket/tcpassembly/tcpreader/reader.go @@ -0,0 +1,210 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package tcpreader provides an implementation for tcpassembly.Stream which presents +// the caller with an io.Reader for easy processing. +// +// The assembly package handles packet data reordering, but its output is +// library-specific, thus not usable by the majority of external Go libraries. +// The io.Reader interface, on the other hand, is used throughout much of Go +// code as an easy mechanism for reading in data streams and decoding them. For +// example, the net/http package provides the ReadRequest function, which can +// parase an HTTP request from a live data stream, just what we'd want when +// sniffing HTTP traffic. Using ReaderStream, this is relatively easy to set +// up: +// +// // Create our StreamFactory +// type httpStreamFactory struct {} +// func (f *httpStreamFactory) New(a, b gopacket.Flow) { +// r := tcpreader.NewReaderStream(false) +// go printRequests(r) +// return &r +// } +// func printRequests(r io.Reader) { +// // Convert to bufio, since that's what ReadRequest wants. +// buf := bufio.NewReader(r) +// for { +// if req, err := http.ReadRequest(buf); err == io.EOF { +// return +// } else if err != nil { +// log.Println("Error parsing HTTP requests:", err) +// } else { +// fmt.Println("HTTP REQUEST:", req) +// fmt.Println("Body contains", tcpreader.DiscardBytesToEOF(req.Body), "bytes") +// } +// } +// } +// +// Using just this code, we're able to reference a powerful, built-in library +// for HTTP request parsing to do all the dirty-work of parsing requests from +// the wire in real-time. Pass this stream factory to an tcpassembly.StreamPool, +// start up an tcpassembly.Assembler, and you're good to go! +package tcpreader + +import ( + "errors" + "github.com/google/gopacket/tcpassembly" + "io" +) + +var discardBuffer = make([]byte, 4096) + +// DiscardBytesToFirstError will read in all bytes up to the first error +// reported by the given reader, then return the number of bytes discarded +// and the error encountered. +func DiscardBytesToFirstError(r io.Reader) (discarded int, err error) { + for { + n, e := r.Read(discardBuffer) + discarded += n + if e != nil { + return discarded, e + } + } +} + +// DiscardBytesToEOF will read in all bytes from a Reader until it +// encounters an io.EOF, then return the number of bytes. Be careful +// of this... if used on a Reader that returns a non-io.EOF error +// consistently, this will loop forever discarding that error while +// it waits for an EOF. +func DiscardBytesToEOF(r io.Reader) (discarded int) { + for { + n, e := DiscardBytesToFirstError(r) + discarded += n + if e == io.EOF { + return + } + } +} + +// ReaderStream implements both tcpassembly.Stream and io.Reader. You can use it +// as a building block to make simple, easy stream handlers. +// +// IMPORTANT: If you use a ReaderStream, you MUST read ALL BYTES from it, +// quickly. Not reading available bytes will block TCP stream reassembly. It's +// a common pattern to do this by starting a goroutine in the factory's New +// method: +// +// type myStreamHandler struct { +// r ReaderStream +// } +// func (m *myStreamHandler) run() { +// // Do something here that reads all of the ReaderStream, or your assembly +// // will block. +// fmt.Println(tcpreader.DiscardBytesToEOF(&m.r)) +// } +// func (f *myStreamFactory) New(a, b gopacket.Flow) tcpassembly.Stream { +// s := &myStreamHandler{} +// go s.run() +// // Return the ReaderStream as the stream that assembly should populate. +// return &s.r +// } +type ReaderStream struct { + ReaderStreamOptions + reassembled chan []tcpassembly.Reassembly + done chan bool + current []tcpassembly.Reassembly + closed bool + lossReported bool + first bool + initiated bool +} + +// ReaderStreamOptions provides user-resettable options for a ReaderStream. +type ReaderStreamOptions struct { + // LossErrors determines whether this stream will return + // ReaderStreamDataLoss errors from its Read function whenever it + // determines data has been lost. + LossErrors bool +} + +// NewReaderStream returns a new ReaderStream object. +func NewReaderStream() ReaderStream { + r := ReaderStream{ + reassembled: make(chan []tcpassembly.Reassembly), + done: make(chan bool), + first: true, + initiated: true, + } + return r +} + +// Reassembled implements tcpassembly.Stream's Reassembled function. +func (r *ReaderStream) Reassembled(reassembly []tcpassembly.Reassembly) { + if !r.initiated { + panic("ReaderStream not created via NewReaderStream") + } + r.reassembled <- reassembly + <-r.done +} + +// ReassemblyComplete implements tcpassembly.Stream's ReassemblyComplete function. +func (r *ReaderStream) ReassemblyComplete() { + close(r.reassembled) + close(r.done) +} + +// stripEmpty strips empty reassembly slices off the front of its current set of +// slices. +func (r *ReaderStream) stripEmpty() { + for len(r.current) > 0 && len(r.current[0].Bytes) == 0 { + r.current = r.current[1:] + r.lossReported = false + } +} + +// DataLost is returned by the ReaderStream's Read function when it encounters +// a Reassembly with Skip != 0. +var DataLost = errors.New("lost data") + +// Read implements io.Reader's Read function. +// Given a byte slice, it will either copy a non-zero number of bytes into +// that slice and return the number of bytes and a nil error, or it will +// leave slice p as is and return 0, io.EOF. +func (r *ReaderStream) Read(p []byte) (int, error) { + if !r.initiated { + panic("ReaderStream not created via NewReaderStream") + } + var ok bool + r.stripEmpty() + for !r.closed && len(r.current) == 0 { + if r.first { + r.first = false + } else { + r.done <- true + } + if r.current, ok = <-r.reassembled; ok { + r.stripEmpty() + } else { + r.closed = true + } + } + if len(r.current) > 0 { + current := &r.current[0] + if r.LossErrors && !r.lossReported && current.Skip != 0 { + r.lossReported = true + return 0, DataLost + } + length := copy(p, current.Bytes) + current.Bytes = current.Bytes[length:] + return length, nil + } + return 0, io.EOF +} + +// Close implements io.Closer's Close function, making ReaderStream a +// io.ReadCloser. It discards all remaining bytes in the reassembly in a +// manner that's safe for the assembler (IE: it doesn't block). +func (r *ReaderStream) Close() error { + r.current = nil + r.closed = true + for { + if _, ok := <-r.reassembled; !ok { + return nil + } + r.done <- true + } +} diff --git a/vendor/github.com/google/gopacket/tcpassembly/tcpreader/reader_test.go b/vendor/github.com/google/gopacket/tcpassembly/tcpreader/reader_test.go new file mode 100644 index 00000000..7da9fd9a --- /dev/null +++ b/vendor/github.com/google/gopacket/tcpassembly/tcpreader/reader_test.go @@ -0,0 +1,129 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package tcpreader + +import ( + "bytes" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/tcpassembly" + "io" + "net" + "testing" +) + +var netFlow gopacket.Flow + +func init() { + netFlow, _ = gopacket.FlowFromEndpoints( + layers.NewIPEndpoint(net.IP{1, 2, 3, 4}), + layers.NewIPEndpoint(net.IP{5, 6, 7, 8})) +} + +type readReturn struct { + data []byte + err error +} +type readSequence struct { + in []layers.TCP + want []readReturn +} +type testReaderFactory struct { + lossErrors bool + readSize int + ReaderStream + output chan []byte +} + +func (t *testReaderFactory) New(a, b gopacket.Flow) tcpassembly.Stream { + return &t.ReaderStream +} + +func testReadSequence(t *testing.T, lossErrors bool, readSize int, seq readSequence) { + f := &testReaderFactory{ReaderStream: NewReaderStream()} + f.ReaderStream.LossErrors = lossErrors + p := tcpassembly.NewStreamPool(f) + a := tcpassembly.NewAssembler(p) + buf := make([]byte, readSize) + go func() { + for i, test := range seq.in { + fmt.Println("Assembling", i) + a.Assemble(netFlow, &test) + fmt.Println("Assembly done") + } + }() + for i, test := range seq.want { + fmt.Println("Waiting for read", i) + n, err := f.Read(buf[:]) + fmt.Println("Got read") + if n != len(test.data) { + t.Errorf("test %d want %d bytes, got %d bytes", i, len(test.data), n) + } else if err != test.err { + t.Errorf("test %d want err %v, got err %v", i, test.err, err) + } else if !bytes.Equal(buf[:n], test.data) { + t.Errorf("test %d\nwant: %v\n got: %v\n", i, test.data, buf[:n]) + } + } + fmt.Println("All done reads") +} + +func TestRead(t *testing.T) { + testReadSequence(t, false, 10, readSequence{ + in: []layers.TCP{ + { + SYN: true, + SrcPort: 1, + DstPort: 2, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + { + FIN: true, + SrcPort: 1, + DstPort: 2, + Seq: 1004, + }, + }, + want: []readReturn{ + {data: []byte{1, 2, 3}}, + {err: io.EOF}, + }, + }) +} + +func TestReadSmallChunks(t *testing.T) { + testReadSequence(t, false, 2, readSequence{ + in: []layers.TCP{ + { + SYN: true, + SrcPort: 1, + DstPort: 2, + Seq: 1000, + BaseLayer: layers.BaseLayer{Payload: []byte{1, 2, 3}}, + }, + { + FIN: true, + SrcPort: 1, + DstPort: 2, + Seq: 1004, + }, + }, + want: []readReturn{ + {data: []byte{1, 2}}, + {data: []byte{3}}, + {err: io.EOF}, + }, + }) +} + +func ExampleDiscardBytesToEOF() { + b := bytes.NewBuffer([]byte{1, 2, 3, 4, 5}) + fmt.Println(DiscardBytesToEOF(b)) + // Output: + // 5 +} diff --git a/vendor/github.com/google/gopacket/writer.go b/vendor/github.com/google/gopacket/writer.go new file mode 100644 index 00000000..9e85a926 --- /dev/null +++ b/vendor/github.com/google/gopacket/writer.go @@ -0,0 +1,213 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" +) + +// SerializableLayer allows its implementations to be written out as a set of bytes, +// so those bytes may be sent on the wire or otherwise used by the caller. +// SerializableLayer is implemented by certain Layer types, and can be encoded to +// bytes using the LayerWriter object. +type SerializableLayer interface { + // SerializeTo writes this layer to a slice, growing that slice if necessary + // to make it fit the layer's data. + // Args: + // b: SerializeBuffer to write this layer on to. When called, b.Bytes() + // is the payload this layer should wrap, if any. Note that this + // layer can either prepend itself (common), append itself + // (uncommon), or both (sometimes padding or footers are required at + // the end of packet data). It's also possible (though probably very + // rarely needed) to overwrite any bytes in the current payload. + // After this call, b.Bytes() should return the byte encoding of + // this layer wrapping the original b.Bytes() payload. + // opts: options to use while writing out data. + // Returns: + // error if a problem was encountered during encoding. If an error is + // returned, the bytes in data should be considered invalidated, and + // not used. + // + // SerializeTo calls SHOULD entirely ignore LayerContents and + // LayerPayload. It just serializes based on struct fields, neither + // modifying nor using contents/payload. + SerializeTo(b SerializeBuffer, opts SerializeOptions) error +} + +// SerializeOptions provides options for behaviors that SerializableLayers may want to +// implement. +type SerializeOptions struct { + // FixLengths determines whether, during serialization, layers should fix + // the values for any length field that depends on the payload. + FixLengths bool + // ComputeChecksums determines whether, during serialization, layers + // should recompute checksums based on their payloads. + ComputeChecksums bool +} + +// SerializeBuffer is a helper used by gopacket for writing out packet layers. +// SerializeBuffer starts off as an empty []byte. Subsequent calls to PrependBytes +// return byte slices before the current Bytes(), AppendBytes returns byte +// slices after. +// +// Byte slices returned by PrependBytes/AppendBytes are NOT zero'd out, so if +// you want to make sure they're all zeros, set them as such. +// +// SerializeBuffer is specifically designed to handle packet writing, where unlike +// with normal writes it's easier to start writing at the inner-most layer and +// work out, meaning that we often need to prepend bytes. This runs counter to +// typical writes to byte slices using append(), where we only write at the end +// of the buffer. +// +// It can be reused via Clear. Note, however, that a Clear call will invalidate the +// byte slices returned by any previous Bytes() call (the same buffer is +// reused). +// +// 1) Reusing a write buffer is generally much faster than creating a new one, +// and with the default implementation it avoids additional memory allocations. +// 2) If a byte slice from a previous Bytes() call will continue to be used, +// it's better to create a new SerializeBuffer. +// +// The Clear method is specifically designed to minimize memory allocations for +// similar later workloads on the SerializeBuffer. IE: if you make a set of +// Prepend/Append calls, then clear, then make the same calls with the same +// sizes, the second round (and all future similar rounds) shouldn't allocate +// any new memory. +type SerializeBuffer interface { + // Bytes returns the contiguous set of bytes collected so far by Prepend/Append + // calls. The slice returned by Bytes will be modified by future Clear calls, + // so if you're planning on clearing this SerializeBuffer, you may want to copy + // Bytes somewhere safe first. + Bytes() []byte + // PrependBytes returns a set of bytes which prepends the current bytes in this + // buffer. These bytes start in an indeterminate state, so they should be + // overwritten by the caller. The caller must only call PrependBytes if they + // know they're going to immediately overwrite all bytes returned. + PrependBytes(num int) ([]byte, error) + // AppendBytes returns a set of bytes which appends the current bytes in this + // buffer. These bytes start in an indeterminate state, so they should be + // overwritten by the caller. The caller must only call AppendBytes if they + // know they're going to immediately overwrite all bytes returned. + AppendBytes(num int) ([]byte, error) + // Clear resets the SerializeBuffer to a new, empty buffer. After a call to clear, + // the byte slice returned by any previous call to Bytes() for this buffer + // should be considered invalidated. + Clear() error +} + +type serializeBuffer struct { + data []byte + start int + prepended, appended int +} + +// NewSerializeBuffer creates a new instance of the default implementation of +// the SerializeBuffer interface. +func NewSerializeBuffer() SerializeBuffer { + return &serializeBuffer{} +} + +// NewSerializeBufferExpectedSize creates a new buffer for serialization, optimized for an +// expected number of bytes prepended/appended. This tends to decrease the +// number of memory allocations made by the buffer during writes. +func NewSerializeBufferExpectedSize(expectedPrependLength, expectedAppendLength int) SerializeBuffer { + return &serializeBuffer{ + data: make([]byte, expectedPrependLength, expectedPrependLength+expectedAppendLength), + start: expectedPrependLength, + prepended: expectedPrependLength, + appended: expectedAppendLength, + } +} + +func (w *serializeBuffer) Bytes() []byte { + return w.data[w.start:] +} + +func (w *serializeBuffer) PrependBytes(num int) ([]byte, error) { + if num < 0 { + panic("num < 0") + } + if w.start < num { + toPrepend := w.prepended + if toPrepend < num { + toPrepend = num + } + w.prepended += toPrepend + length := cap(w.data) + toPrepend + newData := make([]byte, length) + newStart := w.start + toPrepend + copy(newData[newStart:], w.data[w.start:]) + w.start = newStart + w.data = newData[:toPrepend+len(w.data)] + } + w.start -= num + return w.data[w.start : w.start+num], nil +} + +func (w *serializeBuffer) AppendBytes(num int) ([]byte, error) { + if num < 0 { + panic("num < 0") + } + initialLength := len(w.data) + if cap(w.data)-initialLength < num { + toAppend := w.appended + if toAppend < num { + toAppend = num + } + w.appended += toAppend + newData := make([]byte, cap(w.data)+toAppend) + copy(newData[w.start:], w.data[w.start:]) + w.data = newData[:initialLength] + } + // Grow the buffer. We know it'll be under capacity given above. + w.data = w.data[:initialLength+num] + return w.data[initialLength:], nil +} + +func (w *serializeBuffer) Clear() error { + w.start = w.prepended + w.data = w.data[:w.start] + return nil +} + +// SerializeLayers clears the given write buffer, then writes all layers into it so +// they correctly wrap each other. Note that by clearing the buffer, it +// invalidates all slices previously returned by w.Bytes() +// +// Example: +// buf := gopacket.NewSerializeBuffer() +// opts := gopacket.SerializeOptions{} +// gopacket.SerializeLayers(buf, opts, a, b, c) +// firstPayload := buf.Bytes() // contains byte representation of a(b(c)) +// gopacket.SerializeLayers(buf, opts, d, e, f) +// secondPayload := buf.Bytes() // contains byte representation of d(e(f)). firstPayload is now invalidated, since the SerializeLayers call Clears buf. +func SerializeLayers(w SerializeBuffer, opts SerializeOptions, layers ...SerializableLayer) error { + w.Clear() + for i := len(layers) - 1; i >= 0; i-- { + layer := layers[i] + err := layer.SerializeTo(w, opts) + if err != nil { + return err + } + } + return nil +} + +// SerializePacket is a convenience function that calls SerializeLayers +// on packet's Layers(). +// It returns an error if one of the packet layers is not a SerializebleLayer. +func SerializePacket(buf SerializeBuffer, opts SerializeOptions, packet Packet) error { + sls := []SerializableLayer{} + for _, layer := range packet.Layers() { + sl, ok := layer.(SerializableLayer) + if !ok { + return fmt.Errorf("layer %s is not serializable", layer.LayerType().String()) + } + sls = append(sls, sl) + } + return SerializeLayers(buf, opts, sls...) +} diff --git a/vendor/github.com/google/gopacket/writer_test.go b/vendor/github.com/google/gopacket/writer_test.go new file mode 100644 index 00000000..e19069b7 --- /dev/null +++ b/vendor/github.com/google/gopacket/writer_test.go @@ -0,0 +1,94 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" + "testing" +) + +func TestExponentialSizeIncreasePrepend(t *testing.T) { + var b serializeBuffer + for i, test := range []struct { + prepend, size int + }{ + {2, 2}, + {2, 4}, + {2, 8}, + {2, 8}, + {2, 16}, + {2, 16}, + {2, 16}, + {2, 16}, + {2, 32}, + } { + b.PrependBytes(test.prepend) + if test.size != cap(b.data) { + t.Error(i, "size want", test.size, "got", cap(b.data)) + } + } + b.Clear() + if b.start != 32 { + t.Error(b.start) + } +} + +func TestExponentialSizeIncreaseAppend(t *testing.T) { + var b serializeBuffer + for i, test := range []struct { + appnd, size int + }{ + {2, 2}, + {2, 4}, + {2, 8}, + {2, 8}, + {2, 16}, + {2, 16}, + {2, 16}, + {2, 16}, + {2, 32}, + } { + b.AppendBytes(test.appnd) + if test.size != cap(b.data) { + t.Error(i, "size want", test.size, "got", cap(b.data)) + } + } + b.Clear() + if b.start != 0 { + t.Error(b.start) + } +} + +func ExampleSerializeBuffer() { + b := NewSerializeBuffer() + fmt.Println("1:", b.Bytes()) + bytes, _ := b.PrependBytes(3) + copy(bytes, []byte{1, 2, 3}) + fmt.Println("2:", b.Bytes()) + bytes, _ = b.AppendBytes(2) + copy(bytes, []byte{4, 5}) + fmt.Println("3:", b.Bytes()) + bytes, _ = b.PrependBytes(1) + copy(bytes, []byte{0}) + fmt.Println("4:", b.Bytes()) + bytes, _ = b.AppendBytes(3) + copy(bytes, []byte{6, 7, 8}) + fmt.Println("5:", b.Bytes()) + b.Clear() + fmt.Println("6:", b.Bytes()) + bytes, _ = b.PrependBytes(2) + copy(bytes, []byte{9, 9}) + fmt.Println("7:", b.Bytes()) + // Output: + // 1: [] + // 2: [1 2 3] + // 3: [1 2 3 4 5] + // 4: [0 1 2 3 4 5] + // 5: [0 1 2 3 4 5 6 7 8] + // 6: [] + // 7: [9 9] +} diff --git a/vendor/github.com/gorilla/context/.travis.yml b/vendor/github.com/gorilla/context/.travis.yml new file mode 100644 index 00000000..6f440f1e --- /dev/null +++ b/vendor/github.com/gorilla/context/.travis.yml @@ -0,0 +1,19 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.3 + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: 1.7 + - go: tip + allow_failures: + - go: tip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... diff --git a/vendor/github.com/gorilla/context/LICENSE b/vendor/github.com/gorilla/context/LICENSE new file mode 100644 index 00000000..0e5fb872 --- /dev/null +++ b/vendor/github.com/gorilla/context/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 Rodrigo Moraes. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/context/README.md b/vendor/github.com/gorilla/context/README.md new file mode 100644 index 00000000..08f86693 --- /dev/null +++ b/vendor/github.com/gorilla/context/README.md @@ -0,0 +1,10 @@ +context +======= +[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) + +gorilla/context is a general purpose registry for global request variables. + +> Note: gorilla/context, having been born well before `context.Context` existed, does not play well +> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`. + +Read the full documentation here: http://www.gorillatoolkit.org/pkg/context diff --git a/vendor/github.com/gorilla/context/context.go b/vendor/github.com/gorilla/context/context.go new file mode 100644 index 00000000..81cb128b --- /dev/null +++ b/vendor/github.com/gorilla/context/context.go @@ -0,0 +1,143 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "net/http" + "sync" + "time" +) + +var ( + mutex sync.RWMutex + data = make(map[*http.Request]map[interface{}]interface{}) + datat = make(map[*http.Request]int64) +) + +// Set stores a value for a given key in a given request. +func Set(r *http.Request, key, val interface{}) { + mutex.Lock() + if data[r] == nil { + data[r] = make(map[interface{}]interface{}) + datat[r] = time.Now().Unix() + } + data[r][key] = val + mutex.Unlock() +} + +// Get returns a value stored for a given key in a given request. +func Get(r *http.Request, key interface{}) interface{} { + mutex.RLock() + if ctx := data[r]; ctx != nil { + value := ctx[key] + mutex.RUnlock() + return value + } + mutex.RUnlock() + return nil +} + +// GetOk returns stored value and presence state like multi-value return of map access. +func GetOk(r *http.Request, key interface{}) (interface{}, bool) { + mutex.RLock() + if _, ok := data[r]; ok { + value, ok := data[r][key] + mutex.RUnlock() + return value, ok + } + mutex.RUnlock() + return nil, false +} + +// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. +func GetAll(r *http.Request) map[interface{}]interface{} { + mutex.RLock() + if context, ok := data[r]; ok { + result := make(map[interface{}]interface{}, len(context)) + for k, v := range context { + result[k] = v + } + mutex.RUnlock() + return result + } + mutex.RUnlock() + return nil +} + +// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if +// the request was registered. +func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { + mutex.RLock() + context, ok := data[r] + result := make(map[interface{}]interface{}, len(context)) + for k, v := range context { + result[k] = v + } + mutex.RUnlock() + return result, ok +} + +// Delete removes a value stored for a given key in a given request. +func Delete(r *http.Request, key interface{}) { + mutex.Lock() + if data[r] != nil { + delete(data[r], key) + } + mutex.Unlock() +} + +// Clear removes all values stored for a given request. +// +// This is usually called by a handler wrapper to clean up request +// variables at the end of a request lifetime. See ClearHandler(). +func Clear(r *http.Request) { + mutex.Lock() + clear(r) + mutex.Unlock() +} + +// clear is Clear without the lock. +func clear(r *http.Request) { + delete(data, r) + delete(datat, r) +} + +// Purge removes request data stored for longer than maxAge, in seconds. +// It returns the amount of requests removed. +// +// If maxAge <= 0, all request data is removed. +// +// This is only used for sanity check: in case context cleaning was not +// properly set some request data can be kept forever, consuming an increasing +// amount of memory. In case this is detected, Purge() must be called +// periodically until the problem is fixed. +func Purge(maxAge int) int { + mutex.Lock() + count := 0 + if maxAge <= 0 { + count = len(data) + data = make(map[*http.Request]map[interface{}]interface{}) + datat = make(map[*http.Request]int64) + } else { + min := time.Now().Unix() - int64(maxAge) + for r := range data { + if datat[r] < min { + clear(r) + count++ + } + } + } + mutex.Unlock() + return count +} + +// ClearHandler wraps an http.Handler and clears request values at the end +// of a request lifetime. +func ClearHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer Clear(r) + h.ServeHTTP(w, r) + }) +} diff --git a/vendor/github.com/gorilla/context/context_test.go b/vendor/github.com/gorilla/context/context_test.go new file mode 100644 index 00000000..d70e91a2 --- /dev/null +++ b/vendor/github.com/gorilla/context/context_test.go @@ -0,0 +1,161 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "net/http" + "testing" +) + +type keyType int + +const ( + key1 keyType = iota + key2 +) + +func TestContext(t *testing.T) { + assertEqual := func(val interface{}, exp interface{}) { + if val != exp { + t.Errorf("Expected %v, got %v.", exp, val) + } + } + + r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + + // Get() + assertEqual(Get(r, key1), nil) + + // Set() + Set(r, key1, "1") + assertEqual(Get(r, key1), "1") + assertEqual(len(data[r]), 1) + + Set(r, key2, "2") + assertEqual(Get(r, key2), "2") + assertEqual(len(data[r]), 2) + + //GetOk + value, ok := GetOk(r, key1) + assertEqual(value, "1") + assertEqual(ok, true) + + value, ok = GetOk(r, "not exists") + assertEqual(value, nil) + assertEqual(ok, false) + + Set(r, "nil value", nil) + value, ok = GetOk(r, "nil value") + assertEqual(value, nil) + assertEqual(ok, true) + + // GetAll() + values := GetAll(r) + assertEqual(len(values), 3) + + // GetAll() for empty request + values = GetAll(emptyR) + if values != nil { + t.Error("GetAll didn't return nil value for invalid request") + } + + // GetAllOk() + values, ok = GetAllOk(r) + assertEqual(len(values), 3) + assertEqual(ok, true) + + // GetAllOk() for empty request + values, ok = GetAllOk(emptyR) + assertEqual(len(values), 0) + assertEqual(ok, false) + + // Delete() + Delete(r, key1) + assertEqual(Get(r, key1), nil) + assertEqual(len(data[r]), 2) + + Delete(r, key2) + assertEqual(Get(r, key2), nil) + assertEqual(len(data[r]), 1) + + // Clear() + Clear(r) + assertEqual(len(data), 0) +} + +func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) { + <-wait + for i := 0; i < iterations; i++ { + Get(r, key) + } + done <- struct{}{} + +} + +func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { + <-wait + for i := 0; i < iterations; i++ { + Set(r, key, value) + } + done <- struct{}{} + +} + +func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { + + b.StopTimer() + r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + done := make(chan struct{}) + b.StartTimer() + + for i := 0; i < b.N; i++ { + wait := make(chan struct{}) + + for i := 0; i < numReaders; i++ { + go parallelReader(r, "test", iterations, wait, done) + } + + for i := 0; i < numWriters; i++ { + go parallelWriter(r, "test", "123", iterations, wait, done) + } + + close(wait) + + for i := 0; i < numReaders+numWriters; i++ { + <-done + } + + } + +} + +func BenchmarkMutexSameReadWrite1(b *testing.B) { + benchmarkMutex(b, 1, 1, 32) +} +func BenchmarkMutexSameReadWrite2(b *testing.B) { + benchmarkMutex(b, 2, 2, 32) +} +func BenchmarkMutexSameReadWrite4(b *testing.B) { + benchmarkMutex(b, 4, 4, 32) +} +func BenchmarkMutex1(b *testing.B) { + benchmarkMutex(b, 2, 8, 32) +} +func BenchmarkMutex2(b *testing.B) { + benchmarkMutex(b, 16, 4, 64) +} +func BenchmarkMutex3(b *testing.B) { + benchmarkMutex(b, 1, 2, 128) +} +func BenchmarkMutex4(b *testing.B) { + benchmarkMutex(b, 128, 32, 256) +} +func BenchmarkMutex5(b *testing.B) { + benchmarkMutex(b, 1024, 2048, 64) +} +func BenchmarkMutex6(b *testing.B) { + benchmarkMutex(b, 2048, 1024, 512) +} diff --git a/vendor/github.com/gorilla/context/doc.go b/vendor/github.com/gorilla/context/doc.go new file mode 100644 index 00000000..448d1bfc --- /dev/null +++ b/vendor/github.com/gorilla/context/doc.go @@ -0,0 +1,88 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package context stores values shared during a request lifetime. + +Note: gorilla/context, having been born well before `context.Context` existed, +does not play well > with the shallow copying of the request that +[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) +(added to net/http Go 1.7 onwards) performs. You should either use *just* +gorilla/context, or moving forward, the new `http.Request.Context()`. + +For example, a router can set variables extracted from the URL and later +application handlers can access those values, or it can be used to store +sessions values to be saved at the end of a request. There are several +others common uses. + +The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: + + http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 + +Here's the basic usage: first define the keys that you will need. The key +type is interface{} so a key can be of any type that supports equality. +Here we define a key using a custom int type to avoid name collisions: + + package foo + + import ( + "github.com/gorilla/context" + ) + + type key int + + const MyKey key = 0 + +Then set a variable. Variables are bound to an http.Request object, so you +need a request instance to set a value: + + context.Set(r, MyKey, "bar") + +The application can later access the variable using the same key you provided: + + func MyHandler(w http.ResponseWriter, r *http.Request) { + // val is "bar". + val := context.Get(r, foo.MyKey) + + // returns ("bar", true) + val, ok := context.GetOk(r, foo.MyKey) + // ... + } + +And that's all about the basic usage. We discuss some other ideas below. + +Any type can be stored in the context. To enforce a given type, make the key +private and wrap Get() and Set() to accept and return values of a specific +type: + + type key int + + const mykey key = 0 + + // GetMyKey returns a value for this package from the request values. + func GetMyKey(r *http.Request) SomeType { + if rv := context.Get(r, mykey); rv != nil { + return rv.(SomeType) + } + return nil + } + + // SetMyKey sets a value for this package in the request values. + func SetMyKey(r *http.Request, val SomeType) { + context.Set(r, mykey, val) + } + +Variables must be cleared at the end of a request, to remove all values +that were stored. This can be done in an http.Handler, after a request was +served. Just call Clear() passing the request: + + context.Clear(r) + +...or use ClearHandler(), which conveniently wraps an http.Handler to clear +variables at the end of a request lifetime. + +The Routers from the packages gorilla/mux and gorilla/pat call Clear() +so if you are using either of them you don't need to clear the context manually. +*/ +package context diff --git a/vendor/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml new file mode 100644 index 00000000..3302233f --- /dev/null +++ b/vendor/github.com/gorilla/mux/.travis.yml @@ -0,0 +1,22 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.5 + - go: 1.6 + - go: 1.7 + - go: 1.8 + - go: 1.9 + - go: tip + allow_failures: + - go: tip + +install: + - # Skip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go tool vet . + - go test -v -race ./... diff --git a/vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md b/vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..232be82e --- /dev/null +++ b/vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ +**What version of Go are you running?** (Paste the output of `go version`) + + +**What version of gorilla/mux are you at?** (Paste the output of `git rev-parse HEAD` inside `$GOPATH/src/github.com/gorilla/mux`) + + +**Describe your problem** (and what you have tried so far) + + +**Paste a minimal, runnable, reproduction of your issue below** (use backticks to format it) + diff --git a/vendor/github.com/gorilla/mux/LICENSE b/vendor/github.com/gorilla/mux/LICENSE new file mode 100644 index 00000000..0e5fb872 --- /dev/null +++ b/vendor/github.com/gorilla/mux/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 Rodrigo Moraes. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md new file mode 100644 index 00000000..f9b3103f --- /dev/null +++ b/vendor/github.com/gorilla/mux/README.md @@ -0,0 +1,560 @@ +gorilla/mux +=== +[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) +[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux) +[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) + +![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png) + +http://www.gorillatoolkit.org/pkg/mux + +Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to +their respective handler. + +The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are: + +* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. +* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. +* URL hosts, paths and query values can have variables with an optional regular expression. +* Registered URLs can be built, or "reversed", which helps maintaining references to resources. +* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching. + +--- + +* [Install](#install) +* [Examples](#examples) +* [Matching Routes](#matching-routes) +* [Static Files](#static-files) +* [Registered URLs](#registered-urls) +* [Walking Routes](#walking-routes) +* [Graceful Shutdown](#graceful-shutdown) +* [Middleware](#middleware) +* [Full Example](#full-example) + +--- + +## Install + +With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain: + +```sh +go get -u github.com/gorilla/mux +``` + +## Examples + +Let's start registering a couple of URL paths and handlers: + +```go +func main() { + r := mux.NewRouter() + r.HandleFunc("/", HomeHandler) + r.HandleFunc("/products", ProductsHandler) + r.HandleFunc("/articles", ArticlesHandler) + http.Handle("/", r) +} +``` + +Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters. + +Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example: + +```go +r := mux.NewRouter() +r.HandleFunc("/products/{key}", ProductHandler) +r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) +r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) +``` + +The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`: + +```go +func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "Category: %v\n", vars["category"]) +} +``` + +And this is all you need to know about the basic usage. More advanced options are explained below. + +### Matching Routes + +Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables: + +```go +r := mux.NewRouter() +// Only matches if domain is "www.example.com". +r.Host("www.example.com") +// Matches a dynamic subdomain. +r.Host("{subdomain:[a-z]+}.domain.com") +``` + +There are several other matchers that can be added. To match path prefixes: + +```go +r.PathPrefix("/products/") +``` + +...or HTTP methods: + +```go +r.Methods("GET", "POST") +``` + +...or URL schemes: + +```go +r.Schemes("https") +``` + +...or header values: + +```go +r.Headers("X-Requested-With", "XMLHttpRequest") +``` + +...or query values: + +```go +r.Queries("key", "value") +``` + +...or to use a custom matcher function: + +```go +r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { + return r.ProtoMajor == 0 +}) +``` + +...and finally, it is possible to combine several matchers in a single route: + +```go +r.HandleFunc("/products", ProductsHandler). + Host("www.example.com"). + Methods("GET"). + Schemes("http") +``` + +Routes are tested in the order they were added to the router. If two routes match, the first one wins: + +```go +r := mux.NewRouter() +r.HandleFunc("/specific", specificHandler) +r.PathPrefix("/").Handler(catchAllHandler) +``` + +Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting". + +For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it: + +```go +r := mux.NewRouter() +s := r.Host("www.example.com").Subrouter() +``` + +Then register routes in the subrouter: + +```go +s.HandleFunc("/products/", ProductsHandler) +s.HandleFunc("/products/{key}", ProductHandler) +s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) +``` + +The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route. + +Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter. + +There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths: + +```go +r := mux.NewRouter() +s := r.PathPrefix("/products").Subrouter() +// "/products/" +s.HandleFunc("/", ProductsHandler) +// "/products/{key}/" +s.HandleFunc("/{key}/", ProductHandler) +// "/products/{key}/details" +s.HandleFunc("/{key}/details", ProductDetailsHandler) +``` +### Listing Routes + +Routes on a mux can be listed using the Router.Walk method—useful for generating documentation: + +```go +package main + +import ( + "fmt" + "net/http" + "strings" + + "github.com/gorilla/mux" +) + +func handler(w http.ResponseWriter, r *http.Request) { + return +} + +func main() { + r := mux.NewRouter() + r.HandleFunc("/", handler) + r.HandleFunc("/products", handler).Methods("POST") + r.HandleFunc("/articles", handler).Methods("GET") + r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") + r.HandleFunc("/authors", handler).Queries("surname", "{surname}") + r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + t, err := route.GetPathTemplate() + if err != nil { + return err + } + qt, err := route.GetQueriesTemplates() + if err != nil { + return err + } + // p will contain regular expression is compatible with regular expression in Perl, Python, and other languages. + // for instance the regular expression for path '/articles/{id}' will be '^/articles/(?P[^/]+)$' + p, err := route.GetPathRegexp() + if err != nil { + return err + } + // qr will contain a list of regular expressions with the same semantics as GetPathRegexp, + // just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return + // {"^surname=(?P.*)$}. Where each combined query pair will have an entry in the list. + qr, err := route.GetQueriesRegexp() + if err != nil { + return err + } + m, err := route.GetMethods() + if err != nil { + return err + } + fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p) + return nil + }) + http.Handle("/", r) +} +``` + +### Static Files + +Note that the path provided to `PathPrefix()` represents a "wildcard": calling +`PathPrefix("/static/").Handler(...)` means that the handler will be passed any +request that matches "/static/*". This makes it easy to serve static files with mux: + +```go +func main() { + var dir string + + flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") + flag.Parse() + r := mux.NewRouter() + + // This will serve files under http://localhost:8000/static/ + r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) + + srv := &http.Server{ + Handler: r, + Addr: "127.0.0.1:8000", + // Good practice: enforce timeouts for servers you create! + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + log.Fatal(srv.ListenAndServe()) +} +``` + +### Registered URLs + +Now let's see how to build registered URLs. + +Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example: + +```go +r := mux.NewRouter() +r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). + Name("article") +``` + +To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do: + +```go +url, err := r.Get("article").URL("category", "technology", "id", "42") +``` + +...and the result will be a `url.URL` with the following path: + +``` +"/articles/technology/42" +``` + +This also works for host and query value variables: + +```go +r := mux.NewRouter() +r.Host("{subdomain}.domain.com"). + Path("/articles/{category}/{id:[0-9]+}"). + Queries("filter", "{filter}"). + HandlerFunc(ArticleHandler). + Name("article") + +// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla" +url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42", + "filter", "gorilla") +``` + +All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match. + +Regex support also exists for matching Headers within a route. For example, we could do: + +```go +r.HeadersRegexp("Content-Type", "application/(text|json)") +``` + +...and the route will match both requests with a Content-Type of `application/json` as well as `application/text` + +There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do: + +```go +// "http://news.domain.com/" +host, err := r.Get("article").URLHost("subdomain", "news") + +// "/articles/technology/42" +path, err := r.Get("article").URLPath("category", "technology", "id", "42") +``` + +And if you use subrouters, host and path defined separately can be built as well: + +```go +r := mux.NewRouter() +s := r.Host("{subdomain}.domain.com").Subrouter() +s.Path("/articles/{category}/{id:[0-9]+}"). + HandlerFunc(ArticleHandler). + Name("article") + +// "http://news.domain.com/articles/technology/42" +url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42") +``` + +### Walking Routes + +The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example, +the following prints all of the registered routes: + +```go +r := mux.NewRouter() +r.HandleFunc("/", handler) +r.HandleFunc("/products", handler).Methods("POST") +r.HandleFunc("/articles", handler).Methods("GET") +r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") +r.HandleFunc("/authors", handler).Queries("surname", "{surname}") +r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + t, err := route.GetPathTemplate() + if err != nil { + return err + } + qt, err := route.GetQueriesTemplates() + if err != nil { + return err + } + // p will contain a regular expression that is compatible with regular expressions in Perl, Python, and other languages. + // For example, the regular expression for path '/articles/{id}' will be '^/articles/(?P[^/]+)$'. + p, err := route.GetPathRegexp() + if err != nil { + return err + } + // qr will contain a list of regular expressions with the same semantics as GetPathRegexp, + // just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return + // {"^surname=(?P.*)$}. Where each combined query pair will have an entry in the list. + qr, err := route.GetQueriesRegexp() + if err != nil { + return err + } + m, err := route.GetMethods() + if err != nil { + return err + } + fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p) + return nil +}) +``` + +### Graceful Shutdown + +Go 1.8 introduced the ability to [gracefully shutdown](https://golang.org/doc/go1.8#http_shutdown) a `*http.Server`. Here's how to do that alongside `mux`: + +```go +package main + +import ( + "context" + "flag" + "log" + "net/http" + "os" + "os/signal" + + "github.com/gorilla/mux" +) + +func main() { + var wait time.Duration + flag.DurationVar(&wait, "graceful-timeout", time.Second * 15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m") + flag.Parse() + + r := mux.NewRouter() + // Add your routes as needed + + srv := &http.Server{ + Addr: "0.0.0.0:8080", + // Good practice to set timeouts to avoid Slowloris attacks. + WriteTimeout: time.Second * 15, + ReadTimeout: time.Second * 15, + IdleTimeout: time.Second * 60, + Handler: r, // Pass our instance of gorilla/mux in. + } + + // Run our server in a goroutine so that it doesn't block. + go func() { + if err := srv.ListenAndServe(); err != nil { + log.Println(err) + } + }() + + c := make(chan os.Signal, 1) + // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C) + // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught. + signal.Notify(c, os.Interrupt) + + // Block until we receive our signal. + <-c + + // Create a deadline to wait for. + ctx, cancel := context.WithTimeout(ctx, wait) + // Doesn't block if no connections, but will otherwise wait + // until the timeout deadline. + srv.Shutdown(ctx) + // Optionally, you could run srv.Shutdown in a goroutine and block on + // <-ctx.Done() if your application should wait for other services + // to finalize based on context cancellation. + log.Println("shutting down") + os.Exit(0) +} +``` + +### Middleware + +Mux supports the addition of middlewares to a [Router](https://godoc.org/github.com/gorilla/mux#Router), which are executed in the order they are added if a match is found, including its subrouters. +Middlewares are (typically) small pieces of code which take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or `ResponseWriter` hijacking. + +Mux middlewares are defined using the de facto standard type: + +```go +type MiddlewareFunc func(http.Handler) http.Handler +``` + +Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc. This takes advantage of closures being able access variables from the context where they are created, while retaining the signature enforced by the receivers. + +A very basic middleware which logs the URI of the request being handled could be written as: + +```go +func simpleMw(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Do stuff here + log.Println(r.RequestURI) + // Call the next handler, which can be another middleware in the chain, or the final handler. + next.ServeHTTP(w, r) + }) +} +``` + +Middlewares can be added to a router using `Router.AddMiddlewareFunc()`: + +```go +r := mux.NewRouter() +r.HandleFunc("/", handler) +r.AddMiddleware(simpleMw) +``` + +A more complex authentication middleware, which maps session token to users, could be written as: + +```go +// Define our struct +type authenticationMiddleware struct { + tokenUsers map[string]string +} + +// Initialize it somewhere +func (amw *authenticationMiddleware) Populate() { + amw.tokenUsers["00000000"] = "user0" + amw.tokenUsers["aaaaaaaa"] = "userA" + amw.tokenUsers["05f717e5"] = "randomUser" + amw.tokenUsers["deadbeef"] = "user0" +} + +// Middleware function, which will be called for each request +func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("X-Session-Token") + + if user, found := amw.tokenUsers[token]; found { + // We found the token in our map + log.Printf("Authenticated user %s\n", user) + // Pass down the request to the next middleware (or final handler) + next.ServeHTTP(w, r) + } else { + // Write an error and stop the handler chain + http.Error(w, "Forbidden", 403) + } + }) +} +``` + +```go +r := mux.NewRouter() +r.HandleFunc("/", handler) + +amw := authenticationMiddleware{} +amw.Populate() + +r.AddMiddlewareFunc(amw.Middleware) +``` + +Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares *should* write to `ResponseWriter` if they *are* going to terminate the request, and they *should not* write to `ResponseWriter` if they *are not* going to terminate it. + +## Full Example + +Here's a complete, runnable example of a small `mux` based server: + +```go +package main + +import ( + "net/http" + "log" + "github.com/gorilla/mux" +) + +func YourHandler(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Gorilla!\n")) +} + +func main() { + r := mux.NewRouter() + // Routes consist of a path and a handler function. + r.HandleFunc("/", YourHandler) + + // Bind to a port and pass our router in + log.Fatal(http.ListenAndServe(":8000", r)) +} +``` + +## License + +BSD licensed. See the LICENSE file for details. diff --git a/vendor/github.com/gorilla/mux/bench_test.go b/vendor/github.com/gorilla/mux/bench_test.go new file mode 100644 index 00000000..522156dc --- /dev/null +++ b/vendor/github.com/gorilla/mux/bench_test.go @@ -0,0 +1,49 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func BenchmarkMux(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1}", handler) + + request, _ := http.NewRequest("GET", "/v1/anything", nil) + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, request) + } +} + +func BenchmarkMuxAlternativeInRegexp(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1:(?:a|b)}", handler) + + requestA, _ := http.NewRequest("GET", "/v1/a", nil) + requestB, _ := http.NewRequest("GET", "/v1/b", nil) + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, requestA) + router.ServeHTTP(nil, requestB) + } +} + +func BenchmarkManyPathVariables(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1}/{v2}/{v3}/{v4}/{v5}", handler) + + matchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4/5", nil) + notMatchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4", nil) + recorder := httptest.NewRecorder() + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, matchingRequest) + router.ServeHTTP(recorder, notMatchingRequest) + } +} diff --git a/vendor/github.com/gorilla/mux/context_gorilla.go b/vendor/github.com/gorilla/mux/context_gorilla.go new file mode 100644 index 00000000..d7adaa8f --- /dev/null +++ b/vendor/github.com/gorilla/mux/context_gorilla.go @@ -0,0 +1,26 @@ +// +build !go1.7 + +package mux + +import ( + "net/http" + + "github.com/gorilla/context" +) + +func contextGet(r *http.Request, key interface{}) interface{} { + return context.Get(r, key) +} + +func contextSet(r *http.Request, key, val interface{}) *http.Request { + if val == nil { + return r + } + + context.Set(r, key, val) + return r +} + +func contextClear(r *http.Request) { + context.Clear(r) +} diff --git a/vendor/github.com/gorilla/mux/context_gorilla_test.go b/vendor/github.com/gorilla/mux/context_gorilla_test.go new file mode 100644 index 00000000..ffaf384c --- /dev/null +++ b/vendor/github.com/gorilla/mux/context_gorilla_test.go @@ -0,0 +1,40 @@ +// +build !go1.7 + +package mux + +import ( + "net/http" + "testing" + + "github.com/gorilla/context" +) + +// Tests that the context is cleared or not cleared properly depending on +// the configuration of the router +func TestKeepContext(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost/", nil) + context.Set(req, "t", 1) + + res := new(http.ResponseWriter) + r.ServeHTTP(*res, req) + + if _, ok := context.GetOk(req, "t"); ok { + t.Error("Context should have been cleared at end of request") + } + + r.KeepContext = true + + req, _ = http.NewRequest("GET", "http://localhost/", nil) + context.Set(req, "t", 1) + + r.ServeHTTP(*res, req) + if _, ok := context.GetOk(req, "t"); !ok { + t.Error("Context should NOT have been cleared at end of request") + } + +} diff --git a/vendor/github.com/gorilla/mux/context_native.go b/vendor/github.com/gorilla/mux/context_native.go new file mode 100644 index 00000000..209cbea7 --- /dev/null +++ b/vendor/github.com/gorilla/mux/context_native.go @@ -0,0 +1,24 @@ +// +build go1.7 + +package mux + +import ( + "context" + "net/http" +) + +func contextGet(r *http.Request, key interface{}) interface{} { + return r.Context().Value(key) +} + +func contextSet(r *http.Request, key, val interface{}) *http.Request { + if val == nil { + return r + } + + return r.WithContext(context.WithValue(r.Context(), key, val)) +} + +func contextClear(r *http.Request) { + return +} diff --git a/vendor/github.com/gorilla/mux/context_native_test.go b/vendor/github.com/gorilla/mux/context_native_test.go new file mode 100644 index 00000000..c150edf0 --- /dev/null +++ b/vendor/github.com/gorilla/mux/context_native_test.go @@ -0,0 +1,32 @@ +// +build go1.7 + +package mux + +import ( + "context" + "net/http" + "testing" + "time" +) + +func TestNativeContextMiddleware(t *testing.T) { + withTimeout := func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(r.Context(), time.Minute) + defer cancel() + h.ServeHTTP(w, r.WithContext(ctx)) + }) + } + + r := NewRouter() + r.Handle("/path/{foo}", withTimeout(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + vars := Vars(r) + if vars["foo"] != "bar" { + t.Fatal("Expected foo var to be set") + } + }))) + + rec := NewRecorder() + req := newRequest("GET", "/path/bar") + r.ServeHTTP(rec, req) +} diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go new file mode 100644 index 00000000..013f0889 --- /dev/null +++ b/vendor/github.com/gorilla/mux/doc.go @@ -0,0 +1,307 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mux implements a request router and dispatcher. + +The name mux stands for "HTTP request multiplexer". Like the standard +http.ServeMux, mux.Router matches incoming requests against a list of +registered routes and calls a handler for the route that matches the URL +or other conditions. The main features are: + + * Requests can be matched based on URL host, path, path prefix, schemes, + header and query values, HTTP methods or using custom matchers. + * URL hosts, paths and query values can have variables with an optional + regular expression. + * Registered URLs can be built, or "reversed", which helps maintaining + references to resources. + * Routes can be used as subrouters: nested routes are only tested if the + parent route matches. This is useful to define groups of routes that + share common conditions like a host, a path prefix or other repeated + attributes. As a bonus, this optimizes request matching. + * It implements the http.Handler interface so it is compatible with the + standard http.ServeMux. + +Let's start registering a couple of URL paths and handlers: + + func main() { + r := mux.NewRouter() + r.HandleFunc("/", HomeHandler) + r.HandleFunc("/products", ProductsHandler) + r.HandleFunc("/articles", ArticlesHandler) + http.Handle("/", r) + } + +Here we register three routes mapping URL paths to handlers. This is +equivalent to how http.HandleFunc() works: if an incoming request URL matches +one of the paths, the corresponding handler is called passing +(http.ResponseWriter, *http.Request) as parameters. + +Paths can have variables. They are defined using the format {name} or +{name:pattern}. If a regular expression pattern is not defined, the matched +variable will be anything until the next slash. For example: + + r := mux.NewRouter() + r.HandleFunc("/products/{key}", ProductHandler) + r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) + r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) + +Groups can be used inside patterns, as long as they are non-capturing (?:re). For example: + + r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler) + +The names are used to create a map of route variables which can be retrieved +calling mux.Vars(): + + vars := mux.Vars(request) + category := vars["category"] + +Note that if any capturing groups are present, mux will panic() during parsing. To prevent +this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to +"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably +when capturing groups were present. + +And this is all you need to know about the basic usage. More advanced options +are explained below. + +Routes can also be restricted to a domain or subdomain. Just define a host +pattern to be matched. They can also have variables: + + r := mux.NewRouter() + // Only matches if domain is "www.example.com". + r.Host("www.example.com") + // Matches a dynamic subdomain. + r.Host("{subdomain:[a-z]+}.domain.com") + +There are several other matchers that can be added. To match path prefixes: + + r.PathPrefix("/products/") + +...or HTTP methods: + + r.Methods("GET", "POST") + +...or URL schemes: + + r.Schemes("https") + +...or header values: + + r.Headers("X-Requested-With", "XMLHttpRequest") + +...or query values: + + r.Queries("key", "value") + +...or to use a custom matcher function: + + r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { + return r.ProtoMajor == 0 + }) + +...and finally, it is possible to combine several matchers in a single route: + + r.HandleFunc("/products", ProductsHandler). + Host("www.example.com"). + Methods("GET"). + Schemes("http") + +Setting the same matching conditions again and again can be boring, so we have +a way to group several routes that share the same requirements. +We call it "subrouting". + +For example, let's say we have several URLs that should only match when the +host is "www.example.com". Create a route for that host and get a "subrouter" +from it: + + r := mux.NewRouter() + s := r.Host("www.example.com").Subrouter() + +Then register routes in the subrouter: + + s.HandleFunc("/products/", ProductsHandler) + s.HandleFunc("/products/{key}", ProductHandler) + s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) + +The three URL paths we registered above will only be tested if the domain is +"www.example.com", because the subrouter is tested first. This is not +only convenient, but also optimizes request matching. You can create +subrouters combining any attribute matchers accepted by a route. + +Subrouters can be used to create domain or path "namespaces": you define +subrouters in a central place and then parts of the app can register its +paths relatively to a given subrouter. + +There's one more thing about subroutes. When a subrouter has a path prefix, +the inner routes use it as base for their paths: + + r := mux.NewRouter() + s := r.PathPrefix("/products").Subrouter() + // "/products/" + s.HandleFunc("/", ProductsHandler) + // "/products/{key}/" + s.HandleFunc("/{key}/", ProductHandler) + // "/products/{key}/details" + s.HandleFunc("/{key}/details", ProductDetailsHandler) + +Note that the path provided to PathPrefix() represents a "wildcard": calling +PathPrefix("/static/").Handler(...) means that the handler will be passed any +request that matches "/static/*". This makes it easy to serve static files with mux: + + func main() { + var dir string + + flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") + flag.Parse() + r := mux.NewRouter() + + // This will serve files under http://localhost:8000/static/ + r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) + + srv := &http.Server{ + Handler: r, + Addr: "127.0.0.1:8000", + // Good practice: enforce timeouts for servers you create! + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + log.Fatal(srv.ListenAndServe()) + } + +Now let's see how to build registered URLs. + +Routes can be named. All routes that define a name can have their URLs built, +or "reversed". We define a name calling Name() on a route. For example: + + r := mux.NewRouter() + r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). + Name("article") + +To build a URL, get the route and call the URL() method, passing a sequence of +key/value pairs for the route variables. For the previous route, we would do: + + url, err := r.Get("article").URL("category", "technology", "id", "42") + +...and the result will be a url.URL with the following path: + + "/articles/technology/42" + +This also works for host and query value variables: + + r := mux.NewRouter() + r.Host("{subdomain}.domain.com"). + Path("/articles/{category}/{id:[0-9]+}"). + Queries("filter", "{filter}"). + HandlerFunc(ArticleHandler). + Name("article") + + // url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla" + url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42", + "filter", "gorilla") + +All variables defined in the route are required, and their values must +conform to the corresponding patterns. These requirements guarantee that a +generated URL will always match a registered route -- the only exception is +for explicitly defined "build-only" routes which never match. + +Regex support also exists for matching Headers within a route. For example, we could do: + + r.HeadersRegexp("Content-Type", "application/(text|json)") + +...and the route will match both requests with a Content-Type of `application/json` as well as +`application/text` + +There's also a way to build only the URL host or path for a route: +use the methods URLHost() or URLPath() instead. For the previous route, +we would do: + + // "http://news.domain.com/" + host, err := r.Get("article").URLHost("subdomain", "news") + + // "/articles/technology/42" + path, err := r.Get("article").URLPath("category", "technology", "id", "42") + +And if you use subrouters, host and path defined separately can be built +as well: + + r := mux.NewRouter() + s := r.Host("{subdomain}.domain.com").Subrouter() + s.Path("/articles/{category}/{id:[0-9]+}"). + HandlerFunc(ArticleHandler). + Name("article") + + // "http://news.domain.com/articles/technology/42" + url, err := r.Get("article").URL("subdomain", "news", + "category", "technology", + "id", "42") + +Since **vX.Y.Z**, mux supports the addition of middlewares to a [Router](https://godoc.org/github.com/gorilla/mux#Router), which are executed if a +match is found (including subrouters). Middlewares are defined using the de facto standard type: + + type MiddlewareFunc func(http.Handler) http.Handler + +Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc (closures can access variables from the context where they are created). + +A very basic middleware which logs the URI of the request being handled could be written as: + + func simpleMw(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Do stuff here + log.Println(r.RequestURI) + // Call the next handler, which can be another middleware in the chain, or the final handler. + next.ServeHTTP(w, r) + }) + } + +Middlewares can be added to a router using `Router.Use()`: + + r := mux.NewRouter() + r.HandleFunc("/", handler) + r.AddMiddleware(simpleMw) + +A more complex authentication middleware, which maps session token to users, could be written as: + + // Define our struct + type authenticationMiddleware struct { + tokenUsers map[string]string + } + + // Initialize it somewhere + func (amw *authenticationMiddleware) Populate() { + amw.tokenUsers["00000000"] = "user0" + amw.tokenUsers["aaaaaaaa"] = "userA" + amw.tokenUsers["05f717e5"] = "randomUser" + amw.tokenUsers["deadbeef"] = "user0" + } + + // Middleware function, which will be called for each request + func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("X-Session-Token") + + if user, found := amw.tokenUsers[token]; found { + // We found the token in our map + log.Printf("Authenticated user %s\n", user) + next.ServeHTTP(w, r) + } else { + http.Error(w, "Forbidden", 403) + } + }) + } + + r := mux.NewRouter() + r.HandleFunc("/", handler) + + amw := authenticationMiddleware{} + amw.Populate() + + r.Use(amw.Middleware) + +Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. + +*/ +package mux diff --git a/vendor/github.com/gorilla/mux/example_route_test.go b/vendor/github.com/gorilla/mux/example_route_test.go new file mode 100644 index 00000000..11255707 --- /dev/null +++ b/vendor/github.com/gorilla/mux/example_route_test.go @@ -0,0 +1,51 @@ +package mux_test + +import ( + "fmt" + "net/http" + + "github.com/gorilla/mux" +) + +// This example demonstrates setting a regular expression matcher for +// the header value. A plain word will match any value that contains a +// matching substring as if the pattern was wrapped with `.*`. +func ExampleRoute_HeadersRegexp() { + r := mux.NewRouter() + route := r.NewRoute().HeadersRegexp("Accept", "html") + + req1, _ := http.NewRequest("GET", "example.com", nil) + req1.Header.Add("Accept", "text/plain") + req1.Header.Add("Accept", "text/html") + + req2, _ := http.NewRequest("GET", "example.com", nil) + req2.Header.Set("Accept", "application/xhtml+xml") + + matchInfo := &mux.RouteMatch{} + fmt.Printf("Match: %v %q\n", route.Match(req1, matchInfo), req1.Header["Accept"]) + fmt.Printf("Match: %v %q\n", route.Match(req2, matchInfo), req2.Header["Accept"]) + // Output: + // Match: true ["text/plain" "text/html"] + // Match: true ["application/xhtml+xml"] +} + +// This example demonstrates setting a strict regular expression matcher +// for the header value. Using the start and end of string anchors, the +// value must be an exact match. +func ExampleRoute_HeadersRegexp_exactMatch() { + r := mux.NewRouter() + route := r.NewRoute().HeadersRegexp("Origin", "^https://example.co$") + + yes, _ := http.NewRequest("GET", "example.co", nil) + yes.Header.Set("Origin", "https://example.co") + + no, _ := http.NewRequest("GET", "example.co.uk", nil) + no.Header.Set("Origin", "https://example.co.uk") + + matchInfo := &mux.RouteMatch{} + fmt.Printf("Match: %v %q\n", route.Match(yes, matchInfo), yes.Header["Origin"]) + fmt.Printf("Match: %v %q\n", route.Match(no, matchInfo), no.Header["Origin"]) + // Output: + // Match: true ["https://example.co"] + // Match: false ["https://example.co.uk"] +} diff --git a/vendor/github.com/gorilla/mux/middleware.go b/vendor/github.com/gorilla/mux/middleware.go new file mode 100644 index 00000000..8f898675 --- /dev/null +++ b/vendor/github.com/gorilla/mux/middleware.go @@ -0,0 +1,28 @@ +package mux + +import "net/http" + +// MiddlewareFunc is a function which receives an http.Handler and returns another http.Handler. +// Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed +// to it, and then calls the handler passed as parameter to the MiddlewareFunc. +type MiddlewareFunc func(http.Handler) http.Handler + +// middleware interface is anything which implements a MiddlewareFunc named Middleware. +type middleware interface { + Middleware(handler http.Handler) http.Handler +} + +// MiddlewareFunc also implements the middleware interface. +func (mw MiddlewareFunc) Middleware(handler http.Handler) http.Handler { + return mw(handler) +} + +// Use appends a MiddlewareFunc to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. +func (r *Router) Use(mwf MiddlewareFunc) { + r.middlewares = append(r.middlewares, mwf) +} + +// useInterface appends a middleware to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. +func (r *Router) useInterface(mw middleware) { + r.middlewares = append(r.middlewares, mw) +} diff --git a/vendor/github.com/gorilla/mux/middleware_test.go b/vendor/github.com/gorilla/mux/middleware_test.go new file mode 100644 index 00000000..93947e8c --- /dev/null +++ b/vendor/github.com/gorilla/mux/middleware_test.go @@ -0,0 +1,336 @@ +package mux + +import ( + "bytes" + "net/http" + "testing" +) + +type testMiddleware struct { + timesCalled uint +} + +func (tm *testMiddleware) Middleware(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + tm.timesCalled++ + h.ServeHTTP(w, r) + }) +} + +func dummyHandler(w http.ResponseWriter, r *http.Request) {} + +func TestMiddlewareAdd(t *testing.T) { + router := NewRouter() + router.HandleFunc("/", dummyHandler).Methods("GET") + + mw := &testMiddleware{} + + router.useInterface(mw) + if len(router.middlewares) != 1 || router.middlewares[0] != mw { + t.Fatal("Middleware was not added correctly") + } + + router.Use(mw.Middleware) + if len(router.middlewares) != 2 { + t.Fatal("MiddlewareFunc method was not added correctly") + } + + banalMw := func(handler http.Handler) http.Handler { + return handler + } + router.Use(banalMw) + if len(router.middlewares) != 3 { + t.Fatal("MiddlewareFunc method was not added correctly") + } +} + +func TestMiddleware(t *testing.T) { + router := NewRouter() + router.HandleFunc("/", dummyHandler).Methods("GET") + + mw := &testMiddleware{} + router.useInterface(mw) + + rw := NewRecorder() + req := newRequest("GET", "/") + + // Test regular middleware call + router.ServeHTTP(rw, req) + if mw.timesCalled != 1 { + t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled) + } + + // Middleware should not be called for 404 + req = newRequest("GET", "/not/found") + router.ServeHTTP(rw, req) + if mw.timesCalled != 1 { + t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled) + } + + // Middleware should not be called if there is a method mismatch + req = newRequest("POST", "/") + router.ServeHTTP(rw, req) + if mw.timesCalled != 1 { + t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled) + } + + // Add the middleware again as function + router.Use(mw.Middleware) + req = newRequest("GET", "/") + router.ServeHTTP(rw, req) + if mw.timesCalled != 3 { + t.Fatalf("Expected %d calls, but got only %d", 3, mw.timesCalled) + } + +} + +func TestMiddlewareSubrouter(t *testing.T) { + router := NewRouter() + router.HandleFunc("/", dummyHandler).Methods("GET") + + subrouter := router.PathPrefix("/sub").Subrouter() + subrouter.HandleFunc("/x", dummyHandler).Methods("GET") + + mw := &testMiddleware{} + subrouter.useInterface(mw) + + rw := NewRecorder() + req := newRequest("GET", "/") + + router.ServeHTTP(rw, req) + if mw.timesCalled != 0 { + t.Fatalf("Expected %d calls, but got only %d", 0, mw.timesCalled) + } + + req = newRequest("GET", "/sub/") + router.ServeHTTP(rw, req) + if mw.timesCalled != 0 { + t.Fatalf("Expected %d calls, but got only %d", 0, mw.timesCalled) + } + + req = newRequest("GET", "/sub/x") + router.ServeHTTP(rw, req) + if mw.timesCalled != 1 { + t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled) + } + + req = newRequest("GET", "/sub/not/found") + router.ServeHTTP(rw, req) + if mw.timesCalled != 1 { + t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled) + } + + router.useInterface(mw) + + req = newRequest("GET", "/") + router.ServeHTTP(rw, req) + if mw.timesCalled != 2 { + t.Fatalf("Expected %d calls, but got only %d", 2, mw.timesCalled) + } + + req = newRequest("GET", "/sub/x") + router.ServeHTTP(rw, req) + if mw.timesCalled != 4 { + t.Fatalf("Expected %d calls, but got only %d", 4, mw.timesCalled) + } +} + +func TestMiddlewareExecution(t *testing.T) { + mwStr := []byte("Middleware\n") + handlerStr := []byte("Logic\n") + + router := NewRouter() + router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { + w.Write(handlerStr) + }) + + rw := NewRecorder() + req := newRequest("GET", "/") + + // Test handler-only call + router.ServeHTTP(rw, req) + + if bytes.Compare(rw.Body.Bytes(), handlerStr) != 0 { + t.Fatal("Handler response is not what it should be") + } + + // Test middleware call + rw = NewRecorder() + + router.Use(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(mwStr) + h.ServeHTTP(w, r) + }) + }) + + router.ServeHTTP(rw, req) + if bytes.Compare(rw.Body.Bytes(), append(mwStr, handlerStr...)) != 0 { + t.Fatal("Middleware + handler response is not what it should be") + } +} + +func TestMiddlewareNotFound(t *testing.T) { + mwStr := []byte("Middleware\n") + handlerStr := []byte("Logic\n") + + router := NewRouter() + router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { + w.Write(handlerStr) + }) + router.Use(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(mwStr) + h.ServeHTTP(w, r) + }) + }) + + // Test not found call with default handler + rw := NewRecorder() + req := newRequest("GET", "/notfound") + + router.ServeHTTP(rw, req) + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a 404") + } + + // Test not found call with custom handler + rw = NewRecorder() + req = newRequest("GET", "/notfound") + + router.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Write([]byte("Custom 404 handler")) + }) + router.ServeHTTP(rw, req) + + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a custom 404") + } +} + +func TestMiddlewareMethodMismatch(t *testing.T) { + mwStr := []byte("Middleware\n") + handlerStr := []byte("Logic\n") + + router := NewRouter() + router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { + w.Write(handlerStr) + }).Methods("GET") + + router.Use(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(mwStr) + h.ServeHTTP(w, r) + }) + }) + + // Test method mismatch + rw := NewRecorder() + req := newRequest("POST", "/") + + router.ServeHTTP(rw, req) + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a method mismatch") + } + + // Test not found call + rw = NewRecorder() + req = newRequest("POST", "/") + + router.MethodNotAllowedHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Write([]byte("Method not allowed")) + }) + router.ServeHTTP(rw, req) + + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a method mismatch") + } +} + +func TestMiddlewareNotFoundSubrouter(t *testing.T) { + mwStr := []byte("Middleware\n") + handlerStr := []byte("Logic\n") + + router := NewRouter() + router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { + w.Write(handlerStr) + }) + + subrouter := router.PathPrefix("/sub/").Subrouter() + subrouter.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { + w.Write(handlerStr) + }) + + router.Use(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(mwStr) + h.ServeHTTP(w, r) + }) + }) + + // Test not found call for default handler + rw := NewRecorder() + req := newRequest("GET", "/sub/notfound") + + router.ServeHTTP(rw, req) + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a 404") + } + + // Test not found call with custom handler + rw = NewRecorder() + req = newRequest("GET", "/sub/notfound") + + subrouter.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Write([]byte("Custom 404 handler")) + }) + router.ServeHTTP(rw, req) + + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a custom 404") + } +} + +func TestMiddlewareMethodMismatchSubrouter(t *testing.T) { + mwStr := []byte("Middleware\n") + handlerStr := []byte("Logic\n") + + router := NewRouter() + router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { + w.Write(handlerStr) + }) + + subrouter := router.PathPrefix("/sub/").Subrouter() + subrouter.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { + w.Write(handlerStr) + }).Methods("GET") + + router.Use(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(mwStr) + h.ServeHTTP(w, r) + }) + }) + + // Test method mismatch without custom handler + rw := NewRecorder() + req := newRequest("POST", "/sub/") + + router.ServeHTTP(rw, req) + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a method mismatch") + } + + // Test method mismatch with custom handler + rw = NewRecorder() + req = newRequest("POST", "/sub/") + + router.MethodNotAllowedHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Write([]byte("Method not allowed")) + }) + router.ServeHTTP(rw, req) + + if bytes.Contains(rw.Body.Bytes(), mwStr) { + t.Fatal("Middleware was called for a method mismatch") + } +} diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go new file mode 100644 index 00000000..efabd241 --- /dev/null +++ b/vendor/github.com/gorilla/mux/mux.go @@ -0,0 +1,585 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "errors" + "fmt" + "net/http" + "path" + "regexp" +) + +var ( + ErrMethodMismatch = errors.New("method is not allowed") + ErrNotFound = errors.New("no matching route was found") +) + +// NewRouter returns a new router instance. +func NewRouter() *Router { + return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} +} + +// Router registers routes to be matched and dispatches a handler. +// +// It implements the http.Handler interface, so it can be registered to serve +// requests: +// +// var router = mux.NewRouter() +// +// func main() { +// http.Handle("/", router) +// } +// +// Or, for Google App Engine, register it in a init() function: +// +// func init() { +// http.Handle("/", router) +// } +// +// This will send all incoming requests to the router. +type Router struct { + // Configurable Handler to be used when no route matches. + NotFoundHandler http.Handler + + // Configurable Handler to be used when the request method does not match the route. + MethodNotAllowedHandler http.Handler + + // Parent route, if this is a subrouter. + parent parentRoute + // Routes to be matched, in order. + routes []*Route + // Routes by name for URL building. + namedRoutes map[string]*Route + // See Router.StrictSlash(). This defines the flag for new routes. + strictSlash bool + // See Router.SkipClean(). This defines the flag for new routes. + skipClean bool + // If true, do not clear the request context after handling the request. + // This has no effect when go1.7+ is used, since the context is stored + // on the request itself. + KeepContext bool + // see Router.UseEncodedPath(). This defines a flag for all routes. + useEncodedPath bool + // Slice of middlewares to be called after a match is found + middlewares []middleware +} + +// Match attempts to match the given request against the router's registered routes. +// +// If the request matches a route of this router or one of its subrouters the Route, +// Handler, and Vars fields of the the match argument are filled and this function +// returns true. +// +// If the request does not match any of this router's or its subrouters' routes +// then this function returns false. If available, a reason for the match failure +// will be filled in the match argument's MatchErr field. If the match failure type +// (eg: not found) has a registered handler, the handler is assigned to the Handler +// field of the match argument. +func (r *Router) Match(req *http.Request, match *RouteMatch) bool { + for _, route := range r.routes { + if route.Match(req, match) { + // Build middleware chain if no error was found + if match.MatchErr == nil { + for i := len(r.middlewares) - 1; i >= 0; i-- { + match.Handler = r.middlewares[i].Middleware(match.Handler) + } + } + return true + } + } + + if match.MatchErr == ErrMethodMismatch { + if r.MethodNotAllowedHandler != nil { + match.Handler = r.MethodNotAllowedHandler + return true + } else { + return false + } + } + + // Closest match for a router (includes sub-routers) + if r.NotFoundHandler != nil { + match.Handler = r.NotFoundHandler + match.MatchErr = ErrNotFound + return true + } + + match.MatchErr = ErrNotFound + return false +} + +// ServeHTTP dispatches the handler registered in the matched route. +// +// When there is a match, the route variables can be retrieved calling +// mux.Vars(request). +func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if !r.skipClean { + path := req.URL.Path + if r.useEncodedPath { + path = req.URL.EscapedPath() + } + // Clean path to canonical form and redirect. + if p := cleanPath(path); p != path { + + // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. + // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: + // http://code.google.com/p/go/issues/detail?id=5252 + url := *req.URL + url.Path = p + p = url.String() + + w.Header().Set("Location", p) + w.WriteHeader(http.StatusMovedPermanently) + return + } + } + var match RouteMatch + var handler http.Handler + if r.Match(req, &match) { + handler = match.Handler + req = setVars(req, match.Vars) + req = setCurrentRoute(req, match.Route) + } + + if handler == nil && match.MatchErr == ErrMethodMismatch { + handler = methodNotAllowedHandler() + } + + if handler == nil { + handler = http.NotFoundHandler() + } + + if !r.KeepContext { + defer contextClear(req) + } + + handler.ServeHTTP(w, req) +} + +// Get returns a route registered with the given name. +func (r *Router) Get(name string) *Route { + return r.getNamedRoutes()[name] +} + +// GetRoute returns a route registered with the given name. This method +// was renamed to Get() and remains here for backwards compatibility. +func (r *Router) GetRoute(name string) *Route { + return r.getNamedRoutes()[name] +} + +// StrictSlash defines the trailing slash behavior for new routes. The initial +// value is false. +// +// When true, if the route path is "/path/", accessing "/path" will perform a redirect +// to the former and vice versa. In other words, your application will always +// see the path as specified in the route. +// +// When false, if the route path is "/path", accessing "/path/" will not match +// this route and vice versa. +// +// The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for +// routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed +// request will be made as a GET by most clients. Use middleware or client settings +// to modify this behaviour as needed. +// +// Special case: when a route sets a path prefix using the PathPrefix() method, +// strict slash is ignored for that route because the redirect behavior can't +// be determined from a prefix alone. However, any subrouters created from that +// route inherit the original StrictSlash setting. +func (r *Router) StrictSlash(value bool) *Router { + r.strictSlash = value + return r +} + +// SkipClean defines the path cleaning behaviour for new routes. The initial +// value is false. Users should be careful about which routes are not cleaned +// +// When true, if the route path is "/path//to", it will remain with the double +// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/ +// +// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will +// become /fetch/http/xkcd.com/534 +func (r *Router) SkipClean(value bool) *Router { + r.skipClean = value + return r +} + +// UseEncodedPath tells the router to match the encoded original path +// to the routes. +// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to". +// +// If not called, the router will match the unencoded path to the routes. +// For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to" +func (r *Router) UseEncodedPath() *Router { + r.useEncodedPath = true + return r +} + +// ---------------------------------------------------------------------------- +// parentRoute +// ---------------------------------------------------------------------------- + +func (r *Router) getBuildScheme() string { + if r.parent != nil { + return r.parent.getBuildScheme() + } + return "" +} + +// getNamedRoutes returns the map where named routes are registered. +func (r *Router) getNamedRoutes() map[string]*Route { + if r.namedRoutes == nil { + if r.parent != nil { + r.namedRoutes = r.parent.getNamedRoutes() + } else { + r.namedRoutes = make(map[string]*Route) + } + } + return r.namedRoutes +} + +// getRegexpGroup returns regexp definitions from the parent route, if any. +func (r *Router) getRegexpGroup() *routeRegexpGroup { + if r.parent != nil { + return r.parent.getRegexpGroup() + } + return nil +} + +func (r *Router) buildVars(m map[string]string) map[string]string { + if r.parent != nil { + m = r.parent.buildVars(m) + } + return m +} + +// ---------------------------------------------------------------------------- +// Route factories +// ---------------------------------------------------------------------------- + +// NewRoute registers an empty route. +func (r *Router) NewRoute() *Route { + route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath} + r.routes = append(r.routes, route) + return route +} + +// Handle registers a new route with a matcher for the URL path. +// See Route.Path() and Route.Handler(). +func (r *Router) Handle(path string, handler http.Handler) *Route { + return r.NewRoute().Path(path).Handler(handler) +} + +// HandleFunc registers a new route with a matcher for the URL path. +// See Route.Path() and Route.HandlerFunc(). +func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, + *http.Request)) *Route { + return r.NewRoute().Path(path).HandlerFunc(f) +} + +// Headers registers a new route with a matcher for request header values. +// See Route.Headers(). +func (r *Router) Headers(pairs ...string) *Route { + return r.NewRoute().Headers(pairs...) +} + +// Host registers a new route with a matcher for the URL host. +// See Route.Host(). +func (r *Router) Host(tpl string) *Route { + return r.NewRoute().Host(tpl) +} + +// MatcherFunc registers a new route with a custom matcher function. +// See Route.MatcherFunc(). +func (r *Router) MatcherFunc(f MatcherFunc) *Route { + return r.NewRoute().MatcherFunc(f) +} + +// Methods registers a new route with a matcher for HTTP methods. +// See Route.Methods(). +func (r *Router) Methods(methods ...string) *Route { + return r.NewRoute().Methods(methods...) +} + +// Path registers a new route with a matcher for the URL path. +// See Route.Path(). +func (r *Router) Path(tpl string) *Route { + return r.NewRoute().Path(tpl) +} + +// PathPrefix registers a new route with a matcher for the URL path prefix. +// See Route.PathPrefix(). +func (r *Router) PathPrefix(tpl string) *Route { + return r.NewRoute().PathPrefix(tpl) +} + +// Queries registers a new route with a matcher for URL query values. +// See Route.Queries(). +func (r *Router) Queries(pairs ...string) *Route { + return r.NewRoute().Queries(pairs...) +} + +// Schemes registers a new route with a matcher for URL schemes. +// See Route.Schemes(). +func (r *Router) Schemes(schemes ...string) *Route { + return r.NewRoute().Schemes(schemes...) +} + +// BuildVarsFunc registers a new route with a custom function for modifying +// route variables before building a URL. +func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { + return r.NewRoute().BuildVarsFunc(f) +} + +// Walk walks the router and all its sub-routers, calling walkFn for each route +// in the tree. The routes are walked in the order they were added. Sub-routers +// are explored depth-first. +func (r *Router) Walk(walkFn WalkFunc) error { + return r.walk(walkFn, []*Route{}) +} + +// SkipRouter is used as a return value from WalkFuncs to indicate that the +// router that walk is about to descend down to should be skipped. +var SkipRouter = errors.New("skip this router") + +// WalkFunc is the type of the function called for each route visited by Walk. +// At every invocation, it is given the current route, and the current router, +// and a list of ancestor routes that lead to the current route. +type WalkFunc func(route *Route, router *Router, ancestors []*Route) error + +func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { + for _, t := range r.routes { + err := walkFn(t, r, ancestors) + if err == SkipRouter { + continue + } + if err != nil { + return err + } + for _, sr := range t.matchers { + if h, ok := sr.(*Router); ok { + ancestors = append(ancestors, t) + err := h.walk(walkFn, ancestors) + if err != nil { + return err + } + ancestors = ancestors[:len(ancestors)-1] + } + } + if h, ok := t.handler.(*Router); ok { + ancestors = append(ancestors, t) + err := h.walk(walkFn, ancestors) + if err != nil { + return err + } + ancestors = ancestors[:len(ancestors)-1] + } + } + return nil +} + +// ---------------------------------------------------------------------------- +// Context +// ---------------------------------------------------------------------------- + +// RouteMatch stores information about a matched route. +type RouteMatch struct { + Route *Route + Handler http.Handler + Vars map[string]string + + // MatchErr is set to appropriate matching error + // It is set to ErrMethodMismatch if there is a mismatch in + // the request method and route method + MatchErr error +} + +type contextKey int + +const ( + varsKey contextKey = iota + routeKey +) + +// Vars returns the route variables for the current request, if any. +func Vars(r *http.Request) map[string]string { + if rv := contextGet(r, varsKey); rv != nil { + return rv.(map[string]string) + } + return nil +} + +// CurrentRoute returns the matched route for the current request, if any. +// This only works when called inside the handler of the matched route +// because the matched route is stored in the request context which is cleared +// after the handler returns, unless the KeepContext option is set on the +// Router. +func CurrentRoute(r *http.Request) *Route { + if rv := contextGet(r, routeKey); rv != nil { + return rv.(*Route) + } + return nil +} + +func setVars(r *http.Request, val interface{}) *http.Request { + return contextSet(r, varsKey, val) +} + +func setCurrentRoute(r *http.Request, val interface{}) *http.Request { + return contextSet(r, routeKey, val) +} + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +// cleanPath returns the canonical path for p, eliminating . and .. elements. +// Borrowed from the net/http package. +func cleanPath(p string) string { + if p == "" { + return "/" + } + if p[0] != '/' { + p = "/" + p + } + np := path.Clean(p) + // path.Clean removes trailing slash except for root; + // put the trailing slash back if necessary. + if p[len(p)-1] == '/' && np != "/" { + np += "/" + } + + return np +} + +// uniqueVars returns an error if two slices contain duplicated strings. +func uniqueVars(s1, s2 []string) error { + for _, v1 := range s1 { + for _, v2 := range s2 { + if v1 == v2 { + return fmt.Errorf("mux: duplicated route variable %q", v2) + } + } + } + return nil +} + +// checkPairs returns the count of strings passed in, and an error if +// the count is not an even number. +func checkPairs(pairs ...string) (int, error) { + length := len(pairs) + if length%2 != 0 { + return length, fmt.Errorf( + "mux: number of parameters must be multiple of 2, got %v", pairs) + } + return length, nil +} + +// mapFromPairsToString converts variadic string parameters to a +// string to string map. +func mapFromPairsToString(pairs ...string) (map[string]string, error) { + length, err := checkPairs(pairs...) + if err != nil { + return nil, err + } + m := make(map[string]string, length/2) + for i := 0; i < length; i += 2 { + m[pairs[i]] = pairs[i+1] + } + return m, nil +} + +// mapFromPairsToRegex converts variadic string parameters to a +// string to regex map. +func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { + length, err := checkPairs(pairs...) + if err != nil { + return nil, err + } + m := make(map[string]*regexp.Regexp, length/2) + for i := 0; i < length; i += 2 { + regex, err := regexp.Compile(pairs[i+1]) + if err != nil { + return nil, err + } + m[pairs[i]] = regex + } + return m, nil +} + +// matchInArray returns true if the given string value is in the array. +func matchInArray(arr []string, value string) bool { + for _, v := range arr { + if v == value { + return true + } + } + return false +} + +// matchMapWithString returns true if the given key/value pairs exist in a given map. +func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { + for k, v := range toCheck { + // Check if key exists. + if canonicalKey { + k = http.CanonicalHeaderKey(k) + } + if values := toMatch[k]; values == nil { + return false + } else if v != "" { + // If value was defined as an empty string we only check that the + // key exists. Otherwise we also check for equality. + valueExists := false + for _, value := range values { + if v == value { + valueExists = true + break + } + } + if !valueExists { + return false + } + } + } + return true +} + +// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against +// the given regex +func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { + for k, v := range toCheck { + // Check if key exists. + if canonicalKey { + k = http.CanonicalHeaderKey(k) + } + if values := toMatch[k]; values == nil { + return false + } else if v != nil { + // If value was defined as an empty string we only check that the + // key exists. Otherwise we also check for equality. + valueExists := false + for _, value := range values { + if v.MatchString(value) { + valueExists = true + break + } + } + if !valueExists { + return false + } + } + } + return true +} + +// methodNotAllowed replies to the request with an HTTP status code 405. +func methodNotAllowed(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusMethodNotAllowed) +} + +// methodNotAllowedHandler returns a simple request handler +// that replies to each request with a status code 405. +func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) } diff --git a/vendor/github.com/gorilla/mux/mux_test.go b/vendor/github.com/gorilla/mux/mux_test.go new file mode 100644 index 00000000..9e93c983 --- /dev/null +++ b/vendor/github.com/gorilla/mux/mux_test.go @@ -0,0 +1,2347 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "net/http" + "net/url" + "reflect" + "strings" + "testing" +) + +func (r *Route) GoString() string { + matchers := make([]string, len(r.matchers)) + for i, m := range r.matchers { + matchers[i] = fmt.Sprintf("%#v", m) + } + return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", ")) +} + +func (r *routeRegexp) GoString() string { + return fmt.Sprintf("&routeRegexp{template: %q, regexpType: %v, options: %v, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.regexpType, r.options, r.regexp.String(), r.reverse, r.varsN, r.varsR) +} + +type routeTest struct { + title string // title of the test + route *Route // the route being tested + request *http.Request // a request to test the route + vars map[string]string // the expected vars of the match + scheme string // the expected scheme of the built URL + host string // the expected host of the built URL + path string // the expected path of the built URL + query string // the expected query string of the built URL + pathTemplate string // the expected path template of the route + hostTemplate string // the expected host template of the route + queriesTemplate string // the expected query template of the route + methods []string // the expected route methods + pathRegexp string // the expected path regexp + queriesRegexp string // the expected query regexp + shouldMatch bool // whether the request is expected to match the route at all + shouldRedirect bool // whether the request should result in a redirect +} + +func TestHost(t *testing.T) { + // newRequestHost a new request with a method, url, and host header + newRequestHost := func(method, url, host string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + req.Host = host + return req + } + + tests := []routeTest{ + { + title: "Host route match", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: true, + }, + { + title: "Host route, wrong host in request URL", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: false, + }, + { + title: "Host route with port, match", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: true, + }, + { + title: "Host route with port, wrong port in request URL", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: false, + }, + { + title: "Host route, match with host in request header", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: true, + }, + { + title: "Host route, wrong host in request header", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: false, + }, + // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true}, + { + title: "Host route with port, wrong host in request header", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: false, + }, + { + title: "Host route with pattern, match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with pattern, additional capturing group, match", + route: new(Route).Host("aaa.{v1:[a-z]{2}(?:b|c)}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `aaa.{v1:[a-z]{2}(?:b|c)}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with pattern, wrong host in request URL", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: false, + }, + { + title: "Host route with multiple patterns, match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Host route with multiple patterns, wrong host in request URL", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: false, + }, + { + title: "Host route with hyphenated name and pattern, match", + route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `aaa.{v-1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with hyphenated name and pattern, additional capturing group, match", + route: new(Route).Host("aaa.{v-1:[a-z]{2}(?:b|c)}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `aaa.{v-1:[a-z]{2}(?:b|c)}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with multiple hyphenated names and patterns, match", + route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + hostTemplate: `{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}`, + shouldMatch: true, + }, + } + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestPath(t *testing.T) { + tests := []routeTest{ + { + title: "Path route, match", + route: new(Route).Path("/111/222/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/111/222/333", + shouldMatch: true, + }, + { + title: "Path route, match with trailing slash in request and path", + route: new(Route).Path("/111/"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + }, + { + title: "Path route, do not match with trailing slash in path", + route: new(Route).Path("/111/"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111", + pathTemplate: `/111/`, + pathRegexp: `^/111/$`, + shouldMatch: false, + }, + { + title: "Path route, do not match with trailing slash in request", + route: new(Route).Path("/111"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + pathTemplate: `/111`, + shouldMatch: false, + }, + { + title: "Path route, match root with no host", + route: new(Route).Path("/"), + request: newRequest("GET", "/"), + vars: map[string]string{}, + host: "", + path: "/", + pathTemplate: `/`, + pathRegexp: `^/$`, + shouldMatch: true, + }, + { + title: "Path route, match root with no host, App Engine format", + route: new(Route).Path("/"), + request: func() *http.Request { + r := newRequest("GET", "http://localhost/") + r.RequestURI = "/" + return r + }(), + vars: map[string]string{}, + host: "", + path: "/", + pathTemplate: `/`, + shouldMatch: true, + }, + { + title: "Path route, wrong path in request in request URL", + route: new(Route).Path("/111/222/333"), + request: newRequest("GET", "http://localhost/1/2/3"), + vars: map[string]string{}, + host: "", + path: "/111/222/333", + shouldMatch: false, + }, + { + title: "Path route with pattern, match", + route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222/333", + pathTemplate: `/111/{v1:[0-9]{3}}/333`, + shouldMatch: true, + }, + { + title: "Path route with pattern, URL in request does not match", + route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222/333", + pathTemplate: `/111/{v1:[0-9]{3}}/333`, + pathRegexp: `^/111/(?P[0-9]{3})/333$`, + shouldMatch: false, + }, + { + title: "Path route with multiple patterns, match", + route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, + host: "", + path: "/111/222/333", + pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`, + pathRegexp: `^/(?P[0-9]{3})/(?P[0-9]{3})/(?P[0-9]{3})$`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns, URL in request does not match", + route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, + host: "", + path: "/111/222/333", + pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`, + pathRegexp: `^/(?P[0-9]{3})/(?P[0-9]{3})/(?P[0-9]{3})$`, + shouldMatch: false, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|(?:b/c)}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, + host: "", + path: "/a/product_name/1", + pathTemplate: `/{category:a|(?:b/c)}/{product}/{id:[0-9]+}`, + pathRegexp: `^/(?Pa|(?:b/c))/(?P[^/]+)/(?P[0-9]+)$`, + shouldMatch: true, + }, + { + title: "Path route with hyphenated name and pattern, match", + route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v-1": "222"}, + host: "", + path: "/111/222/333", + pathTemplate: `/111/{v-1:[0-9]{3}}/333`, + pathRegexp: `^/111/(?P[0-9]{3})/333$`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns, match", + route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"}, + host: "", + path: "/111/222/333", + pathTemplate: `/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}`, + pathRegexp: `^/(?P[0-9]{3})/(?P[0-9]{3})/(?P[0-9]{3})$`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns with pipe, match", + route: new(Route).Path("/{product-category:a|(?:b/c)}/{product-name}/{product-id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"}, + host: "", + path: "/a/product_name/1", + pathTemplate: `/{product-category:a|(?:b/c)}/{product-name}/{product-id:[0-9]+}`, + pathRegexp: `^/(?Pa|(?:b/c))/(?P[^/]+)/(?P[0-9]+)$`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns with pipe and case insensitive, match", + route: new(Route).Path("/{type:(?i:daily|mini|variety)}-{date:\\d{4,4}-\\d{2,2}-\\d{2,2}}"), + request: newRequest("GET", "http://localhost/daily-2016-01-01"), + vars: map[string]string{"type": "daily", "date": "2016-01-01"}, + host: "", + path: "/daily-2016-01-01", + pathTemplate: `/{type:(?i:daily|mini|variety)}-{date:\d{4,4}-\d{2,2}-\d{2,2}}`, + pathRegexp: `^/(?P(?i:daily|mini|variety))-(?P\d{4,4}-\d{2,2}-\d{2,2})$`, + shouldMatch: true, + }, + { + title: "Path route with empty match right after other match", + route: new(Route).Path(`/{v1:[0-9]*}{v2:[a-z]*}/{v3:[0-9]*}`), + request: newRequest("GET", "http://localhost/111/222"), + vars: map[string]string{"v1": "111", "v2": "", "v3": "222"}, + host: "", + path: "/111/222", + pathTemplate: `/{v1:[0-9]*}{v2:[a-z]*}/{v3:[0-9]*}`, + pathRegexp: `^/(?P[0-9]*)(?P[a-z]*)/(?P[0-9]*)$`, + shouldMatch: true, + }, + { + title: "Path route with single pattern with pipe, match", + route: new(Route).Path("/{category:a|b/c}"), + request: newRequest("GET", "http://localhost/a"), + vars: map[string]string{"category": "a"}, + host: "", + path: "/a", + pathTemplate: `/{category:a|b/c}`, + shouldMatch: true, + }, + { + title: "Path route with single pattern with pipe, match", + route: new(Route).Path("/{category:a|b/c}"), + request: newRequest("GET", "http://localhost/b/c"), + vars: map[string]string{"category": "b/c"}, + host: "", + path: "/b/c", + pathTemplate: `/{category:a|b/c}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, + host: "", + path: "/a/product_name/1", + pathTemplate: `/{category:a|b/c}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/b/c/product_name/1"), + vars: map[string]string{"category": "b/c", "product": "product_name", "id": "1"}, + host: "", + path: "/b/c/product_name/1", + pathTemplate: `/{category:a|b/c}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + testUseEscapedRoute(t, test) + testRegexp(t, test) + } +} + +func TestPathPrefix(t *testing.T) { + tests := []routeTest{ + { + title: "PathPrefix route, match", + route: new(Route).PathPrefix("/111"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + }, + { + title: "PathPrefix route, match substring", + route: new(Route).PathPrefix("/1"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/1", + shouldMatch: true, + }, + { + title: "PathPrefix route, URL prefix in request does not match", + route: new(Route).PathPrefix("/111"), + request: newRequest("GET", "http://localhost/1/2/3"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: false, + }, + { + title: "PathPrefix route with pattern, match", + route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222", + pathTemplate: `/111/{v1:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "PathPrefix route with pattern, URL prefix in request does not match", + route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222", + pathTemplate: `/111/{v1:[0-9]{3}}`, + shouldMatch: false, + }, + { + title: "PathPrefix route with multiple patterns, match", + route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "111", "v2": "222"}, + host: "", + path: "/111/222", + pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "PathPrefix route with multiple patterns, URL prefix in request does not match", + route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "111", "v2": "222"}, + host: "", + path: "/111/222", + pathTemplate: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + testUseEscapedRoute(t, test) + } +} + +func TestSchemeHostPath(t *testing.T) { + tests := []routeTest{ + { + title: "Host and Path route, match", + route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{}, + scheme: "http", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/111/222/333`, + hostTemplate: `aaa.bbb.ccc`, + shouldMatch: true, + }, + { + title: "Scheme, Host, and Path route, match", + route: new(Route).Schemes("https").Host("aaa.bbb.ccc").Path("/111/222/333"), + request: newRequest("GET", "https://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{}, + scheme: "https", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/111/222/333`, + hostTemplate: `aaa.bbb.ccc`, + shouldMatch: true, + }, + { + title: "Host and Path route, wrong host in request URL", + route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{}, + scheme: "http", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/111/222/333`, + hostTemplate: `aaa.bbb.ccc`, + shouldMatch: false, + }, + { + title: "Host and Path route with pattern, match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb", "v2": "222"}, + scheme: "http", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/111/{v2:[0-9]{3}}/333`, + hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Scheme, Host, and Path route with host and path patterns, match", + route: new(Route).Schemes("ftp", "ssss").Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), + request: newRequest("GET", "ssss://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb", "v2": "222"}, + scheme: "ftp", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/111/{v2:[0-9]{3}}/333`, + hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host and Path route with pattern, URL in request does not match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb", "v2": "222"}, + scheme: "http", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/111/{v2:[0-9]{3}}/333`, + hostTemplate: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: false, + }, + { + title: "Host and Path route with multiple patterns, match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, + scheme: "http", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`, + hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Host and Path route with multiple patterns, URL in request does not match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, + scheme: "http", + host: "aaa.bbb.ccc", + path: "/111/222/333", + pathTemplate: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`, + hostTemplate: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + testUseEscapedRoute(t, test) + } +} + +func TestHeaders(t *testing.T) { + // newRequestHeaders creates a new request with a method, url, and headers + newRequestHeaders := func(method, url string, headers map[string]string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + for k, v := range headers { + req.Header.Add(k, v) + } + return req + } + + tests := []routeTest{ + { + title: "Headers route, match", + route: new(Route).Headers("foo", "bar", "baz", "ding"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Headers route, bad header values", + route: new(Route).Headers("foo", "bar", "baz", "ding"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Headers route, regex header values to match", + route: new(Route).Headers("foo", "ba[zr]"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Headers route, regex header values to match", + route: new(Route).HeadersRegexp("foo", "ba[zr]"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestMethods(t *testing.T) { + tests := []routeTest{ + { + title: "Methods route, match GET", + route: new(Route).Methods("GET", "POST"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + methods: []string{"GET", "POST"}, + shouldMatch: true, + }, + { + title: "Methods route, match POST", + route: new(Route).Methods("GET", "POST"), + request: newRequest("POST", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + methods: []string{"GET", "POST"}, + shouldMatch: true, + }, + { + title: "Methods route, bad method", + route: new(Route).Methods("GET", "POST"), + request: newRequest("PUT", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + methods: []string{"GET", "POST"}, + shouldMatch: false, + }, + { + title: "Route without methods", + route: new(Route), + request: newRequest("PUT", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + methods: []string{}, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + testMethods(t, test) + } +} + +func TestQueries(t *testing.T) { + tests := []routeTest{ + { + title: "Queries route, match", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=bar&baz=ding", + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: true, + }, + { + title: "Queries route, match with a query string", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=bar&baz=ding", + pathTemplate: `/api`, + hostTemplate: `www.example.com`, + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: true, + }, + { + title: "Queries route, match with a query string out of order", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=bar&baz=ding", + pathTemplate: `/api`, + hostTemplate: `www.example.com`, + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: true, + }, + { + title: "Queries route, bad query", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=dong"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: false, + }, + { + title: "Queries route with pattern, match", + route: new(Route).Queries("foo", "{v1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v1": "bar"}, + host: "", + path: "", + query: "foo=bar", + queriesTemplate: "foo={v1}", + queriesRegexp: "^foo=(?P.*)$", + shouldMatch: true, + }, + { + title: "Queries route with multiple patterns, match", + route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "", + query: "foo=bar&baz=ding", + queriesTemplate: "foo={v1},baz={v2}", + queriesRegexp: "^foo=(?P.*)$,^baz=(?P.*)$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v1": "10"}, + host: "", + path: "", + query: "foo=10", + queriesTemplate: "foo={v1:[0-9]+}", + queriesRegexp: "^foo=(?P[0-9]+)$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=a"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={v1:[0-9]+}", + queriesRegexp: "^foo=(?P[0-9]+)$", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + query: "foo=1", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P[0-9]{1})$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?bar=2&foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + query: "foo=1", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P[0-9]{1})$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P[0-9]{1})$", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v1:[0-9]{1}(?:a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v1": "1a"}, + host: "", + path: "", + query: "foo=1a", + queriesTemplate: "foo={v1:[0-9]{1}(?:a|b)}", + queriesRegexp: "^foo=(?P[0-9]{1}(?:a|b))$", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={v1:[0-9]{1}}", + queriesRegexp: "^foo=(?P[0-9]{1})$", + shouldMatch: false, + }, + { + title: "Queries route with hyphenated name, match", + route: new(Route).Queries("foo", "{v-1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v-1": "bar"}, + host: "", + path: "", + query: "foo=bar", + queriesTemplate: "foo={v-1}", + queriesRegexp: "^foo=(?P.*)$", + shouldMatch: true, + }, + { + title: "Queries route with multiple hyphenated names, match", + route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v-1": "bar", "v-2": "ding"}, + host: "", + path: "", + query: "foo=bar&baz=ding", + queriesTemplate: "foo={v-1},baz={v-2}", + queriesRegexp: "^foo=(?P.*)$,^baz=(?P.*)$", + shouldMatch: true, + }, + { + title: "Queries route with hyphenate name and pattern, match", + route: new(Route).Queries("foo", "{v-1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v-1": "10"}, + host: "", + path: "", + query: "foo=10", + queriesTemplate: "foo={v-1:[0-9]+}", + queriesRegexp: "^foo=(?P[0-9]+)$", + shouldMatch: true, + }, + { + title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v-1:[0-9]{1}(?:a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v-1": "1a"}, + host: "", + path: "", + query: "foo=1a", + queriesTemplate: "foo={v-1:[0-9]{1}(?:a|b)}", + queriesRegexp: "^foo=(?P[0-9]{1}(?:a|b))$", + shouldMatch: true, + }, + { + title: "Queries route with empty value, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=", + queriesTemplate: "foo=", + queriesRegexp: "^foo=.*$", + shouldMatch: true, + }, + { + title: "Queries route with empty value and no parameter in request, should not match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=", + queriesRegexp: "^foo=.*$", + shouldMatch: false, + }, + { + title: "Queries route with empty value and empty parameter in request, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{}, + host: "", + path: "", + query: "foo=", + queriesTemplate: "foo=", + queriesRegexp: "^foo=.*$", + shouldMatch: true, + }, + { + title: "Queries route with overlapping value, should not match", + route: new(Route).Queries("foo", "bar"), + request: newRequest("GET", "http://localhost?foo=barfoo"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=bar", + queriesRegexp: "^foo=bar$", + shouldMatch: false, + }, + { + title: "Queries route with no parameter in request, should not match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo={bar}", + queriesRegexp: "^foo=(?P.*)$", + shouldMatch: false, + }, + { + title: "Queries route with empty parameter in request, should match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{"foo": ""}, + host: "", + path: "", + query: "foo=", + queriesTemplate: "foo={bar}", + queriesRegexp: "^foo=(?P.*)$", + shouldMatch: true, + }, + { + title: "Queries route, bad submatch", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"), + vars: map[string]string{}, + host: "", + path: "", + queriesTemplate: "foo=bar,baz=ding", + queriesRegexp: "^foo=bar$,^baz=ding$", + shouldMatch: false, + }, + { + title: "Queries route with pattern, match, escaped value", + route: new(Route).Queries("foo", "{v1}"), + request: newRequest("GET", "http://localhost?foo=%25bar%26%20%2F%3D%3F"), + vars: map[string]string{"v1": "%bar& /=?"}, + host: "", + path: "", + query: "foo=%25bar%26+%2F%3D%3F", + queriesTemplate: "foo={v1}", + queriesRegexp: "^foo=(?P.*)$", + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + testQueriesTemplates(t, test) + testUseEscapedRoute(t, test) + testQueriesRegexp(t, test) + } +} + +func TestSchemes(t *testing.T) { + tests := []routeTest{ + // Schemes + { + title: "Schemes route, default scheme, match http, build http", + route: new(Route).Host("localhost"), + request: newRequest("GET", "http://localhost"), + scheme: "http", + host: "localhost", + shouldMatch: true, + }, + { + title: "Schemes route, match https, build https", + route: new(Route).Schemes("https", "ftp").Host("localhost"), + request: newRequest("GET", "https://localhost"), + scheme: "https", + host: "localhost", + shouldMatch: true, + }, + { + title: "Schemes route, match ftp, build https", + route: new(Route).Schemes("https", "ftp").Host("localhost"), + request: newRequest("GET", "ftp://localhost"), + scheme: "https", + host: "localhost", + shouldMatch: true, + }, + { + title: "Schemes route, match ftp, build ftp", + route: new(Route).Schemes("ftp", "https").Host("localhost"), + request: newRequest("GET", "ftp://localhost"), + scheme: "ftp", + host: "localhost", + shouldMatch: true, + }, + { + title: "Schemes route, bad scheme", + route: new(Route).Schemes("https", "ftp").Host("localhost"), + request: newRequest("GET", "http://localhost"), + scheme: "https", + host: "localhost", + shouldMatch: false, + }, + } + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestMatcherFunc(t *testing.T) { + m := func(r *http.Request, m *RouteMatch) bool { + if r.URL.Host == "aaa.bbb.ccc" { + return true + } + return false + } + + tests := []routeTest{ + { + title: "MatchFunc route, match", + route: new(Route).MatcherFunc(m), + request: newRequest("GET", "http://aaa.bbb.ccc"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "MatchFunc route, non-match", + route: new(Route).MatcherFunc(m), + request: newRequest("GET", "http://aaa.222.ccc"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestBuildVarsFunc(t *testing.T) { + tests := []routeTest{ + { + title: "BuildVarsFunc set on route", + route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v1"] = "3" + vars["v2"] = "a" + return vars + }), + request: newRequest("GET", "http://localhost/111/2"), + path: "/111/3a", + pathTemplate: `/111/{v1:\d}{v2:.*}`, + shouldMatch: true, + }, + { + title: "BuildVarsFunc set on route and parent route", + route: new(Route).PathPrefix(`/{v1:\d}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v1"] = "2" + return vars + }).Subrouter().Path(`/{v2:\w}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v2"] = "b" + return vars + }), + request: newRequest("GET", "http://localhost/1/a"), + path: "/2/b", + pathTemplate: `/{v1:\d}/{v2:\w}`, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestSubRouter(t *testing.T) { + subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter() + subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter() + subrouter3 := new(Route).PathPrefix("/foo").Subrouter() + subrouter4 := new(Route).PathPrefix("/foo/bar").Subrouter() + subrouter5 := new(Route).PathPrefix("/{category}").Subrouter() + + tests := []routeTest{ + { + route: subrouter1.Path("/{v2:[a-z]+}"), + request: newRequest("GET", "http://aaa.google.com/bbb"), + vars: map[string]string{"v1": "aaa", "v2": "bbb"}, + host: "aaa.google.com", + path: "/bbb", + pathTemplate: `/{v2:[a-z]+}`, + hostTemplate: `{v1:[a-z]+}.google.com`, + shouldMatch: true, + }, + { + route: subrouter1.Path("/{v2:[a-z]+}"), + request: newRequest("GET", "http://111.google.com/111"), + vars: map[string]string{"v1": "aaa", "v2": "bbb"}, + host: "aaa.google.com", + path: "/bbb", + pathTemplate: `/{v2:[a-z]+}`, + hostTemplate: `{v1:[a-z]+}.google.com`, + shouldMatch: false, + }, + { + route: subrouter2.Path("/baz/{v2}"), + request: newRequest("GET", "http://localhost/foo/bar/baz/ding"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "/foo/bar/baz/ding", + pathTemplate: `/foo/{v1}/baz/{v2}`, + shouldMatch: true, + }, + { + route: subrouter2.Path("/baz/{v2}"), + request: newRequest("GET", "http://localhost/foo/bar"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "/foo/bar/baz/ding", + pathTemplate: `/foo/{v1}/baz/{v2}`, + shouldMatch: false, + }, + { + route: subrouter3.Path("/"), + request: newRequest("GET", "http://localhost/foo/"), + vars: map[string]string{}, + host: "", + path: "/foo/", + pathTemplate: `/foo/`, + shouldMatch: true, + }, + { + route: subrouter3.Path(""), + request: newRequest("GET", "http://localhost/foo"), + vars: map[string]string{}, + host: "", + path: "/foo", + pathTemplate: `/foo`, + shouldMatch: true, + }, + + { + route: subrouter4.Path("/"), + request: newRequest("GET", "http://localhost/foo/bar/"), + vars: map[string]string{}, + host: "", + path: "/foo/bar/", + pathTemplate: `/foo/bar/`, + shouldMatch: true, + }, + { + route: subrouter4.Path(""), + request: newRequest("GET", "http://localhost/foo/bar"), + vars: map[string]string{}, + host: "", + path: "/foo/bar", + pathTemplate: `/foo/bar`, + shouldMatch: true, + }, + { + route: subrouter5.Path("/"), + request: newRequest("GET", "http://localhost/baz/"), + vars: map[string]string{"category": "baz"}, + host: "", + path: "/baz/", + pathTemplate: `/{category}/`, + shouldMatch: true, + }, + { + route: subrouter5.Path(""), + request: newRequest("GET", "http://localhost/baz"), + vars: map[string]string{"category": "baz"}, + host: "", + path: "/baz", + pathTemplate: `/{category}`, + shouldMatch: true, + }, + { + title: "Build with scheme on parent router", + route: new(Route).Schemes("ftp").Host("google.com").Subrouter().Path("/"), + request: newRequest("GET", "ftp://google.com/"), + scheme: "ftp", + host: "google.com", + path: "/", + pathTemplate: `/`, + hostTemplate: `google.com`, + shouldMatch: true, + }, + { + title: "Prefer scheme on child route when building URLs", + route: new(Route).Schemes("https", "ftp").Host("google.com").Subrouter().Schemes("ftp").Path("/"), + request: newRequest("GET", "ftp://google.com/"), + scheme: "ftp", + host: "google.com", + path: "/", + pathTemplate: `/`, + hostTemplate: `google.com`, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + testUseEscapedRoute(t, test) + } +} + +func TestNamedRoutes(t *testing.T) { + r1 := NewRouter() + r1.NewRoute().Name("a") + r1.NewRoute().Name("b") + r1.NewRoute().Name("c") + + r2 := r1.NewRoute().Subrouter() + r2.NewRoute().Name("d") + r2.NewRoute().Name("e") + r2.NewRoute().Name("f") + + r3 := r2.NewRoute().Subrouter() + r3.NewRoute().Name("g") + r3.NewRoute().Name("h") + r3.NewRoute().Name("i") + + if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 { + t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes) + } else if r1.Get("i") == nil { + t.Errorf("Subroute name not registered") + } +} + +func TestStrictSlash(t *testing.T) { + r := NewRouter() + r.StrictSlash(true) + + tests := []routeTest{ + { + title: "Redirect path without slash", + route: r.NewRoute().Path("/111/"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Do not redirect path with slash", + route: r.NewRoute().Path("/111/"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + shouldRedirect: false, + }, + { + title: "Redirect path with slash", + route: r.NewRoute().Path("/111"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Do not redirect path without slash", + route: r.NewRoute().Path("/111"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + shouldRedirect: false, + }, + { + title: "Propagate StrictSlash to subrouters", + route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"), + request: newRequest("GET", "http://localhost/static/images"), + vars: map[string]string{}, + host: "", + path: "/static/images/", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Ignore StrictSlash for path prefix", + route: r.NewRoute().PathPrefix("/static/"), + request: newRequest("GET", "http://localhost/static/logo.png"), + vars: map[string]string{}, + host: "", + path: "/static/", + shouldMatch: true, + shouldRedirect: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + testUseEscapedRoute(t, test) + } +} + +func TestUseEncodedPath(t *testing.T) { + r := NewRouter() + r.UseEncodedPath() + + tests := []routeTest{ + { + title: "Router with useEncodedPath, URL with encoded slash does match", + route: r.NewRoute().Path("/v1/{v1}/v2"), + request: newRequest("GET", "http://localhost/v1/1%2F2/v2"), + vars: map[string]string{"v1": "1%2F2"}, + host: "", + path: "/v1/1%2F2/v2", + pathTemplate: `/v1/{v1}/v2`, + shouldMatch: true, + }, + { + title: "Router with useEncodedPath, URL with encoded slash doesn't match", + route: r.NewRoute().Path("/v1/1/2/v2"), + request: newRequest("GET", "http://localhost/v1/1%2F2/v2"), + vars: map[string]string{"v1": "1%2F2"}, + host: "", + path: "/v1/1%2F2/v2", + pathTemplate: `/v1/1/2/v2`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestWalkSingleDepth(t *testing.T) { + r0 := NewRouter() + r1 := NewRouter() + r2 := NewRouter() + + r0.Path("/g") + r0.Path("/o") + r0.Path("/d").Handler(r1) + r0.Path("/r").Handler(r2) + r0.Path("/a") + + r1.Path("/z") + r1.Path("/i") + r1.Path("/l") + r1.Path("/l") + + r2.Path("/i") + r2.Path("/l") + r2.Path("/l") + + paths := []string{"g", "o", "r", "i", "l", "l", "a"} + depths := []int{0, 0, 0, 1, 1, 1, 0} + i := 0 + err := r0.Walk(func(route *Route, router *Router, ancestors []*Route) error { + matcher := route.matchers[0].(*routeRegexp) + if matcher.template == "/d" { + return SkipRouter + } + if len(ancestors) != depths[i] { + t.Errorf(`Expected depth of %d at i = %d; got "%d"`, depths[i], i, len(ancestors)) + } + if matcher.template != "/"+paths[i] { + t.Errorf(`Expected "/%s" at i = %d; got "%s"`, paths[i], i, matcher.template) + } + i++ + return nil + }) + if err != nil { + panic(err) + } + if i != len(paths) { + t.Errorf("Expected %d routes, found %d", len(paths), i) + } +} + +func TestWalkNested(t *testing.T) { + router := NewRouter() + + g := router.Path("/g").Subrouter() + o := g.PathPrefix("/o").Subrouter() + r := o.PathPrefix("/r").Subrouter() + i := r.PathPrefix("/i").Subrouter() + l1 := i.PathPrefix("/l").Subrouter() + l2 := l1.PathPrefix("/l").Subrouter() + l2.Path("/a") + + testCases := []struct { + path string + ancestors []*Route + }{ + {"/g", []*Route{}}, + {"/g/o", []*Route{g.parent.(*Route)}}, + {"/g/o/r", []*Route{g.parent.(*Route), o.parent.(*Route)}}, + {"/g/o/r/i", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route)}}, + {"/g/o/r/i/l", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route)}}, + {"/g/o/r/i/l/l", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route), l1.parent.(*Route)}}, + {"/g/o/r/i/l/l/a", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route), l1.parent.(*Route), l2.parent.(*Route)}}, + } + + idx := 0 + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + path := testCases[idx].path + tpl := route.regexp.path.template + if tpl != path { + t.Errorf(`Expected %s got %s`, path, tpl) + } + currWantAncestors := testCases[idx].ancestors + if !reflect.DeepEqual(currWantAncestors, ancestors) { + t.Errorf(`Expected %+v got %+v`, currWantAncestors, ancestors) + } + idx++ + return nil + }) + if err != nil { + panic(err) + } + if idx != len(testCases) { + t.Errorf("Expected %d routes, found %d", len(testCases), idx) + } +} + +func TestWalkSubrouters(t *testing.T) { + router := NewRouter() + + g := router.Path("/g").Subrouter() + o := g.PathPrefix("/o").Subrouter() + o.Methods("GET") + o.Methods("PUT") + + // all 4 routes should be matched, but final 2 routes do not have path templates + paths := []string{"/g", "/g/o", "", ""} + idx := 0 + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + path := paths[idx] + tpl, _ := route.GetPathTemplate() + if tpl != path { + t.Errorf(`Expected %s got %s`, path, tpl) + } + idx++ + return nil + }) + if err != nil { + panic(err) + } + if idx != len(paths) { + t.Errorf("Expected %d routes, found %d", len(paths), idx) + } +} + +func TestWalkErrorRoute(t *testing.T) { + router := NewRouter() + router.Path("/g") + expectedError := errors.New("error") + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + return expectedError + }) + if err != expectedError { + t.Errorf("Expected %v routes, found %v", expectedError, err) + } +} + +func TestWalkErrorMatcher(t *testing.T) { + router := NewRouter() + expectedError := router.Path("/g").Subrouter().Path("").GetError() + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + return route.GetError() + }) + if err != expectedError { + t.Errorf("Expected %v routes, found %v", expectedError, err) + } +} + +func TestWalkErrorHandler(t *testing.T) { + handler := NewRouter() + expectedError := handler.Path("/path").Subrouter().Path("").GetError() + router := NewRouter() + router.Path("/g").Handler(handler) + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + return route.GetError() + }) + if err != expectedError { + t.Errorf("Expected %v routes, found %v", expectedError, err) + } +} + +func TestSubrouterErrorHandling(t *testing.T) { + superRouterCalled := false + subRouterCalled := false + + router := NewRouter() + router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + superRouterCalled = true + }) + subRouter := router.PathPrefix("/bign8").Subrouter() + subRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + subRouterCalled = true + }) + + req, _ := http.NewRequest("GET", "http://localhost/bign8/was/here", nil) + router.ServeHTTP(NewRecorder(), req) + + if superRouterCalled { + t.Error("Super router 404 handler called when sub-router 404 handler is available.") + } + if !subRouterCalled { + t.Error("Sub-router 404 handler was not called.") + } +} + +// See: https://github.com/gorilla/mux/issues/200 +func TestPanicOnCapturingGroups(t *testing.T) { + defer func() { + if recover() == nil { + t.Errorf("(Test that capturing groups now fail fast) Expected panic, however test completed successfully.\n") + } + }() + NewRouter().NewRoute().Path("/{type:(promo|special)}/{promoId}.json") +} + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +func getRouteTemplate(route *Route) string { + host, err := route.GetHostTemplate() + if err != nil { + host = "none" + } + path, err := route.GetPathTemplate() + if err != nil { + path = "none" + } + return fmt.Sprintf("Host: %v, Path: %v", host, path) +} + +func testRoute(t *testing.T, test routeTest) { + request := test.request + route := test.route + vars := test.vars + shouldMatch := test.shouldMatch + query := test.query + shouldRedirect := test.shouldRedirect + uri := url.URL{ + Scheme: test.scheme, + Host: test.host, + Path: test.path, + } + if uri.Scheme == "" { + uri.Scheme = "http" + } + + var match RouteMatch + ok := route.Match(request, &match) + if ok != shouldMatch { + msg := "Should match" + if !shouldMatch { + msg = "Should not match" + } + t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars) + return + } + if shouldMatch { + if vars != nil && !stringMapEqual(vars, match.Vars) { + t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars) + return + } + if test.scheme != "" { + u, err := route.URL(mapToPairs(match.Vars)...) + if err != nil { + t.Fatalf("(%v) URL error: %v -- %v", test.title, err, getRouteTemplate(route)) + } + if uri.Scheme != u.Scheme { + t.Errorf("(%v) URLScheme not equal: expected %v, got %v", test.title, uri.Scheme, u.Scheme) + return + } + } + if test.host != "" { + u, err := test.route.URLHost(mapToPairs(match.Vars)...) + if err != nil { + t.Fatalf("(%v) URLHost error: %v -- %v", test.title, err, getRouteTemplate(route)) + } + if uri.Scheme != u.Scheme { + t.Errorf("(%v) URLHost scheme not equal: expected %v, got %v -- %v", test.title, uri.Scheme, u.Scheme, getRouteTemplate(route)) + return + } + if uri.Host != u.Host { + t.Errorf("(%v) URLHost host not equal: expected %v, got %v -- %v", test.title, uri.Host, u.Host, getRouteTemplate(route)) + return + } + } + if test.path != "" { + u, err := route.URLPath(mapToPairs(match.Vars)...) + if err != nil { + t.Fatalf("(%v) URLPath error: %v -- %v", test.title, err, getRouteTemplate(route)) + } + if uri.Path != u.Path { + t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, uri.Path, u.Path, getRouteTemplate(route)) + return + } + } + if test.host != "" && test.path != "" { + u, err := route.URL(mapToPairs(match.Vars)...) + if err != nil { + t.Fatalf("(%v) URL error: %v -- %v", test.title, err, getRouteTemplate(route)) + } + if expected, got := uri.String(), u.String(); expected != got { + t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, expected, got, getRouteTemplate(route)) + return + } + } + if query != "" { + u, _ := route.URL(mapToPairs(match.Vars)...) + if query != u.RawQuery { + t.Errorf("(%v) URL query not equal: expected %v, got %v", test.title, query, u.RawQuery) + return + } + } + if shouldRedirect && match.Handler == nil { + t.Errorf("(%v) Did not redirect", test.title) + return + } + if !shouldRedirect && match.Handler != nil { + t.Errorf("(%v) Unexpected redirect", test.title) + return + } + } +} + +func testUseEscapedRoute(t *testing.T, test routeTest) { + test.route.useEncodedPath = true + testRoute(t, test) +} + +func testTemplate(t *testing.T, test routeTest) { + route := test.route + pathTemplate := test.pathTemplate + if len(pathTemplate) == 0 { + pathTemplate = test.path + } + hostTemplate := test.hostTemplate + if len(hostTemplate) == 0 { + hostTemplate = test.host + } + + routePathTemplate, pathErr := route.GetPathTemplate() + if pathErr == nil && routePathTemplate != pathTemplate { + t.Errorf("(%v) GetPathTemplate not equal: expected %v, got %v", test.title, pathTemplate, routePathTemplate) + } + + routeHostTemplate, hostErr := route.GetHostTemplate() + if hostErr == nil && routeHostTemplate != hostTemplate { + t.Errorf("(%v) GetHostTemplate not equal: expected %v, got %v", test.title, hostTemplate, routeHostTemplate) + } +} + +func testMethods(t *testing.T, test routeTest) { + route := test.route + methods, _ := route.GetMethods() + if strings.Join(methods, ",") != strings.Join(test.methods, ",") { + t.Errorf("(%v) GetMethods not equal: expected %v, got %v", test.title, test.methods, methods) + } +} + +func testRegexp(t *testing.T, test routeTest) { + route := test.route + routePathRegexp, regexpErr := route.GetPathRegexp() + if test.pathRegexp != "" && regexpErr == nil && routePathRegexp != test.pathRegexp { + t.Errorf("(%v) GetPathRegexp not equal: expected %v, got %v", test.title, test.pathRegexp, routePathRegexp) + } +} + +func testQueriesRegexp(t *testing.T, test routeTest) { + route := test.route + queries, queriesErr := route.GetQueriesRegexp() + gotQueries := strings.Join(queries, ",") + if test.queriesRegexp != "" && queriesErr == nil && gotQueries != test.queriesRegexp { + t.Errorf("(%v) GetQueriesRegexp not equal: expected %v, got %v", test.title, test.queriesRegexp, gotQueries) + } +} + +func testQueriesTemplates(t *testing.T, test routeTest) { + route := test.route + queries, queriesErr := route.GetQueriesTemplates() + gotQueries := strings.Join(queries, ",") + if test.queriesTemplate != "" && queriesErr == nil && gotQueries != test.queriesTemplate { + t.Errorf("(%v) GetQueriesTemplates not equal: expected %v, got %v", test.title, test.queriesTemplate, gotQueries) + } +} + +type TestA301ResponseWriter struct { + hh http.Header + status int +} + +func (ho *TestA301ResponseWriter) Header() http.Header { + return http.Header(ho.hh) +} + +func (ho *TestA301ResponseWriter) Write(b []byte) (int, error) { + return 0, nil +} + +func (ho *TestA301ResponseWriter) WriteHeader(code int) { + ho.status = code +} + +func Test301Redirect(t *testing.T) { + m := make(http.Header) + + func1 := func(w http.ResponseWriter, r *http.Request) {} + func2 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/api/", func2).Name("func2") + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil) + + res := TestA301ResponseWriter{ + hh: m, + status: 0, + } + r.ServeHTTP(&res, req) + + if "http://localhost/api/?abc=def" != res.hh["Location"][0] { + t.Errorf("Should have complete URL with query string") + } +} + +func TestSkipClean(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + func2 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.SkipClean(true) + r.HandleFunc("/api/", func2).Name("func2") + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil) + res := NewRecorder() + r.ServeHTTP(res, req) + + if len(res.HeaderMap["Location"]) != 0 { + t.Errorf("Shouldn't redirect since skip clean is disabled") + } +} + +// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW +func TestSubrouterHeader(t *testing.T) { + expected := "func1 response" + func1 := func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, expected) + } + func2 := func(http.ResponseWriter, *http.Request) {} + + r := NewRouter() + s := r.Headers("SomeSpecialHeader", "").Subrouter() + s.HandleFunc("/", func1).Name("func1") + r.HandleFunc("/", func2).Name("func2") + + req, _ := http.NewRequest("GET", "http://localhost/", nil) + req.Header.Add("SomeSpecialHeader", "foo") + match := new(RouteMatch) + matched := r.Match(req, match) + if !matched { + t.Errorf("Should match request") + } + if match.Route.GetName() != "func1" { + t.Errorf("Expecting func1 handler, got %s", match.Route.GetName()) + } + resp := NewRecorder() + match.Handler.ServeHTTP(resp, req) + if resp.Body.String() != expected { + t.Errorf("Expecting %q", expected) + } +} + +func TestNoMatchMethodErrorHandler(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", func1).Methods("GET", "POST") + + req, _ := http.NewRequest("PUT", "http://localhost/", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + + if matched { + t.Error("Should not have matched route for methods") + } + + if match.MatchErr != ErrMethodMismatch { + t.Error("Should get ErrMethodMismatch error") + } + + resp := NewRecorder() + r.ServeHTTP(resp, req) + if resp.Code != 405 { + t.Errorf("Expecting code %v", 405) + } + + // Add matching route + r.HandleFunc("/", func1).Methods("PUT") + + match = new(RouteMatch) + matched = r.Match(req, match) + + if !matched { + t.Error("Should have matched route for methods") + } + + if match.MatchErr != nil { + t.Error("Should not have any matching error. Found:", match.MatchErr) + } +} + +func TestErrMatchNotFound(t *testing.T) { + emptyHandler := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", emptyHandler) + s := r.PathPrefix("/sub/").Subrouter() + s.HandleFunc("/", emptyHandler) + + // Regular 404 not found + req, _ := http.NewRequest("GET", "/sub/whatever", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + + if matched { + t.Errorf("Subrouter should not have matched that, got %v", match.Route) + } + // Even without a custom handler, MatchErr is set to ErrNotFound + if match.MatchErr != ErrNotFound { + t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr) + } + + // Now lets add a 404 handler to subrouter + s.NotFoundHandler = http.NotFoundHandler() + req, _ = http.NewRequest("GET", "/sub/whatever", nil) + + // Test the subrouter first + match = new(RouteMatch) + matched = s.Match(req, match) + // Now we should get a match + if !matched { + t.Errorf("Subrouter should have matched %s", req.RequestURI) + } + // But MatchErr should be set to ErrNotFound anyway + if match.MatchErr != ErrNotFound { + t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr) + } + + // Now test the parent (MatchErr should propagate) + match = new(RouteMatch) + matched = r.Match(req, match) + + // Now we should get a match + if !matched { + t.Errorf("Router should have matched %s via subrouter", req.RequestURI) + } + // But MatchErr should be set to ErrNotFound anyway + if match.MatchErr != ErrNotFound { + t.Errorf("Expected ErrNotFound MatchErr, but was %v", match.MatchErr) + } +} + +// methodsSubrouterTest models the data necessary for testing handler +// matching for subrouters created after HTTP methods matcher registration. +type methodsSubrouterTest struct { + title string + wantCode int + router *Router + // method is the input into the request and expected response + method string + // input request path + path string + // redirectTo is the expected location path for strict-slash matches + redirectTo string +} + +// methodHandler writes the method string in response. +func methodHandler(method string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(method)) + } +} + +// TestMethodsSubrouterCatchall matches handlers for subrouters where a +// catchall handler is set for a mis-matching method. +func TestMethodsSubrouterCatchall(t *testing.T) { + t.Parallel() + + router := NewRouter() + router.Methods("PATCH").Subrouter().PathPrefix("/").HandlerFunc(methodHandler("PUT")) + router.Methods("GET").Subrouter().HandleFunc("/foo", methodHandler("GET")) + router.Methods("POST").Subrouter().HandleFunc("/foo", methodHandler("POST")) + router.Methods("DELETE").Subrouter().HandleFunc("/foo", methodHandler("DELETE")) + + tests := []methodsSubrouterTest{ + { + title: "match GET handler", + router: router, + path: "http://localhost/foo", + method: "GET", + wantCode: http.StatusOK, + }, + { + title: "match POST handler", + router: router, + method: "POST", + path: "http://localhost/foo", + wantCode: http.StatusOK, + }, + { + title: "match DELETE handler", + router: router, + method: "DELETE", + path: "http://localhost/foo", + wantCode: http.StatusOK, + }, + { + title: "disallow PUT method", + router: router, + method: "PUT", + path: "http://localhost/foo", + wantCode: http.StatusMethodNotAllowed, + }, + } + + for _, test := range tests { + testMethodsSubrouter(t, test) + } +} + +// TestMethodsSubrouterStrictSlash matches handlers on subrouters with +// strict-slash matchers. +func TestMethodsSubrouterStrictSlash(t *testing.T) { + t.Parallel() + + router := NewRouter() + sub := router.PathPrefix("/").Subrouter() + sub.StrictSlash(true).Path("/foo").Methods("GET").Subrouter().HandleFunc("", methodHandler("GET")) + sub.StrictSlash(true).Path("/foo/").Methods("PUT").Subrouter().HandleFunc("/", methodHandler("PUT")) + sub.StrictSlash(true).Path("/foo/").Methods("POST").Subrouter().HandleFunc("/", methodHandler("POST")) + + tests := []methodsSubrouterTest{ + { + title: "match POST handler", + router: router, + method: "POST", + path: "http://localhost/foo/", + wantCode: http.StatusOK, + }, + { + title: "match GET handler", + router: router, + method: "GET", + path: "http://localhost/foo", + wantCode: http.StatusOK, + }, + { + title: "match POST handler, redirect strict-slash", + router: router, + method: "POST", + path: "http://localhost/foo", + redirectTo: "http://localhost/foo/", + wantCode: http.StatusMovedPermanently, + }, + { + title: "match GET handler, redirect strict-slash", + router: router, + method: "GET", + path: "http://localhost/foo/", + redirectTo: "http://localhost/foo", + wantCode: http.StatusMovedPermanently, + }, + { + title: "disallow DELETE method", + router: router, + method: "DELETE", + path: "http://localhost/foo", + wantCode: http.StatusMethodNotAllowed, + }, + } + + for _, test := range tests { + testMethodsSubrouter(t, test) + } +} + +// TestMethodsSubrouterPathPrefix matches handlers on subrouters created +// on a router with a path prefix matcher and method matcher. +func TestMethodsSubrouterPathPrefix(t *testing.T) { + t.Parallel() + + router := NewRouter() + router.PathPrefix("/1").Methods("POST").Subrouter().HandleFunc("/2", methodHandler("POST")) + router.PathPrefix("/1").Methods("DELETE").Subrouter().HandleFunc("/2", methodHandler("DELETE")) + router.PathPrefix("/1").Methods("PUT").Subrouter().HandleFunc("/2", methodHandler("PUT")) + router.PathPrefix("/1").Methods("POST").Subrouter().HandleFunc("/2", methodHandler("POST2")) + + tests := []methodsSubrouterTest{ + { + title: "match first POST handler", + router: router, + method: "POST", + path: "http://localhost/1/2", + wantCode: http.StatusOK, + }, + { + title: "match DELETE handler", + router: router, + method: "DELETE", + path: "http://localhost/1/2", + wantCode: http.StatusOK, + }, + { + title: "match PUT handler", + router: router, + method: "PUT", + path: "http://localhost/1/2", + wantCode: http.StatusOK, + }, + { + title: "disallow PATCH method", + router: router, + method: "PATCH", + path: "http://localhost/1/2", + wantCode: http.StatusMethodNotAllowed, + }, + } + + for _, test := range tests { + testMethodsSubrouter(t, test) + } +} + +// TestMethodsSubrouterSubrouter matches handlers on subrouters produced +// from method matchers registered on a root subrouter. +func TestMethodsSubrouterSubrouter(t *testing.T) { + t.Parallel() + + router := NewRouter() + sub := router.PathPrefix("/1").Subrouter() + sub.Methods("POST").Subrouter().HandleFunc("/2", methodHandler("POST")) + sub.Methods("GET").Subrouter().HandleFunc("/2", methodHandler("GET")) + sub.Methods("PATCH").Subrouter().HandleFunc("/2", methodHandler("PATCH")) + sub.HandleFunc("/2", methodHandler("PUT")).Subrouter().Methods("PUT") + sub.HandleFunc("/2", methodHandler("POST2")).Subrouter().Methods("POST") + + tests := []methodsSubrouterTest{ + { + title: "match first POST handler", + router: router, + method: "POST", + path: "http://localhost/1/2", + wantCode: http.StatusOK, + }, + { + title: "match GET handler", + router: router, + method: "GET", + path: "http://localhost/1/2", + wantCode: http.StatusOK, + }, + { + title: "match PATCH handler", + router: router, + method: "PATCH", + path: "http://localhost/1/2", + wantCode: http.StatusOK, + }, + { + title: "match PUT handler", + router: router, + method: "PUT", + path: "http://localhost/1/2", + wantCode: http.StatusOK, + }, + { + title: "disallow DELETE method", + router: router, + method: "DELETE", + path: "http://localhost/1/2", + wantCode: http.StatusMethodNotAllowed, + }, + } + + for _, test := range tests { + testMethodsSubrouter(t, test) + } +} + +// TestMethodsSubrouterPathVariable matches handlers on matching paths +// with path variables in them. +func TestMethodsSubrouterPathVariable(t *testing.T) { + t.Parallel() + + router := NewRouter() + router.Methods("GET").Subrouter().HandleFunc("/foo", methodHandler("GET")) + router.Methods("POST").Subrouter().HandleFunc("/{any}", methodHandler("POST")) + router.Methods("DELETE").Subrouter().HandleFunc("/1/{any}", methodHandler("DELETE")) + router.Methods("PUT").Subrouter().HandleFunc("/1/{any}", methodHandler("PUT")) + + tests := []methodsSubrouterTest{ + { + title: "match GET handler", + router: router, + method: "GET", + path: "http://localhost/foo", + wantCode: http.StatusOK, + }, + { + title: "match POST handler", + router: router, + method: "POST", + path: "http://localhost/foo", + wantCode: http.StatusOK, + }, + { + title: "match DELETE handler", + router: router, + method: "DELETE", + path: "http://localhost/1/foo", + wantCode: http.StatusOK, + }, + { + title: "match PUT handler", + router: router, + method: "PUT", + path: "http://localhost/1/foo", + wantCode: http.StatusOK, + }, + { + title: "disallow PATCH method", + router: router, + method: "PATCH", + path: "http://localhost/1/foo", + wantCode: http.StatusMethodNotAllowed, + }, + } + + for _, test := range tests { + testMethodsSubrouter(t, test) + } +} + +// testMethodsSubrouter runs an individual methodsSubrouterTest. +func testMethodsSubrouter(t *testing.T, test methodsSubrouterTest) { + // Execute request + req, _ := http.NewRequest(test.method, test.path, nil) + resp := NewRecorder() + test.router.ServeHTTP(resp, req) + + switch test.wantCode { + case http.StatusMethodNotAllowed: + if resp.Code != http.StatusMethodNotAllowed { + t.Errorf(`(%s) Expected "405 Method Not Allowed", but got %d code`, test.title, resp.Code) + } else if matchedMethod := resp.Body.String(); matchedMethod != "" { + t.Errorf(`(%s) Expected "405 Method Not Allowed", but %q handler was called`, test.title, matchedMethod) + } + + case http.StatusMovedPermanently: + if gotLocation := resp.HeaderMap.Get("Location"); gotLocation != test.redirectTo { + t.Errorf("(%s) Expected %q route-match to redirect to %q, but got %q", test.title, test.method, test.redirectTo, gotLocation) + } + + case http.StatusOK: + if matchedMethod := resp.Body.String(); matchedMethod != test.method { + t.Errorf("(%s) Expected %q handler to be called, but %q handler was called", test.title, test.method, matchedMethod) + } + + default: + expectedCodes := []int{http.StatusMethodNotAllowed, http.StatusMovedPermanently, http.StatusOK} + t.Errorf("(%s) Expected wantCode to be one of: %v, but got %d", test.title, expectedCodes, test.wantCode) + } +} + +// mapToPairs converts a string map to a slice of string pairs +func mapToPairs(m map[string]string) []string { + var i int + p := make([]string, len(m)*2) + for k, v := range m { + p[i] = k + p[i+1] = v + i += 2 + } + return p +} + +// stringMapEqual checks the equality of two string maps +func stringMapEqual(m1, m2 map[string]string) bool { + nil1 := m1 == nil + nil2 := m2 == nil + if nil1 != nil2 || len(m1) != len(m2) { + return false + } + for k, v := range m1 { + if v != m2[k] { + return false + } + } + return true +} + +// newRequest is a helper function to create a new request with a method and url. +// The request returned is a 'server' request as opposed to a 'client' one through +// simulated write onto the wire and read off of the wire. +// The differences between requests are detailed in the net/http package. +func newRequest(method, url string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + // extract the escaped original host+path from url + // http://localhost/path/here?v=1#frag -> //localhost/path/here + opaque := "" + if i := len(req.URL.Scheme); i > 0 { + opaque = url[i+1:] + } + + if i := strings.LastIndex(opaque, "?"); i > -1 { + opaque = opaque[:i] + } + if i := strings.LastIndex(opaque, "#"); i > -1 { + opaque = opaque[:i] + } + + // Escaped host+path workaround as detailed in https://golang.org/pkg/net/url/#URL + // for < 1.5 client side workaround + req.URL.Opaque = opaque + + // Simulate writing to wire + var buff bytes.Buffer + req.Write(&buff) + ioreader := bufio.NewReader(&buff) + + // Parse request off of 'wire' + req, err = http.ReadRequest(ioreader) + if err != nil { + panic(err) + } + return req +} diff --git a/vendor/github.com/gorilla/mux/old_test.go b/vendor/github.com/gorilla/mux/old_test.go new file mode 100644 index 00000000..b228983c --- /dev/null +++ b/vendor/github.com/gorilla/mux/old_test.go @@ -0,0 +1,704 @@ +// Old tests ported to Go1. This is a mess. Want to drop it one day. + +// Copyright 2011 Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "bytes" + "net/http" + "testing" +) + +// ---------------------------------------------------------------------------- +// ResponseRecorder +// ---------------------------------------------------------------------------- +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// ResponseRecorder is an implementation of http.ResponseWriter that +// records its mutations for later inspection in tests. +type ResponseRecorder struct { + Code int // the HTTP response code from WriteHeader + HeaderMap http.Header // the HTTP response headers + Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to + Flushed bool +} + +// NewRecorder returns an initialized ResponseRecorder. +func NewRecorder() *ResponseRecorder { + return &ResponseRecorder{ + HeaderMap: make(http.Header), + Body: new(bytes.Buffer), + } +} + +// Header returns the response headers. +func (rw *ResponseRecorder) Header() http.Header { + return rw.HeaderMap +} + +// Write always succeeds and writes to rw.Body, if not nil. +func (rw *ResponseRecorder) Write(buf []byte) (int, error) { + if rw.Body != nil { + rw.Body.Write(buf) + } + if rw.Code == 0 { + rw.Code = http.StatusOK + } + return len(buf), nil +} + +// WriteHeader sets rw.Code. +func (rw *ResponseRecorder) WriteHeader(code int) { + rw.Code = code +} + +// Flush sets rw.Flushed to true. +func (rw *ResponseRecorder) Flush() { + rw.Flushed = true +} + +// ---------------------------------------------------------------------------- + +func TestRouteMatchers(t *testing.T) { + var scheme, host, path, query, method string + var headers map[string]string + var resultVars map[bool]map[string]string + + router := NewRouter() + router.NewRoute().Host("{var1}.google.com"). + Path("/{var2:[a-z]+}/{var3:[0-9]+}"). + Queries("foo", "bar"). + Methods("GET"). + Schemes("https"). + Headers("x-requested-with", "XMLHttpRequest") + router.NewRoute().Host("www.{var4}.com"). + PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}"). + Queries("baz", "ding"). + Methods("POST"). + Schemes("http"). + Headers("Content-Type", "application/json") + + reset := func() { + // Everything match. + scheme = "https" + host = "www.google.com" + path = "/product/42" + query = "?foo=bar" + method = "GET" + headers = map[string]string{"X-Requested-With": "XMLHttpRequest"} + resultVars = map[bool]map[string]string{ + true: {"var1": "www", "var2": "product", "var3": "42"}, + false: {}, + } + } + + reset2 := func() { + // Everything match. + scheme = "http" + host = "www.google.com" + path = "/foo/product/42/path/that/is/ignored" + query = "?baz=ding" + method = "POST" + headers = map[string]string{"Content-Type": "application/json"} + resultVars = map[bool]map[string]string{ + true: {"var4": "google", "var5": "product", "var6": "42"}, + false: {}, + } + } + + match := func(shouldMatch bool) { + url := scheme + "://" + host + path + query + request, _ := http.NewRequest(method, url, nil) + for key, value := range headers { + request.Header.Add(key, value) + } + + var routeMatch RouteMatch + matched := router.Match(request, &routeMatch) + if matched != shouldMatch { + t.Errorf("Expected: %v\nGot: %v\nRequest: %v %v", shouldMatch, matched, request.Method, url) + } + + if matched { + currentRoute := routeMatch.Route + if currentRoute == nil { + t.Errorf("Expected a current route.") + } + vars := routeMatch.Vars + expectedVars := resultVars[shouldMatch] + if len(vars) != len(expectedVars) { + t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) + } + for name, value := range vars { + if expectedVars[name] != value { + t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) + } + } + } + } + + // 1st route -------------------------------------------------------------- + + // Everything match. + reset() + match(true) + + // Scheme doesn't match. + reset() + scheme = "http" + match(false) + + // Host doesn't match. + reset() + host = "www.mygoogle.com" + match(false) + + // Path doesn't match. + reset() + path = "/product/notdigits" + match(false) + + // Query doesn't match. + reset() + query = "?foo=baz" + match(false) + + // Method doesn't match. + reset() + method = "POST" + match(false) + + // Header doesn't match. + reset() + headers = map[string]string{} + match(false) + + // Everything match, again. + reset() + match(true) + + // 2nd route -------------------------------------------------------------- + // Everything match. + reset2() + match(true) + + // Scheme doesn't match. + reset2() + scheme = "https" + match(false) + + // Host doesn't match. + reset2() + host = "sub.google.com" + match(false) + + // Path doesn't match. + reset2() + path = "/bar/product/42" + match(false) + + // Query doesn't match. + reset2() + query = "?foo=baz" + match(false) + + // Method doesn't match. + reset2() + method = "GET" + match(false) + + // Header doesn't match. + reset2() + headers = map[string]string{} + match(false) + + // Everything match, again. + reset2() + match(true) +} + +type headerMatcherTest struct { + matcher headerMatcher + headers map[string]string + result bool +} + +var headerMatcherTests = []headerMatcherTest{ + { + matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), + headers: map[string]string{"X-Requested-With": "XMLHttpRequest"}, + result: true, + }, + { + matcher: headerMatcher(map[string]string{"x-requested-with": ""}), + headers: map[string]string{"X-Requested-With": "anything"}, + result: true, + }, + { + matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), + headers: map[string]string{}, + result: false, + }, +} + +type hostMatcherTest struct { + matcher *Route + url string + vars map[string]string + result bool +} + +var hostMatcherTests = []hostMatcherTest{ + { + matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), + url: "http://abc.def.ghi/", + vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, + result: true, + }, + { + matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), + url: "http://a.b.c/", + vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, + result: false, + }, +} + +type methodMatcherTest struct { + matcher methodMatcher + method string + result bool +} + +var methodMatcherTests = []methodMatcherTest{ + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "GET", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "POST", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "PUT", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "DELETE", + result: false, + }, +} + +type pathMatcherTest struct { + matcher *Route + url string + vars map[string]string + result bool +} + +var pathMatcherTests = []pathMatcherTest{ + { + matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), + url: "http://localhost:8080/123/456/789", + vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, + result: true, + }, + { + matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), + url: "http://localhost:8080/1/2/3", + vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, + result: false, + }, +} + +type schemeMatcherTest struct { + matcher schemeMatcher + url string + result bool +} + +var schemeMatcherTests = []schemeMatcherTest{ + { + matcher: schemeMatcher([]string{"http", "https"}), + url: "http://localhost:8080/", + result: true, + }, + { + matcher: schemeMatcher([]string{"http", "https"}), + url: "https://localhost:8080/", + result: true, + }, + { + matcher: schemeMatcher([]string{"https"}), + url: "http://localhost:8080/", + result: false, + }, + { + matcher: schemeMatcher([]string{"http"}), + url: "https://localhost:8080/", + result: false, + }, +} + +type urlBuildingTest struct { + route *Route + vars []string + url string +} + +var urlBuildingTests = []urlBuildingTest{ + { + route: new(Route).Host("foo.domain.com"), + vars: []string{}, + url: "http://foo.domain.com", + }, + { + route: new(Route).Host("{subdomain}.domain.com"), + vars: []string{"subdomain", "bar"}, + url: "http://bar.domain.com", + }, + { + route: new(Route).Host("foo.domain.com").Path("/articles"), + vars: []string{}, + url: "http://foo.domain.com/articles", + }, + { + route: new(Route).Path("/articles"), + vars: []string{}, + url: "/articles", + }, + { + route: new(Route).Path("/articles/{category}/{id:[0-9]+}"), + vars: []string{"category", "technology", "id", "42"}, + url: "/articles/technology/42", + }, + { + route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"), + vars: []string{"subdomain", "foo", "category", "technology", "id", "42"}, + url: "http://foo.domain.com/articles/technology/42", + }, +} + +func TestHeaderMatcher(t *testing.T) { + for _, v := range headerMatcherTests { + request, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + for key, value := range v.headers { + request.Header.Add(key, value) + } + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, request.Header) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, request.Header) + } + } + } +} + +func TestHostMatcher(t *testing.T) { + for _, v := range hostMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + vars := routeMatch.Vars + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + if result { + if len(vars) != len(v.vars) { + t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) + } + for name, value := range vars { + if v.vars[name] != value { + t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) + } + } + } else { + if len(vars) != 0 { + t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) + } + } + } +} + +func TestMethodMatcher(t *testing.T) { + for _, v := range methodMatcherTests { + request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.method) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.method) + } + } + } +} + +func TestPathMatcher(t *testing.T) { + for _, v := range pathMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + vars := routeMatch.Vars + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + if result { + if len(vars) != len(v.vars) { + t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) + } + for name, value := range vars { + if v.vars[name] != value { + t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) + } + } + } else { + if len(vars) != 0 { + t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) + } + } + } +} + +func TestSchemeMatcher(t *testing.T) { + for _, v := range schemeMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + } +} + +func TestUrlBuilding(t *testing.T) { + + for _, v := range urlBuildingTests { + u, _ := v.route.URL(v.vars...) + url := u.String() + if url != v.url { + t.Errorf("expected %v, got %v", v.url, url) + /* + reversePath := "" + reverseHost := "" + if v.route.pathTemplate != nil { + reversePath = v.route.pathTemplate.Reverse + } + if v.route.hostTemplate != nil { + reverseHost = v.route.hostTemplate.Reverse + } + + t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost) + */ + } + } + + ArticleHandler := func(w http.ResponseWriter, r *http.Request) { + } + + router := NewRouter() + router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article") + + url, _ := router.Get("article").URL("category", "technology", "id", "42") + expected := "/articles/technology/42" + if url.String() != expected { + t.Errorf("Expected %v, got %v", expected, url.String()) + } +} + +func TestMatchedRouteName(t *testing.T) { + routeName := "stock" + router := NewRouter() + route := router.NewRoute().Path("/products/").Name(routeName) + + url := "http://www.example.com/products/" + request, _ := http.NewRequest("GET", url, nil) + var rv RouteMatch + ok := router.Match(request, &rv) + + if !ok || rv.Route != route { + t.Errorf("Expected same route, got %+v.", rv.Route) + } + + retName := rv.Route.GetName() + if retName != routeName { + t.Errorf("Expected %q, got %q.", routeName, retName) + } +} + +func TestSubRouting(t *testing.T) { + // Example from docs. + router := NewRouter() + subrouter := router.NewRoute().Host("www.example.com").Subrouter() + route := subrouter.NewRoute().Path("/products/").Name("products") + + url := "http://www.example.com/products/" + request, _ := http.NewRequest("GET", url, nil) + var rv RouteMatch + ok := router.Match(request, &rv) + + if !ok || rv.Route != route { + t.Errorf("Expected same route, got %+v.", rv.Route) + } + + u, _ := router.Get("products").URL() + builtURL := u.String() + // Yay, subroute aware of the domain when building! + if builtURL != url { + t.Errorf("Expected %q, got %q.", url, builtURL) + } +} + +func TestVariableNames(t *testing.T) { + route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}") + if route.err == nil { + t.Errorf("Expected error for duplicated variable names") + } +} + +func TestRedirectSlash(t *testing.T) { + var route *Route + var routeMatch RouteMatch + r := NewRouter() + + r.StrictSlash(false) + route = r.NewRoute() + if route.strictSlash != false { + t.Errorf("Expected false redirectSlash.") + } + + r.StrictSlash(true) + route = r.NewRoute() + if route.strictSlash != true { + t.Errorf("Expected true redirectSlash.") + } + + route = new(Route) + route.strictSlash = true + route.Path("/{arg1}/{arg2:[0-9]+}/") + request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil) + routeMatch = RouteMatch{} + _ = route.Match(request, &routeMatch) + vars := routeMatch.Vars + if vars["arg1"] != "foo" { + t.Errorf("Expected foo.") + } + if vars["arg2"] != "123" { + t.Errorf("Expected 123.") + } + rsp := NewRecorder() + routeMatch.Handler.ServeHTTP(rsp, request) + if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" { + t.Errorf("Expected redirect header.") + } + + route = new(Route) + route.strictSlash = true + route.Path("/{arg1}/{arg2:[0-9]+}") + request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil) + routeMatch = RouteMatch{} + _ = route.Match(request, &routeMatch) + vars = routeMatch.Vars + if vars["arg1"] != "foo" { + t.Errorf("Expected foo.") + } + if vars["arg2"] != "123" { + t.Errorf("Expected 123.") + } + rsp = NewRecorder() + routeMatch.Handler.ServeHTTP(rsp, request) + if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" { + t.Errorf("Expected redirect header.") + } +} + +// Test for the new regexp library, still not available in stable Go. +func TestNewRegexp(t *testing.T) { + var p *routeRegexp + var matches []string + + tests := map[string]map[string][]string{ + "/{foo:a{2}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": nil, + "/aaaa": nil, + }, + "/{foo:a{2,}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": {"aaa"}, + "/aaaa": {"aaaa"}, + }, + "/{foo:a{2,3}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": {"aaa"}, + "/aaaa": nil, + }, + "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": { + "/a": nil, + "/ab": nil, + "/abc": nil, + "/abcd": nil, + "/abc/ab": {"abc", "ab"}, + "/abc/abc": nil, + "/abcd/ab": nil, + }, + `/{foo:\w{3,}}/{bar:\d{2,}}`: { + "/a": nil, + "/ab": nil, + "/abc": nil, + "/abc/1": nil, + "/abc/12": {"abc", "12"}, + "/abcd/12": {"abcd", "12"}, + "/abcd/123": {"abcd", "123"}, + }, + } + + for pattern, paths := range tests { + p, _ = newRouteRegexp(pattern, regexpTypePath, routeRegexpOptions{}) + for path, result := range paths { + matches = p.regexp.FindStringSubmatch(path) + if result == nil { + if matches != nil { + t.Errorf("%v should not match %v.", pattern, path) + } + } else { + if len(matches) != len(result)+1 { + t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches)) + } else { + for k, v := range result { + if matches[k+1] != v { + t.Errorf("Expected %v, got %v.", v, matches[k+1]) + } + } + } + } + } + } +} diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go new file mode 100644 index 00000000..2b57e562 --- /dev/null +++ b/vendor/github.com/gorilla/mux/regexp.go @@ -0,0 +1,332 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "bytes" + "fmt" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" +) + +type routeRegexpOptions struct { + strictSlash bool + useEncodedPath bool +} + +type regexpType int + +const ( + regexpTypePath regexpType = 0 + regexpTypeHost regexpType = 1 + regexpTypePrefix regexpType = 2 + regexpTypeQuery regexpType = 3 +) + +// newRouteRegexp parses a route template and returns a routeRegexp, +// used to match a host, a path or a query string. +// +// It will extract named variables, assemble a regexp to be matched, create +// a "reverse" template to build URLs and compile regexps to validate variable +// values used in URL building. +// +// Previously we accepted only Python-like identifiers for variable +// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that +// name and pattern can't be empty, and names can't contain a colon. +func newRouteRegexp(tpl string, typ regexpType, options routeRegexpOptions) (*routeRegexp, error) { + // Check if it is well-formed. + idxs, errBraces := braceIndices(tpl) + if errBraces != nil { + return nil, errBraces + } + // Backup the original. + template := tpl + // Now let's parse it. + defaultPattern := "[^/]+" + if typ == regexpTypeQuery { + defaultPattern = ".*" + } else if typ == regexpTypeHost { + defaultPattern = "[^.]+" + } + // Only match strict slash if not matching + if typ != regexpTypePath { + options.strictSlash = false + } + // Set a flag for strictSlash. + endSlash := false + if options.strictSlash && strings.HasSuffix(tpl, "/") { + tpl = tpl[:len(tpl)-1] + endSlash = true + } + varsN := make([]string, len(idxs)/2) + varsR := make([]*regexp.Regexp, len(idxs)/2) + pattern := bytes.NewBufferString("") + pattern.WriteByte('^') + reverse := bytes.NewBufferString("") + var end int + var err error + for i := 0; i < len(idxs); i += 2 { + // Set all values we are interested in. + raw := tpl[end:idxs[i]] + end = idxs[i+1] + parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2) + name := parts[0] + patt := defaultPattern + if len(parts) == 2 { + patt = parts[1] + } + // Name or pattern can't be empty. + if name == "" || patt == "" { + return nil, fmt.Errorf("mux: missing name or pattern in %q", + tpl[idxs[i]:end]) + } + // Build the regexp pattern. + fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt) + + // Build the reverse template. + fmt.Fprintf(reverse, "%s%%s", raw) + + // Append variable name and compiled pattern. + varsN[i/2] = name + varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) + if err != nil { + return nil, err + } + } + // Add the remaining. + raw := tpl[end:] + pattern.WriteString(regexp.QuoteMeta(raw)) + if options.strictSlash { + pattern.WriteString("[/]?") + } + if typ == regexpTypeQuery { + // Add the default pattern if the query value is empty + if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" { + pattern.WriteString(defaultPattern) + } + } + if typ != regexpTypePrefix { + pattern.WriteByte('$') + } + reverse.WriteString(raw) + if endSlash { + reverse.WriteByte('/') + } + // Compile full regexp. + reg, errCompile := regexp.Compile(pattern.String()) + if errCompile != nil { + return nil, errCompile + } + + // Check for capturing groups which used to work in older versions + if reg.NumSubexp() != len(idxs)/2 { + panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) + + "Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)") + } + + // Done! + return &routeRegexp{ + template: template, + regexpType: typ, + options: options, + regexp: reg, + reverse: reverse.String(), + varsN: varsN, + varsR: varsR, + }, nil +} + +// routeRegexp stores a regexp to match a host or path and information to +// collect and validate route variables. +type routeRegexp struct { + // The unmodified template. + template string + // The type of match + regexpType regexpType + // Options for matching + options routeRegexpOptions + // Expanded regexp. + regexp *regexp.Regexp + // Reverse template. + reverse string + // Variable names. + varsN []string + // Variable regexps (validators). + varsR []*regexp.Regexp +} + +// Match matches the regexp against the URL host or path. +func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { + if r.regexpType != regexpTypeHost { + if r.regexpType == regexpTypeQuery { + return r.matchQueryString(req) + } + path := req.URL.Path + if r.options.useEncodedPath { + path = req.URL.EscapedPath() + } + return r.regexp.MatchString(path) + } + + return r.regexp.MatchString(getHost(req)) +} + +// url builds a URL part using the given values. +func (r *routeRegexp) url(values map[string]string) (string, error) { + urlValues := make([]interface{}, len(r.varsN)) + for k, v := range r.varsN { + value, ok := values[v] + if !ok { + return "", fmt.Errorf("mux: missing route variable %q", v) + } + if r.regexpType == regexpTypeQuery { + value = url.QueryEscape(value) + } + urlValues[k] = value + } + rv := fmt.Sprintf(r.reverse, urlValues...) + if !r.regexp.MatchString(rv) { + // The URL is checked against the full regexp, instead of checking + // individual variables. This is faster but to provide a good error + // message, we check individual regexps if the URL doesn't match. + for k, v := range r.varsN { + if !r.varsR[k].MatchString(values[v]) { + return "", fmt.Errorf( + "mux: variable %q doesn't match, expected %q", values[v], + r.varsR[k].String()) + } + } + } + return rv, nil +} + +// getURLQuery returns a single query parameter from a request URL. +// For a URL with foo=bar&baz=ding, we return only the relevant key +// value pair for the routeRegexp. +func (r *routeRegexp) getURLQuery(req *http.Request) string { + if r.regexpType != regexpTypeQuery { + return "" + } + templateKey := strings.SplitN(r.template, "=", 2)[0] + for key, vals := range req.URL.Query() { + if key == templateKey && len(vals) > 0 { + return key + "=" + vals[0] + } + } + return "" +} + +func (r *routeRegexp) matchQueryString(req *http.Request) bool { + return r.regexp.MatchString(r.getURLQuery(req)) +} + +// braceIndices returns the first level curly brace indices from a string. +// It returns an error in case of unbalanced braces. +func braceIndices(s string) ([]int, error) { + var level, idx int + var idxs []int + for i := 0; i < len(s); i++ { + switch s[i] { + case '{': + if level++; level == 1 { + idx = i + } + case '}': + if level--; level == 0 { + idxs = append(idxs, idx, i+1) + } else if level < 0 { + return nil, fmt.Errorf("mux: unbalanced braces in %q", s) + } + } + } + if level != 0 { + return nil, fmt.Errorf("mux: unbalanced braces in %q", s) + } + return idxs, nil +} + +// varGroupName builds a capturing group name for the indexed variable. +func varGroupName(idx int) string { + return "v" + strconv.Itoa(idx) +} + +// ---------------------------------------------------------------------------- +// routeRegexpGroup +// ---------------------------------------------------------------------------- + +// routeRegexpGroup groups the route matchers that carry variables. +type routeRegexpGroup struct { + host *routeRegexp + path *routeRegexp + queries []*routeRegexp +} + +// setMatch extracts the variables from the URL once a route matches. +func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { + // Store host variables. + if v.host != nil { + host := getHost(req) + matches := v.host.regexp.FindStringSubmatchIndex(host) + if len(matches) > 0 { + extractVars(host, matches, v.host.varsN, m.Vars) + } + } + path := req.URL.Path + if r.useEncodedPath { + path = req.URL.EscapedPath() + } + // Store path variables. + if v.path != nil { + matches := v.path.regexp.FindStringSubmatchIndex(path) + if len(matches) > 0 { + extractVars(path, matches, v.path.varsN, m.Vars) + // Check if we should redirect. + if v.path.options.strictSlash { + p1 := strings.HasSuffix(path, "/") + p2 := strings.HasSuffix(v.path.template, "/") + if p1 != p2 { + u, _ := url.Parse(req.URL.String()) + if p1 { + u.Path = u.Path[:len(u.Path)-1] + } else { + u.Path += "/" + } + m.Handler = http.RedirectHandler(u.String(), 301) + } + } + } + } + // Store query string variables. + for _, q := range v.queries { + queryURL := q.getURLQuery(req) + matches := q.regexp.FindStringSubmatchIndex(queryURL) + if len(matches) > 0 { + extractVars(queryURL, matches, q.varsN, m.Vars) + } + } +} + +// getHost tries its best to return the request host. +func getHost(r *http.Request) string { + if r.URL.IsAbs() { + return r.URL.Host + } + host := r.Host + // Slice off any port information. + if i := strings.Index(host, ":"); i != -1 { + host = host[:i] + } + return host + +} + +func extractVars(input string, matches []int, names []string, output map[string]string) { + for i, name := range names { + output[name] = input[matches[2*i+2]:matches[2*i+3]] + } +} diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go new file mode 100644 index 00000000..4ce098d4 --- /dev/null +++ b/vendor/github.com/gorilla/mux/route.go @@ -0,0 +1,761 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "regexp" + "strings" +) + +// Route stores information to match a request and build URLs. +type Route struct { + // Parent where the route was registered (a Router). + parent parentRoute + // Request handler for the route. + handler http.Handler + // List of matchers. + matchers []matcher + // Manager for the variables from host and path. + regexp *routeRegexpGroup + // If true, when the path pattern is "/path/", accessing "/path" will + // redirect to the former and vice versa. + strictSlash bool + // If true, when the path pattern is "/path//to", accessing "/path//to" + // will not redirect + skipClean bool + // If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to" + useEncodedPath bool + // The scheme used when building URLs. + buildScheme string + // If true, this route never matches: it is only used to build URLs. + buildOnly bool + // The name used to build URLs. + name string + // Error resulted from building a route. + err error + + buildVarsFunc BuildVarsFunc +} + +func (r *Route) SkipClean() bool { + return r.skipClean +} + +// Match matches the route against the request. +func (r *Route) Match(req *http.Request, match *RouteMatch) bool { + if r.buildOnly || r.err != nil { + return false + } + + var matchErr error + + // Match everything. + for _, m := range r.matchers { + if matched := m.Match(req, match); !matched { + if _, ok := m.(methodMatcher); ok { + matchErr = ErrMethodMismatch + continue + } + matchErr = nil + return false + } + } + + if matchErr != nil { + match.MatchErr = matchErr + return false + } + + if match.MatchErr == ErrMethodMismatch { + // We found a route which matches request method, clear MatchErr + match.MatchErr = nil + // Then override the mis-matched handler + match.Handler = r.handler + } + + // Yay, we have a match. Let's collect some info about it. + if match.Route == nil { + match.Route = r + } + if match.Handler == nil { + match.Handler = r.handler + } + if match.Vars == nil { + match.Vars = make(map[string]string) + } + + // Set variables. + if r.regexp != nil { + r.regexp.setMatch(req, match, r) + } + return true +} + +// ---------------------------------------------------------------------------- +// Route attributes +// ---------------------------------------------------------------------------- + +// GetError returns an error resulted from building the route, if any. +func (r *Route) GetError() error { + return r.err +} + +// BuildOnly sets the route to never match: it is only used to build URLs. +func (r *Route) BuildOnly() *Route { + r.buildOnly = true + return r +} + +// Handler -------------------------------------------------------------------- + +// Handler sets a handler for the route. +func (r *Route) Handler(handler http.Handler) *Route { + if r.err == nil { + r.handler = handler + } + return r +} + +// HandlerFunc sets a handler function for the route. +func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route { + return r.Handler(http.HandlerFunc(f)) +} + +// GetHandler returns the handler for the route, if any. +func (r *Route) GetHandler() http.Handler { + return r.handler +} + +// Name ----------------------------------------------------------------------- + +// Name sets the name for the route, used to build URLs. +// If the name was registered already it will be overwritten. +func (r *Route) Name(name string) *Route { + if r.name != "" { + r.err = fmt.Errorf("mux: route already has name %q, can't set %q", + r.name, name) + } + if r.err == nil { + r.name = name + r.getNamedRoutes()[name] = r + } + return r +} + +// GetName returns the name for the route, if any. +func (r *Route) GetName() string { + return r.name +} + +// ---------------------------------------------------------------------------- +// Matchers +// ---------------------------------------------------------------------------- + +// matcher types try to match a request. +type matcher interface { + Match(*http.Request, *RouteMatch) bool +} + +// addMatcher adds a matcher to the route. +func (r *Route) addMatcher(m matcher) *Route { + if r.err == nil { + r.matchers = append(r.matchers, m) + } + return r +} + +// addRegexpMatcher adds a host or path matcher and builder to a route. +func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error { + if r.err != nil { + return r.err + } + r.regexp = r.getRegexpGroup() + if typ == regexpTypePath || typ == regexpTypePrefix { + if len(tpl) > 0 && tpl[0] != '/' { + return fmt.Errorf("mux: path must start with a slash, got %q", tpl) + } + if r.regexp.path != nil { + tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl + } + } + rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{ + strictSlash: r.strictSlash, + useEncodedPath: r.useEncodedPath, + }) + if err != nil { + return err + } + for _, q := range r.regexp.queries { + if err = uniqueVars(rr.varsN, q.varsN); err != nil { + return err + } + } + if typ == regexpTypeHost { + if r.regexp.path != nil { + if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil { + return err + } + } + r.regexp.host = rr + } else { + if r.regexp.host != nil { + if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil { + return err + } + } + if typ == regexpTypeQuery { + r.regexp.queries = append(r.regexp.queries, rr) + } else { + r.regexp.path = rr + } + } + r.addMatcher(rr) + return nil +} + +// Headers -------------------------------------------------------------------- + +// headerMatcher matches the request against header values. +type headerMatcher map[string]string + +func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchMapWithString(m, r.Header, true) +} + +// Headers adds a matcher for request header values. +// It accepts a sequence of key/value pairs to be matched. For example: +// +// r := mux.NewRouter() +// r.Headers("Content-Type", "application/json", +// "X-Requested-With", "XMLHttpRequest") +// +// The above route will only match if both request header values match. +// If the value is an empty string, it will match any value if the key is set. +func (r *Route) Headers(pairs ...string) *Route { + if r.err == nil { + var headers map[string]string + headers, r.err = mapFromPairsToString(pairs...) + return r.addMatcher(headerMatcher(headers)) + } + return r +} + +// headerRegexMatcher matches the request against the route given a regex for the header +type headerRegexMatcher map[string]*regexp.Regexp + +func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchMapWithRegex(m, r.Header, true) +} + +// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex +// support. For example: +// +// r := mux.NewRouter() +// r.HeadersRegexp("Content-Type", "application/(text|json)", +// "X-Requested-With", "XMLHttpRequest") +// +// The above route will only match if both the request header matches both regular expressions. +// If the value is an empty string, it will match any value if the key is set. +// Use the start and end of string anchors (^ and $) to match an exact value. +func (r *Route) HeadersRegexp(pairs ...string) *Route { + if r.err == nil { + var headers map[string]*regexp.Regexp + headers, r.err = mapFromPairsToRegex(pairs...) + return r.addMatcher(headerRegexMatcher(headers)) + } + return r +} + +// Host ----------------------------------------------------------------------- + +// Host adds a matcher for the URL host. +// It accepts a template with zero or more URL variables enclosed by {}. +// Variables can define an optional regexp pattern to be matched: +// +// - {name} matches anything until the next dot. +// +// - {name:pattern} matches the given regexp pattern. +// +// For example: +// +// r := mux.NewRouter() +// r.Host("www.example.com") +// r.Host("{subdomain}.domain.com") +// r.Host("{subdomain:[a-z]+}.domain.com") +// +// Variable names must be unique in a given route. They can be retrieved +// calling mux.Vars(request). +func (r *Route) Host(tpl string) *Route { + r.err = r.addRegexpMatcher(tpl, regexpTypeHost) + return r +} + +// MatcherFunc ---------------------------------------------------------------- + +// MatcherFunc is the function signature used by custom matchers. +type MatcherFunc func(*http.Request, *RouteMatch) bool + +// Match returns the match for a given request. +func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool { + return m(r, match) +} + +// MatcherFunc adds a custom function to be used as request matcher. +func (r *Route) MatcherFunc(f MatcherFunc) *Route { + return r.addMatcher(f) +} + +// Methods -------------------------------------------------------------------- + +// methodMatcher matches the request against HTTP methods. +type methodMatcher []string + +func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchInArray(m, r.Method) +} + +// Methods adds a matcher for HTTP methods. +// It accepts a sequence of one or more methods to be matched, e.g.: +// "GET", "POST", "PUT". +func (r *Route) Methods(methods ...string) *Route { + for k, v := range methods { + methods[k] = strings.ToUpper(v) + } + return r.addMatcher(methodMatcher(methods)) +} + +// Path ----------------------------------------------------------------------- + +// Path adds a matcher for the URL path. +// It accepts a template with zero or more URL variables enclosed by {}. The +// template must start with a "/". +// Variables can define an optional regexp pattern to be matched: +// +// - {name} matches anything until the next slash. +// +// - {name:pattern} matches the given regexp pattern. +// +// For example: +// +// r := mux.NewRouter() +// r.Path("/products/").Handler(ProductsHandler) +// r.Path("/products/{key}").Handler(ProductsHandler) +// r.Path("/articles/{category}/{id:[0-9]+}"). +// Handler(ArticleHandler) +// +// Variable names must be unique in a given route. They can be retrieved +// calling mux.Vars(request). +func (r *Route) Path(tpl string) *Route { + r.err = r.addRegexpMatcher(tpl, regexpTypePath) + return r +} + +// PathPrefix ----------------------------------------------------------------- + +// PathPrefix adds a matcher for the URL path prefix. This matches if the given +// template is a prefix of the full URL path. See Route.Path() for details on +// the tpl argument. +// +// Note that it does not treat slashes specially ("/foobar/" will be matched by +// the prefix "/foo") so you may want to use a trailing slash here. +// +// Also note that the setting of Router.StrictSlash() has no effect on routes +// with a PathPrefix matcher. +func (r *Route) PathPrefix(tpl string) *Route { + r.err = r.addRegexpMatcher(tpl, regexpTypePrefix) + return r +} + +// Query ---------------------------------------------------------------------- + +// Queries adds a matcher for URL query values. +// It accepts a sequence of key/value pairs. Values may define variables. +// For example: +// +// r := mux.NewRouter() +// r.Queries("foo", "bar", "id", "{id:[0-9]+}") +// +// The above route will only match if the URL contains the defined queries +// values, e.g.: ?foo=bar&id=42. +// +// It the value is an empty string, it will match any value if the key is set. +// +// Variables can define an optional regexp pattern to be matched: +// +// - {name} matches anything until the next slash. +// +// - {name:pattern} matches the given regexp pattern. +func (r *Route) Queries(pairs ...string) *Route { + length := len(pairs) + if length%2 != 0 { + r.err = fmt.Errorf( + "mux: number of parameters must be multiple of 2, got %v", pairs) + return nil + } + for i := 0; i < length; i += 2 { + if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil { + return r + } + } + + return r +} + +// Schemes -------------------------------------------------------------------- + +// schemeMatcher matches the request against URL schemes. +type schemeMatcher []string + +func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { + return matchInArray(m, r.URL.Scheme) +} + +// Schemes adds a matcher for URL schemes. +// It accepts a sequence of schemes to be matched, e.g.: "http", "https". +func (r *Route) Schemes(schemes ...string) *Route { + for k, v := range schemes { + schemes[k] = strings.ToLower(v) + } + if r.buildScheme == "" && len(schemes) > 0 { + r.buildScheme = schemes[0] + } + return r.addMatcher(schemeMatcher(schemes)) +} + +// BuildVarsFunc -------------------------------------------------------------- + +// BuildVarsFunc is the function signature used by custom build variable +// functions (which can modify route variables before a route's URL is built). +type BuildVarsFunc func(map[string]string) map[string]string + +// BuildVarsFunc adds a custom function to be used to modify build variables +// before a route's URL is built. +func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { + r.buildVarsFunc = f + return r +} + +// Subrouter ------------------------------------------------------------------ + +// Subrouter creates a subrouter for the route. +// +// It will test the inner routes only if the parent route matched. For example: +// +// r := mux.NewRouter() +// s := r.Host("www.example.com").Subrouter() +// s.HandleFunc("/products/", ProductsHandler) +// s.HandleFunc("/products/{key}", ProductHandler) +// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) +// +// Here, the routes registered in the subrouter won't be tested if the host +// doesn't match. +func (r *Route) Subrouter() *Router { + router := &Router{parent: r, strictSlash: r.strictSlash} + r.addMatcher(router) + return router +} + +// ---------------------------------------------------------------------------- +// URL building +// ---------------------------------------------------------------------------- + +// URL builds a URL for the route. +// +// It accepts a sequence of key/value pairs for the route variables. For +// example, given this route: +// +// r := mux.NewRouter() +// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Name("article") +// +// ...a URL for it can be built using: +// +// url, err := r.Get("article").URL("category", "technology", "id", "42") +// +// ...which will return an url.URL with the following path: +// +// "/articles/technology/42" +// +// This also works for host variables: +// +// r := mux.NewRouter() +// r.Host("{subdomain}.domain.com"). +// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Name("article") +// +// // url.String() will be "http://news.domain.com/articles/technology/42" +// url, err := r.Get("article").URL("subdomain", "news", +// "category", "technology", +// "id", "42") +// +// All variables defined in the route are required, and their values must +// conform to the corresponding patterns. +func (r *Route) URL(pairs ...string) (*url.URL, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil { + return nil, errors.New("mux: route doesn't have a host or path") + } + values, err := r.prepareVars(pairs...) + if err != nil { + return nil, err + } + var scheme, host, path string + queries := make([]string, 0, len(r.regexp.queries)) + if r.regexp.host != nil { + if host, err = r.regexp.host.url(values); err != nil { + return nil, err + } + scheme = "http" + if s := r.getBuildScheme(); s != "" { + scheme = s + } + } + if r.regexp.path != nil { + if path, err = r.regexp.path.url(values); err != nil { + return nil, err + } + } + for _, q := range r.regexp.queries { + var query string + if query, err = q.url(values); err != nil { + return nil, err + } + queries = append(queries, query) + } + return &url.URL{ + Scheme: scheme, + Host: host, + Path: path, + RawQuery: strings.Join(queries, "&"), + }, nil +} + +// URLHost builds the host part of the URL for a route. See Route.URL(). +// +// The route must have a host defined. +func (r *Route) URLHost(pairs ...string) (*url.URL, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.host == nil { + return nil, errors.New("mux: route doesn't have a host") + } + values, err := r.prepareVars(pairs...) + if err != nil { + return nil, err + } + host, err := r.regexp.host.url(values) + if err != nil { + return nil, err + } + u := &url.URL{ + Scheme: "http", + Host: host, + } + if s := r.getBuildScheme(); s != "" { + u.Scheme = s + } + return u, nil +} + +// URLPath builds the path part of the URL for a route. See Route.URL(). +// +// The route must have a path defined. +func (r *Route) URLPath(pairs ...string) (*url.URL, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.path == nil { + return nil, errors.New("mux: route doesn't have a path") + } + values, err := r.prepareVars(pairs...) + if err != nil { + return nil, err + } + path, err := r.regexp.path.url(values) + if err != nil { + return nil, err + } + return &url.URL{ + Path: path, + }, nil +} + +// GetPathTemplate returns the template used to build the +// route match. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not define a path. +func (r *Route) GetPathTemplate() (string, error) { + if r.err != nil { + return "", r.err + } + if r.regexp == nil || r.regexp.path == nil { + return "", errors.New("mux: route doesn't have a path") + } + return r.regexp.path.template, nil +} + +// GetPathRegexp returns the expanded regular expression used to match route path. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not define a path. +func (r *Route) GetPathRegexp() (string, error) { + if r.err != nil { + return "", r.err + } + if r.regexp == nil || r.regexp.path == nil { + return "", errors.New("mux: route does not have a path") + } + return r.regexp.path.regexp.String(), nil +} + +// GetQueriesRegexp returns the expanded regular expressions used to match the +// route queries. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An empty list will be returned if the route does not have queries. +func (r *Route) GetQueriesRegexp() ([]string, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.queries == nil { + return nil, errors.New("mux: route doesn't have queries") + } + var queries []string + for _, query := range r.regexp.queries { + queries = append(queries, query.regexp.String()) + } + return queries, nil +} + +// GetQueriesTemplates returns the templates used to build the +// query matching. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An empty list will be returned if the route does not define queries. +func (r *Route) GetQueriesTemplates() ([]string, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp == nil || r.regexp.queries == nil { + return nil, errors.New("mux: route doesn't have queries") + } + var queries []string + for _, query := range r.regexp.queries { + queries = append(queries, query.template) + } + return queries, nil +} + +// GetMethods returns the methods the route matches against +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An empty list will be returned if route does not have methods. +func (r *Route) GetMethods() ([]string, error) { + if r.err != nil { + return nil, r.err + } + for _, m := range r.matchers { + if methods, ok := m.(methodMatcher); ok { + return []string(methods), nil + } + } + return nil, nil +} + +// GetHostTemplate returns the template used to build the +// route match. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not define a host. +func (r *Route) GetHostTemplate() (string, error) { + if r.err != nil { + return "", r.err + } + if r.regexp == nil || r.regexp.host == nil { + return "", errors.New("mux: route doesn't have a host") + } + return r.regexp.host.template, nil +} + +// prepareVars converts the route variable pairs into a map. If the route has a +// BuildVarsFunc, it is invoked. +func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { + m, err := mapFromPairsToString(pairs...) + if err != nil { + return nil, err + } + return r.buildVars(m), nil +} + +func (r *Route) buildVars(m map[string]string) map[string]string { + if r.parent != nil { + m = r.parent.buildVars(m) + } + if r.buildVarsFunc != nil { + m = r.buildVarsFunc(m) + } + return m +} + +// ---------------------------------------------------------------------------- +// parentRoute +// ---------------------------------------------------------------------------- + +// parentRoute allows routes to know about parent host and path definitions. +type parentRoute interface { + getBuildScheme() string + getNamedRoutes() map[string]*Route + getRegexpGroup() *routeRegexpGroup + buildVars(map[string]string) map[string]string +} + +func (r *Route) getBuildScheme() string { + if r.buildScheme != "" { + return r.buildScheme + } + if r.parent != nil { + return r.parent.getBuildScheme() + } + return "" +} + +// getNamedRoutes returns the map where named routes are registered. +func (r *Route) getNamedRoutes() map[string]*Route { + if r.parent == nil { + // During tests router is not always set. + r.parent = NewRouter() + } + return r.parent.getNamedRoutes() +} + +// getRegexpGroup returns regexp definitions from this route. +func (r *Route) getRegexpGroup() *routeRegexpGroup { + if r.regexp == nil { + if r.parent == nil { + // During tests router is not always set. + r.parent = NewRouter() + } + regexp := r.parent.getRegexpGroup() + if regexp == nil { + r.regexp = new(routeRegexpGroup) + } else { + // Copy. + r.regexp = &routeRegexpGroup{ + host: regexp.host, + path: regexp.path, + queries: regexp.queries, + } + } + } + return r.regexp +} diff --git a/vendor/github.com/gorilla/mux/test_helpers.go b/vendor/github.com/gorilla/mux/test_helpers.go new file mode 100644 index 00000000..8b2c4a4c --- /dev/null +++ b/vendor/github.com/gorilla/mux/test_helpers.go @@ -0,0 +1,18 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import "net/http" + +// SetURLVars sets the URL variables for the given request, to be accessed via +// mux.Vars for testing route behaviour. +// +// This API should only be used for testing purposes; it provides a way to +// inject variables into the request context. Alternatively, URL variables +// can be set by making a route that captures the required variables, +// starting a server and sending the request to that server. +func SetURLVars(r *http.Request, val map[string]string) *http.Request { + return setVars(r, val) +} diff --git a/vendor/github.com/gorilla/websocket/.gitignore b/vendor/github.com/gorilla/websocket/.gitignore new file mode 100644 index 00000000..ac710204 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/.gitignore @@ -0,0 +1,25 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +.idea/ +*.iml \ No newline at end of file diff --git a/vendor/github.com/gorilla/websocket/.travis.yml b/vendor/github.com/gorilla/websocket/.travis.yml new file mode 100644 index 00000000..3d8d29cf --- /dev/null +++ b/vendor/github.com/gorilla/websocket/.travis.yml @@ -0,0 +1,19 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: 1.7 + - go: 1.8 + - go: tip + allow_failures: + - go: tip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS new file mode 100644 index 00000000..b003eca0 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/AUTHORS @@ -0,0 +1,8 @@ +# This is the official list of Gorilla WebSocket authors for copyright +# purposes. +# +# Please keep the list sorted. + +Gary Burd +Joachim Bauch + diff --git a/vendor/github.com/gorilla/websocket/LICENSE b/vendor/github.com/gorilla/websocket/LICENSE new file mode 100644 index 00000000..9171c972 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md new file mode 100644 index 00000000..33c3d2be --- /dev/null +++ b/vendor/github.com/gorilla/websocket/README.md @@ -0,0 +1,64 @@ +# Gorilla WebSocket + +Gorilla WebSocket is a [Go](http://golang.org/) implementation of the +[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. + +[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) +[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) + +### Documentation + +* [API Reference](http://godoc.org/github.com/gorilla/websocket) +* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) +* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) +* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) +* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) + +### Status + +The Gorilla WebSocket package provides a complete and tested implementation of +the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The +package API is stable. + +### Installation + + go get github.com/gorilla/websocket + +### Protocol Compliance + +The Gorilla WebSocket package passes the server tests in the [Autobahn Test +Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn +subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). + +### Gorilla WebSocket compared with other packages + + + + + + + + + + + + + + + + + + +
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
+ +Notes: + +1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). +2. The application can get the type of a received data message by implementing + a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) + function. +3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. + Read returns when the input buffer is full or a frame boundary is + encountered. Each call to Write sends a single frame message. The Gorilla + io.Reader and io.WriteCloser operate on a single WebSocket message. + diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go new file mode 100644 index 00000000..43a87c75 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client.go @@ -0,0 +1,392 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "bytes" + "crypto/tls" + "encoding/base64" + "errors" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +// ErrBadHandshake is returned when the server response to opening handshake is +// invalid. +var ErrBadHandshake = errors.New("websocket: bad handshake") + +var errInvalidCompression = errors.New("websocket: invalid compression negotiation") + +// NewClient creates a new client connection using the given net connection. +// The URL u specifies the host and request URI. Use requestHeader to specify +// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies +// (Cookie). Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etc. +// +// Deprecated: Use Dialer instead. +func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { + d := Dialer{ + ReadBufferSize: readBufSize, + WriteBufferSize: writeBufSize, + NetDial: func(net, addr string) (net.Conn, error) { + return netConn, nil + }, + } + return d.Dial(u.String(), requestHeader) +} + +// A Dialer contains options for connecting to WebSocket server. +type Dialer struct { + // NetDial specifies the dial function for creating TCP connections. If + // NetDial is nil, net.Dial is used. + NetDial func(network, addr string) (net.Conn, error) + + // Proxy specifies a function to return a proxy for a given + // Request. If the function returns a non-nil error, the + // request is aborted with the provided error. + // If Proxy is nil or returns a nil *URL, no proxy is used. + Proxy func(*http.Request) (*url.URL, error) + + // TLSClientConfig specifies the TLS configuration to use with tls.Client. + // If nil, the default configuration is used. + TLSClientConfig *tls.Config + + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // size is zero, then a useful default size is used. The I/O buffer sizes + // do not limit the size of the messages that can be sent or received. + ReadBufferSize, WriteBufferSize int + + // Subprotocols specifies the client's requested subprotocols. + Subprotocols []string + + // EnableCompression specifies if the client should attempt to negotiate + // per message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool + + // Jar specifies the cookie jar. + // If Jar is nil, cookies are not sent in requests and ignored + // in responses. + Jar http.CookieJar +} + +var errMalformedURL = errors.New("malformed ws or wss URL") + +// parseURL parses the URL. +// +// This function is a replacement for the standard library url.Parse function. +// In Go 1.4 and earlier, url.Parse loses information from the path. +func parseURL(s string) (*url.URL, error) { + // From the RFC: + // + // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] + // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] + var u url.URL + switch { + case strings.HasPrefix(s, "ws://"): + u.Scheme = "ws" + s = s[len("ws://"):] + case strings.HasPrefix(s, "wss://"): + u.Scheme = "wss" + s = s[len("wss://"):] + default: + return nil, errMalformedURL + } + + if i := strings.Index(s, "?"); i >= 0 { + u.RawQuery = s[i+1:] + s = s[:i] + } + + if i := strings.Index(s, "/"); i >= 0 { + u.Opaque = s[i:] + s = s[:i] + } else { + u.Opaque = "/" + } + + u.Host = s + + if strings.Contains(u.Host, "@") { + // Don't bother parsing user information because user information is + // not allowed in websocket URIs. + return nil, errMalformedURL + } + + return &u, nil +} + +func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { + hostPort = u.Host + hostNoPort = u.Host + if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { + hostNoPort = hostNoPort[:i] + } else { + switch u.Scheme { + case "wss": + hostPort += ":443" + case "https": + hostPort += ":443" + default: + hostPort += ":80" + } + } + return hostPort, hostNoPort +} + +// DefaultDialer is a dialer with all fields set to the default zero values. +var DefaultDialer = &Dialer{ + Proxy: http.ProxyFromEnvironment, +} + +// Dial creates a new client connection. Use requestHeader to specify the +// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). +// Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etcetera. The response body may not contain the entire response and does not +// need to be closed by the application. +func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { + + if d == nil { + d = &Dialer{ + Proxy: http.ProxyFromEnvironment, + } + } + + challengeKey, err := generateChallengeKey() + if err != nil { + return nil, nil, err + } + + u, err := parseURL(urlStr) + if err != nil { + return nil, nil, err + } + + switch u.Scheme { + case "ws": + u.Scheme = "http" + case "wss": + u.Scheme = "https" + default: + return nil, nil, errMalformedURL + } + + if u.User != nil { + // User name and password are not allowed in websocket URIs. + return nil, nil, errMalformedURL + } + + req := &http.Request{ + Method: "GET", + URL: u, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Host: u.Host, + } + + // Set the cookies present in the cookie jar of the dialer + if d.Jar != nil { + for _, cookie := range d.Jar.Cookies(u) { + req.AddCookie(cookie) + } + } + + // Set the request headers using the capitalization for names and values in + // RFC examples. Although the capitalization shouldn't matter, there are + // servers that depend on it. The Header.Set method is not used because the + // method canonicalizes the header names. + req.Header["Upgrade"] = []string{"websocket"} + req.Header["Connection"] = []string{"Upgrade"} + req.Header["Sec-WebSocket-Key"] = []string{challengeKey} + req.Header["Sec-WebSocket-Version"] = []string{"13"} + if len(d.Subprotocols) > 0 { + req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} + } + for k, vs := range requestHeader { + switch { + case k == "Host": + if len(vs) > 0 { + req.Host = vs[0] + } + case k == "Upgrade" || + k == "Connection" || + k == "Sec-Websocket-Key" || + k == "Sec-Websocket-Version" || + k == "Sec-Websocket-Extensions" || + (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): + return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) + default: + req.Header[k] = vs + } + } + + if d.EnableCompression { + req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover") + } + + hostPort, hostNoPort := hostPortNoPort(u) + + var proxyURL *url.URL + // Check wether the proxy method has been configured + if d.Proxy != nil { + proxyURL, err = d.Proxy(req) + } + if err != nil { + return nil, nil, err + } + + var targetHostPort string + if proxyURL != nil { + targetHostPort, _ = hostPortNoPort(proxyURL) + } else { + targetHostPort = hostPort + } + + var deadline time.Time + if d.HandshakeTimeout != 0 { + deadline = time.Now().Add(d.HandshakeTimeout) + } + + netDial := d.NetDial + if netDial == nil { + netDialer := &net.Dialer{Deadline: deadline} + netDial = netDialer.Dial + } + + netConn, err := netDial("tcp", targetHostPort) + if err != nil { + return nil, nil, err + } + + defer func() { + if netConn != nil { + netConn.Close() + } + }() + + if err := netConn.SetDeadline(deadline); err != nil { + return nil, nil, err + } + + if proxyURL != nil { + connectHeader := make(http.Header) + if user := proxyURL.User; user != nil { + proxyUser := user.Username() + if proxyPassword, passwordSet := user.Password(); passwordSet { + credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) + connectHeader.Set("Proxy-Authorization", "Basic "+credential) + } + } + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: hostPort}, + Host: hostPort, + Header: connectHeader, + } + + connectReq.Write(netConn) + + // Read response. + // Okay to use and discard buffered reader here, because + // TLS server will not speak until spoken to. + br := bufio.NewReader(netConn) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + return nil, nil, err + } + if resp.StatusCode != 200 { + f := strings.SplitN(resp.Status, " ", 2) + return nil, nil, errors.New(f[1]) + } + } + + if u.Scheme == "https" { + cfg := cloneTLSConfig(d.TLSClientConfig) + if cfg.ServerName == "" { + cfg.ServerName = hostNoPort + } + tlsConn := tls.Client(netConn, cfg) + netConn = tlsConn + if err := tlsConn.Handshake(); err != nil { + return nil, nil, err + } + if !cfg.InsecureSkipVerify { + if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { + return nil, nil, err + } + } + } + + conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) + + if err := req.Write(netConn); err != nil { + return nil, nil, err + } + + resp, err := http.ReadResponse(conn.br, req) + if err != nil { + return nil, nil, err + } + + if d.Jar != nil { + if rc := resp.Cookies(); len(rc) > 0 { + d.Jar.SetCookies(u, rc) + } + } + + if resp.StatusCode != 101 || + !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || + !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || + resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { + // Before closing the network connection on return from this + // function, slurp up some of the response to aid application + // debugging. + buf := make([]byte, 1024) + n, _ := io.ReadFull(resp.Body, buf) + resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) + return nil, resp, ErrBadHandshake + } + + for _, ext := range parseExtensions(resp.Header) { + if ext[""] != "permessage-deflate" { + continue + } + _, snct := ext["server_no_context_takeover"] + _, cnct := ext["client_no_context_takeover"] + if !snct || !cnct { + return nil, resp, errInvalidCompression + } + conn.newCompressionWriter = compressNoContextTakeover + conn.newDecompressionReader = decompressNoContextTakeover + break + } + + resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) + conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") + + netConn.SetDeadline(time.Time{}) + netConn = nil // to avoid close in defer. + return conn, resp, nil +} diff --git a/vendor/github.com/gorilla/websocket/client_clone.go b/vendor/github.com/gorilla/websocket/client_clone.go new file mode 100644 index 00000000..4f0d9437 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_clone.go @@ -0,0 +1,16 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.8 + +package websocket + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return cfg.Clone() +} diff --git a/vendor/github.com/gorilla/websocket/client_clone_legacy.go b/vendor/github.com/gorilla/websocket/client_clone_legacy.go new file mode 100644 index 00000000..babb007f --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_clone_legacy.go @@ -0,0 +1,38 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8 + +package websocket + +import "crypto/tls" + +// cloneTLSConfig clones all public fields except the fields +// SessionTicketsDisabled and SessionTicketKey. This avoids copying the +// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a +// config in active use. +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return &tls.Config{ + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + } +} diff --git a/vendor/github.com/gorilla/websocket/client_server_test.go b/vendor/github.com/gorilla/websocket/client_server_test.go new file mode 100644 index 00000000..7d39da68 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_server_test.go @@ -0,0 +1,512 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "crypto/tls" + "crypto/x509" + "encoding/base64" + "io" + "io/ioutil" + "net/http" + "net/http/cookiejar" + "net/http/httptest" + "net/url" + "reflect" + "strings" + "testing" + "time" +) + +var cstUpgrader = Upgrader{ + Subprotocols: []string{"p0", "p1"}, + ReadBufferSize: 1024, + WriteBufferSize: 1024, + EnableCompression: true, + Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) { + http.Error(w, reason.Error(), status) + }, +} + +var cstDialer = Dialer{ + Subprotocols: []string{"p1", "p2"}, + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +type cstHandler struct{ *testing.T } + +type cstServer struct { + *httptest.Server + URL string +} + +const ( + cstPath = "/a/b" + cstRawQuery = "x=y" + cstRequestURI = cstPath + "?" + cstRawQuery +) + +func newServer(t *testing.T) *cstServer { + var s cstServer + s.Server = httptest.NewServer(cstHandler{t}) + s.Server.URL += cstRequestURI + s.URL = makeWsProto(s.Server.URL) + return &s +} + +func newTLSServer(t *testing.T) *cstServer { + var s cstServer + s.Server = httptest.NewTLSServer(cstHandler{t}) + s.Server.URL += cstRequestURI + s.URL = makeWsProto(s.Server.URL) + return &s +} + +func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != cstPath { + t.Logf("path=%v, want %v", r.URL.Path, cstPath) + http.Error(w, "bad path", 400) + return + } + if r.URL.RawQuery != cstRawQuery { + t.Logf("query=%v, want %v", r.URL.RawQuery, cstRawQuery) + http.Error(w, "bad path", 400) + return + } + subprotos := Subprotocols(r) + if !reflect.DeepEqual(subprotos, cstDialer.Subprotocols) { + t.Logf("subprotols=%v, want %v", subprotos, cstDialer.Subprotocols) + http.Error(w, "bad protocol", 400) + return + } + ws, err := cstUpgrader.Upgrade(w, r, http.Header{"Set-Cookie": {"sessionID=1234"}}) + if err != nil { + t.Logf("Upgrade: %v", err) + return + } + defer ws.Close() + + if ws.Subprotocol() != "p1" { + t.Logf("Subprotocol() = %s, want p1", ws.Subprotocol()) + ws.Close() + return + } + op, rd, err := ws.NextReader() + if err != nil { + t.Logf("NextReader: %v", err) + return + } + wr, err := ws.NextWriter(op) + if err != nil { + t.Logf("NextWriter: %v", err) + return + } + if _, err = io.Copy(wr, rd); err != nil { + t.Logf("NextWriter: %v", err) + return + } + if err := wr.Close(); err != nil { + t.Logf("Close: %v", err) + return + } +} + +func makeWsProto(s string) string { + return "ws" + strings.TrimPrefix(s, "http") +} + +func sendRecv(t *testing.T, ws *Conn) { + const message = "Hello World!" + if err := ws.SetWriteDeadline(time.Now().Add(time.Second)); err != nil { + t.Fatalf("SetWriteDeadline: %v", err) + } + if err := ws.WriteMessage(TextMessage, []byte(message)); err != nil { + t.Fatalf("WriteMessage: %v", err) + } + if err := ws.SetReadDeadline(time.Now().Add(time.Second)); err != nil { + t.Fatalf("SetReadDeadline: %v", err) + } + _, p, err := ws.ReadMessage() + if err != nil { + t.Fatalf("ReadMessage: %v", err) + } + if string(p) != message { + t.Fatalf("message=%s, want %s", p, message) + } +} + +func TestProxyDial(t *testing.T) { + + s := newServer(t) + defer s.Close() + + surl, _ := url.Parse(s.URL) + + cstDialer.Proxy = http.ProxyURL(surl) + + connect := false + origHandler := s.Server.Config.Handler + + // Capture the request Host header. + s.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if r.Method == "CONNECT" { + connect = true + w.WriteHeader(200) + return + } + + if !connect { + t.Log("connect not recieved") + http.Error(w, "connect not recieved", 405) + return + } + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) + + cstDialer.Proxy = http.ProxyFromEnvironment +} + +func TestProxyAuthorizationDial(t *testing.T) { + s := newServer(t) + defer s.Close() + + surl, _ := url.Parse(s.URL) + surl.User = url.UserPassword("username", "password") + cstDialer.Proxy = http.ProxyURL(surl) + + connect := false + origHandler := s.Server.Config.Handler + + // Capture the request Host header. + s.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + proxyAuth := r.Header.Get("Proxy-Authorization") + expectedProxyAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte("username:password")) + if r.Method == "CONNECT" && proxyAuth == expectedProxyAuth { + connect = true + w.WriteHeader(200) + return + } + + if !connect { + t.Log("connect with proxy authorization not recieved") + http.Error(w, "connect with proxy authorization not recieved", 405) + return + } + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) + + cstDialer.Proxy = http.ProxyFromEnvironment +} + +func TestDial(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} + +func TestDialCookieJar(t *testing.T) { + s := newServer(t) + defer s.Close() + + jar, _ := cookiejar.New(nil) + d := cstDialer + d.Jar = jar + + u, _ := parseURL(s.URL) + + switch u.Scheme { + case "ws": + u.Scheme = "http" + case "wss": + u.Scheme = "https" + } + + cookies := []*http.Cookie{&http.Cookie{Name: "gorilla", Value: "ws", Path: "/"}} + d.Jar.SetCookies(u, cookies) + + ws, _, err := d.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + + var gorilla string + var sessionID string + for _, c := range d.Jar.Cookies(u) { + if c.Name == "gorilla" { + gorilla = c.Value + } + + if c.Name == "sessionID" { + sessionID = c.Value + } + } + if gorilla != "ws" { + t.Error("Cookie not present in jar.") + } + + if sessionID != "1234" { + t.Error("Set-Cookie not received from the server.") + } + + sendRecv(t, ws) +} + +func TestDialTLS(t *testing.T) { + s := newTLSServer(t) + defer s.Close() + + certs := x509.NewCertPool() + for _, c := range s.TLS.Certificates { + roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1]) + if err != nil { + t.Fatalf("error parsing server's root cert: %v", err) + } + for _, root := range roots { + certs.AddCert(root) + } + } + + d := cstDialer + d.TLSClientConfig = &tls.Config{RootCAs: certs} + ws, _, err := d.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} + +func xTestDialTLSBadCert(t *testing.T) { + // This test is deactivated because of noisy logging from the net/http package. + s := newTLSServer(t) + defer s.Close() + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } +} + +func TestDialTLSNoVerify(t *testing.T) { + s := newTLSServer(t) + defer s.Close() + + d := cstDialer + d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + ws, _, err := d.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} + +func TestDialTimeout(t *testing.T) { + s := newServer(t) + defer s.Close() + + d := cstDialer + d.HandshakeTimeout = -1 + ws, _, err := d.Dial(s.URL, nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } +} + +func TestDialBadScheme(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, _, err := cstDialer.Dial(s.Server.URL, nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } +} + +func TestDialBadOrigin(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}}) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } + if resp == nil { + t.Fatalf("resp=nil, err=%v", err) + } + if resp.StatusCode != http.StatusForbidden { + t.Fatalf("status=%d, want %d", resp.StatusCode, http.StatusForbidden) + } +} + +func TestDialBadHeader(t *testing.T) { + s := newServer(t) + defer s.Close() + + for _, k := range []string{"Upgrade", + "Connection", + "Sec-Websocket-Key", + "Sec-Websocket-Version", + "Sec-Websocket-Protocol"} { + h := http.Header{} + h.Set(k, "bad") + ws, _, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}}) + if err == nil { + ws.Close() + t.Errorf("Dial with header %s returned nil", k) + } + } +} + +func TestBadMethod(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ws, err := cstUpgrader.Upgrade(w, r, nil) + if err == nil { + t.Errorf("handshake succeeded, expect fail") + ws.Close() + } + })) + defer s.Close() + + resp, err := http.PostForm(s.URL, url.Values{}) + if err != nil { + t.Fatalf("PostForm returned error %v", err) + } + resp.Body.Close() + if resp.StatusCode != http.StatusMethodNotAllowed { + t.Errorf("Status = %d, want %d", resp.StatusCode, http.StatusMethodNotAllowed) + } +} + +func TestHandshake(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {s.URL}}) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + + var sessionID string + for _, c := range resp.Cookies() { + if c.Name == "sessionID" { + sessionID = c.Value + } + } + if sessionID != "1234" { + t.Error("Set-Cookie not received from the server.") + } + + if ws.Subprotocol() != "p1" { + t.Errorf("ws.Subprotocol() = %s, want p1", ws.Subprotocol()) + } + sendRecv(t, ws) +} + +func TestRespOnBadHandshake(t *testing.T) { + const expectedStatus = http.StatusGone + const expectedBody = "This is the response body." + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(expectedStatus) + io.WriteString(w, expectedBody) + })) + defer s.Close() + + ws, resp, err := cstDialer.Dial(makeWsProto(s.URL), nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } + + if resp == nil { + t.Fatalf("resp=nil, err=%v", err) + } + + if resp.StatusCode != expectedStatus { + t.Errorf("resp.StatusCode=%d, want %d", resp.StatusCode, expectedStatus) + } + + p, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("ReadFull(resp.Body) returned error %v", err) + } + + if string(p) != expectedBody { + t.Errorf("resp.Body=%s, want %s", p, expectedBody) + } +} + +// TestHostHeader confirms that the host header provided in the call to Dial is +// sent to the server. +func TestHostHeader(t *testing.T) { + s := newServer(t) + defer s.Close() + + specifiedHost := make(chan string, 1) + origHandler := s.Server.Config.Handler + + // Capture the request Host header. + s.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + specifiedHost <- r.Host + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, http.Header{"Host": {"testhost"}}) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + + if gotHost := <-specifiedHost; gotHost != "testhost" { + t.Fatalf("gotHost = %q, want \"testhost\"", gotHost) + } + + sendRecv(t, ws) +} + +func TestDialCompression(t *testing.T) { + s := newServer(t) + defer s.Close() + + dialer := cstDialer + dialer.EnableCompression = true + ws, _, err := dialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} diff --git a/vendor/github.com/gorilla/websocket/client_test.go b/vendor/github.com/gorilla/websocket/client_test.go new file mode 100644 index 00000000..7d2b0844 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_test.go @@ -0,0 +1,72 @@ +// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "net/url" + "reflect" + "testing" +) + +var parseURLTests = []struct { + s string + u *url.URL + rui string +}{ + {"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"}, + {"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"}, + {"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}, "/"}, + {"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}, "/"}, + {"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}, "/a/b"}, + {"ss://example.com/a/b", nil, ""}, + {"ws://webmaster@example.com/", nil, ""}, + {"wss://example.com/a/b?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b", RawQuery: "x=y"}, "/a/b?x=y"}, + {"wss://example.com?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/", RawQuery: "x=y"}, "/?x=y"}, +} + +func TestParseURL(t *testing.T) { + for _, tt := range parseURLTests { + u, err := parseURL(tt.s) + if tt.u != nil && err != nil { + t.Errorf("parseURL(%q) returned error %v", tt.s, err) + continue + } + if tt.u == nil { + if err == nil { + t.Errorf("parseURL(%q) did not return error", tt.s) + } + continue + } + if !reflect.DeepEqual(u, tt.u) { + t.Errorf("parseURL(%q) = %v, want %v", tt.s, u, tt.u) + continue + } + if u.RequestURI() != tt.rui { + t.Errorf("parseURL(%q).RequestURI() = %v, want %v", tt.s, u.RequestURI(), tt.rui) + } + } +} + +var hostPortNoPortTests = []struct { + u *url.URL + hostPort, hostNoPort string +}{ + {&url.URL{Scheme: "ws", Host: "example.com"}, "example.com:80", "example.com"}, + {&url.URL{Scheme: "wss", Host: "example.com"}, "example.com:443", "example.com"}, + {&url.URL{Scheme: "ws", Host: "example.com:7777"}, "example.com:7777", "example.com"}, + {&url.URL{Scheme: "wss", Host: "example.com:7777"}, "example.com:7777", "example.com"}, +} + +func TestHostPortNoPort(t *testing.T) { + for _, tt := range hostPortNoPortTests { + hostPort, hostNoPort := hostPortNoPort(tt.u) + if hostPort != tt.hostPort { + t.Errorf("hostPortNoPort(%v) returned hostPort %q, want %q", tt.u, hostPort, tt.hostPort) + } + if hostNoPort != tt.hostNoPort { + t.Errorf("hostPortNoPort(%v) returned hostNoPort %q, want %q", tt.u, hostNoPort, tt.hostNoPort) + } + } +} diff --git a/vendor/github.com/gorilla/websocket/compression.go b/vendor/github.com/gorilla/websocket/compression.go new file mode 100644 index 00000000..813ffb1e --- /dev/null +++ b/vendor/github.com/gorilla/websocket/compression.go @@ -0,0 +1,148 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "compress/flate" + "errors" + "io" + "strings" + "sync" +) + +const ( + minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 + maxCompressionLevel = flate.BestCompression + defaultCompressionLevel = 1 +) + +var ( + flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool + flateReaderPool = sync.Pool{New: func() interface{} { + return flate.NewReader(nil) + }} +) + +func decompressNoContextTakeover(r io.Reader) io.ReadCloser { + const tail = + // Add four bytes as specified in RFC + "\x00\x00\xff\xff" + + // Add final block to squelch unexpected EOF error from flate reader. + "\x01\x00\x00\xff\xff" + + fr, _ := flateReaderPool.Get().(io.ReadCloser) + fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) + return &flateReadWrapper{fr} +} + +func isValidCompressionLevel(level int) bool { + return minCompressionLevel <= level && level <= maxCompressionLevel +} + +func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { + p := &flateWriterPools[level-minCompressionLevel] + tw := &truncWriter{w: w} + fw, _ := p.Get().(*flate.Writer) + if fw == nil { + fw, _ = flate.NewWriter(tw, level) + } else { + fw.Reset(tw) + } + return &flateWriteWrapper{fw: fw, tw: tw, p: p} +} + +// truncWriter is an io.Writer that writes all but the last four bytes of the +// stream to another io.Writer. +type truncWriter struct { + w io.WriteCloser + n int + p [4]byte +} + +func (w *truncWriter) Write(p []byte) (int, error) { + n := 0 + + // fill buffer first for simplicity. + if w.n < len(w.p) { + n = copy(w.p[w.n:], p) + p = p[n:] + w.n += n + if len(p) == 0 { + return n, nil + } + } + + m := len(p) + if m > len(w.p) { + m = len(w.p) + } + + if nn, err := w.w.Write(w.p[:m]); err != nil { + return n + nn, err + } + + copy(w.p[:], w.p[m:]) + copy(w.p[len(w.p)-m:], p[len(p)-m:]) + nn, err := w.w.Write(p[:len(p)-m]) + return n + nn, err +} + +type flateWriteWrapper struct { + fw *flate.Writer + tw *truncWriter + p *sync.Pool +} + +func (w *flateWriteWrapper) Write(p []byte) (int, error) { + if w.fw == nil { + return 0, errWriteClosed + } + return w.fw.Write(p) +} + +func (w *flateWriteWrapper) Close() error { + if w.fw == nil { + return errWriteClosed + } + err1 := w.fw.Flush() + w.p.Put(w.fw) + w.fw = nil + if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { + return errors.New("websocket: internal error, unexpected bytes at end of flate stream") + } + err2 := w.tw.w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +type flateReadWrapper struct { + fr io.ReadCloser +} + +func (r *flateReadWrapper) Read(p []byte) (int, error) { + if r.fr == nil { + return 0, io.ErrClosedPipe + } + n, err := r.fr.Read(p) + if err == io.EOF { + // Preemptively place the reader back in the pool. This helps with + // scenarios where the application does not call NextReader() soon after + // this final read. + r.Close() + } + return n, err +} + +func (r *flateReadWrapper) Close() error { + if r.fr == nil { + return io.ErrClosedPipe + } + err := r.fr.Close() + flateReaderPool.Put(r.fr) + r.fr = nil + return err +} diff --git a/vendor/github.com/gorilla/websocket/compression_test.go b/vendor/github.com/gorilla/websocket/compression_test.go new file mode 100644 index 00000000..659cf421 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/compression_test.go @@ -0,0 +1,80 @@ +package websocket + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "testing" +) + +type nopCloser struct{ io.Writer } + +func (nopCloser) Close() error { return nil } + +func TestTruncWriter(t *testing.T) { + const data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijlkmnopqrstuvwxyz987654321" + for n := 1; n <= 10; n++ { + var b bytes.Buffer + w := &truncWriter{w: nopCloser{&b}} + p := []byte(data) + for len(p) > 0 { + m := len(p) + if m > n { + m = n + } + w.Write(p[:m]) + p = p[m:] + } + if b.String() != data[:len(data)-len(w.p)] { + t.Errorf("%d: %q", n, b.String()) + } + } +} + +func textMessages(num int) [][]byte { + messages := make([][]byte, num) + for i := 0; i < num; i++ { + msg := fmt.Sprintf("planet: %d, country: %d, city: %d, street: %d", i, i, i, i) + messages[i] = []byte(msg) + } + return messages +} + +func BenchmarkWriteNoCompression(b *testing.B) { + w := ioutil.Discard + c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024) + messages := textMessages(100) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.WriteMessage(TextMessage, messages[i%len(messages)]) + } + b.ReportAllocs() +} + +func BenchmarkWriteWithCompression(b *testing.B) { + w := ioutil.Discard + c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024) + messages := textMessages(100) + c.enableWriteCompression = true + c.newCompressionWriter = compressNoContextTakeover + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.WriteMessage(TextMessage, messages[i%len(messages)]) + } + b.ReportAllocs() +} + +func TestValidCompressionLevel(t *testing.T) { + c := newConn(fakeNetConn{}, false, 1024, 1024) + for _, level := range []int{minCompressionLevel - 1, maxCompressionLevel + 1} { + if err := c.SetCompressionLevel(level); err == nil { + t.Errorf("no error for level %d", level) + } + } + for _, level := range []int{minCompressionLevel, maxCompressionLevel} { + if err := c.SetCompressionLevel(level); err != nil { + t.Errorf("error for level %d", level) + } + } +} diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go new file mode 100644 index 00000000..97e1dbac --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn.go @@ -0,0 +1,1149 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "encoding/binary" + "errors" + "io" + "io/ioutil" + "math/rand" + "net" + "strconv" + "sync" + "time" + "unicode/utf8" +) + +const ( + // Frame header byte 0 bits from Section 5.2 of RFC 6455 + finalBit = 1 << 7 + rsv1Bit = 1 << 6 + rsv2Bit = 1 << 5 + rsv3Bit = 1 << 4 + + // Frame header byte 1 bits from Section 5.2 of RFC 6455 + maskBit = 1 << 7 + + maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask + maxControlFramePayloadSize = 125 + + writeWait = time.Second + + defaultReadBufferSize = 4096 + defaultWriteBufferSize = 4096 + + continuationFrame = 0 + noFrame = -1 +) + +// Close codes defined in RFC 6455, section 11.7. +const ( + CloseNormalClosure = 1000 + CloseGoingAway = 1001 + CloseProtocolError = 1002 + CloseUnsupportedData = 1003 + CloseNoStatusReceived = 1005 + CloseAbnormalClosure = 1006 + CloseInvalidFramePayloadData = 1007 + ClosePolicyViolation = 1008 + CloseMessageTooBig = 1009 + CloseMandatoryExtension = 1010 + CloseInternalServerErr = 1011 + CloseServiceRestart = 1012 + CloseTryAgainLater = 1013 + CloseTLSHandshake = 1015 +) + +// The message types are defined in RFC 6455, section 11.8. +const ( + // TextMessage denotes a text data message. The text message payload is + // interpreted as UTF-8 encoded text data. + TextMessage = 1 + + // BinaryMessage denotes a binary data message. + BinaryMessage = 2 + + // CloseMessage denotes a close control message. The optional message + // payload contains a numeric code and text. Use the FormatCloseMessage + // function to format a close message payload. + CloseMessage = 8 + + // PingMessage denotes a ping control message. The optional message payload + // is UTF-8 encoded text. + PingMessage = 9 + + // PongMessage denotes a ping control message. The optional message payload + // is UTF-8 encoded text. + PongMessage = 10 +) + +// ErrCloseSent is returned when the application writes a message to the +// connection after sending a close message. +var ErrCloseSent = errors.New("websocket: close sent") + +// ErrReadLimit is returned when reading a message that is larger than the +// read limit set for the connection. +var ErrReadLimit = errors.New("websocket: read limit exceeded") + +// netError satisfies the net Error interface. +type netError struct { + msg string + temporary bool + timeout bool +} + +func (e *netError) Error() string { return e.msg } +func (e *netError) Temporary() bool { return e.temporary } +func (e *netError) Timeout() bool { return e.timeout } + +// CloseError represents close frame. +type CloseError struct { + + // Code is defined in RFC 6455, section 11.7. + Code int + + // Text is the optional text payload. + Text string +} + +func (e *CloseError) Error() string { + s := []byte("websocket: close ") + s = strconv.AppendInt(s, int64(e.Code), 10) + switch e.Code { + case CloseNormalClosure: + s = append(s, " (normal)"...) + case CloseGoingAway: + s = append(s, " (going away)"...) + case CloseProtocolError: + s = append(s, " (protocol error)"...) + case CloseUnsupportedData: + s = append(s, " (unsupported data)"...) + case CloseNoStatusReceived: + s = append(s, " (no status)"...) + case CloseAbnormalClosure: + s = append(s, " (abnormal closure)"...) + case CloseInvalidFramePayloadData: + s = append(s, " (invalid payload data)"...) + case ClosePolicyViolation: + s = append(s, " (policy violation)"...) + case CloseMessageTooBig: + s = append(s, " (message too big)"...) + case CloseMandatoryExtension: + s = append(s, " (mandatory extension missing)"...) + case CloseInternalServerErr: + s = append(s, " (internal server error)"...) + case CloseTLSHandshake: + s = append(s, " (TLS handshake error)"...) + } + if e.Text != "" { + s = append(s, ": "...) + s = append(s, e.Text...) + } + return string(s) +} + +// IsCloseError returns boolean indicating whether the error is a *CloseError +// with one of the specified codes. +func IsCloseError(err error, codes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range codes { + if e.Code == code { + return true + } + } + } + return false +} + +// IsUnexpectedCloseError returns boolean indicating whether the error is a +// *CloseError with a code not in the list of expected codes. +func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range expectedCodes { + if e.Code == code { + return false + } + } + return true + } + return false +} + +var ( + errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} + errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} + errBadWriteOpCode = errors.New("websocket: bad write message type") + errWriteClosed = errors.New("websocket: write closed") + errInvalidControlFrame = errors.New("websocket: invalid control frame") +) + +func newMaskKey() [4]byte { + n := rand.Uint32() + return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} +} + +func hideTempErr(err error) error { + if e, ok := err.(net.Error); ok && e.Temporary() { + err = &netError{msg: e.Error(), timeout: e.Timeout()} + } + return err +} + +func isControl(frameType int) bool { + return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage +} + +func isData(frameType int) bool { + return frameType == TextMessage || frameType == BinaryMessage +} + +var validReceivedCloseCodes = map[int]bool{ + // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number + + CloseNormalClosure: true, + CloseGoingAway: true, + CloseProtocolError: true, + CloseUnsupportedData: true, + CloseNoStatusReceived: false, + CloseAbnormalClosure: false, + CloseInvalidFramePayloadData: true, + ClosePolicyViolation: true, + CloseMessageTooBig: true, + CloseMandatoryExtension: true, + CloseInternalServerErr: true, + CloseServiceRestart: true, + CloseTryAgainLater: true, + CloseTLSHandshake: false, +} + +func isValidReceivedCloseCode(code int) bool { + return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) +} + +// The Conn type represents a WebSocket connection. +type Conn struct { + conn net.Conn + isServer bool + subprotocol string + + // Write fields + mu chan bool // used as mutex to protect write to conn + writeBuf []byte // frame is constructed in this buffer. + writeDeadline time.Time + writer io.WriteCloser // the current writer returned to the application + isWriting bool // for best-effort concurrent write detection + + writeErrMu sync.Mutex + writeErr error + + enableWriteCompression bool + compressionLevel int + newCompressionWriter func(io.WriteCloser, int) io.WriteCloser + + // Read fields + reader io.ReadCloser // the current reader returned to the application + readErr error + br *bufio.Reader + readRemaining int64 // bytes remaining in current frame. + readFinal bool // true the current message has more frames. + readLength int64 // Message size. + readLimit int64 // Maximum message size. + readMaskPos int + readMaskKey [4]byte + handlePong func(string) error + handlePing func(string) error + handleClose func(int, string) error + readErrCount int + messageReader *messageReader // the current low-level reader + + readDecompress bool // whether last read frame had RSV1 set + newDecompressionReader func(io.Reader) io.ReadCloser +} + +func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { + return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil) +} + +type writeHook struct { + p []byte +} + +func (wh *writeHook) Write(p []byte) (int, error) { + wh.p = p + return len(p), nil +} + +func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn { + mu := make(chan bool, 1) + mu <- true + + var br *bufio.Reader + if readBufferSize == 0 && brw != nil && brw.Reader != nil { + // Reuse the supplied bufio.Reader if the buffer has a useful size. + // This code assumes that peek on a reader returns + // bufio.Reader.buf[:0]. + brw.Reader.Reset(conn) + if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 { + br = brw.Reader + } + } + if br == nil { + if readBufferSize == 0 { + readBufferSize = defaultReadBufferSize + } + if readBufferSize < maxControlFramePayloadSize { + readBufferSize = maxControlFramePayloadSize + } + br = bufio.NewReaderSize(conn, readBufferSize) + } + + var writeBuf []byte + if writeBufferSize == 0 && brw != nil && brw.Writer != nil { + // Use the bufio.Writer's buffer if the buffer has a useful size. This + // code assumes that bufio.Writer.buf[:1] is passed to the + // bufio.Writer's underlying writer. + var wh writeHook + brw.Writer.Reset(&wh) + brw.Writer.WriteByte(0) + brw.Flush() + if cap(wh.p) >= maxFrameHeaderSize+256 { + writeBuf = wh.p[:cap(wh.p)] + } + } + + if writeBuf == nil { + if writeBufferSize == 0 { + writeBufferSize = defaultWriteBufferSize + } + writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize) + } + + c := &Conn{ + isServer: isServer, + br: br, + conn: conn, + mu: mu, + readFinal: true, + writeBuf: writeBuf, + enableWriteCompression: true, + compressionLevel: defaultCompressionLevel, + } + c.SetCloseHandler(nil) + c.SetPingHandler(nil) + c.SetPongHandler(nil) + return c +} + +// Subprotocol returns the negotiated protocol for the connection. +func (c *Conn) Subprotocol() string { + return c.subprotocol +} + +// Close closes the underlying network connection without sending or waiting for a close frame. +func (c *Conn) Close() error { + return c.conn.Close() +} + +// LocalAddr returns the local network address. +func (c *Conn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +// RemoteAddr returns the remote network address. +func (c *Conn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +// Write methods + +func (c *Conn) writeFatal(err error) error { + err = hideTempErr(err) + c.writeErrMu.Lock() + if c.writeErr == nil { + c.writeErr = err + } + c.writeErrMu.Unlock() + return err +} + +func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error { + <-c.mu + defer func() { c.mu <- true }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + for _, buf := range bufs { + if len(buf) > 0 { + _, err := c.conn.Write(buf) + if err != nil { + return c.writeFatal(err) + } + } + } + + if frameType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return nil +} + +// WriteControl writes a control message with the given deadline. The allowed +// message types are CloseMessage, PingMessage and PongMessage. +func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { + if !isControl(messageType) { + return errBadWriteOpCode + } + if len(data) > maxControlFramePayloadSize { + return errInvalidControlFrame + } + + b0 := byte(messageType) | finalBit + b1 := byte(len(data)) + if !c.isServer { + b1 |= maskBit + } + + buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) + buf = append(buf, b0, b1) + + if c.isServer { + buf = append(buf, data...) + } else { + key := newMaskKey() + buf = append(buf, key[:]...) + buf = append(buf, data...) + maskBytes(key, 0, buf[6:]) + } + + d := time.Hour * 1000 + if !deadline.IsZero() { + d = deadline.Sub(time.Now()) + if d < 0 { + return errWriteTimeout + } + } + + timer := time.NewTimer(d) + select { + case <-c.mu: + timer.Stop() + case <-timer.C: + return errWriteTimeout + } + defer func() { c.mu <- true }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + _, err = c.conn.Write(buf) + if err != nil { + return c.writeFatal(err) + } + if messageType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return err +} + +func (c *Conn) prepWrite(messageType int) error { + // Close previous writer if not already closed by the application. It's + // probably better to return an error in this situation, but we cannot + // change this without breaking existing applications. + if c.writer != nil { + c.writer.Close() + c.writer = nil + } + + if !isControl(messageType) && !isData(messageType) { + return errBadWriteOpCode + } + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + return err +} + +// NextWriter returns a writer for the next message to send. The writer's Close +// method flushes the complete message to the network. +// +// There can be at most one open writer on a connection. NextWriter closes the +// previous writer if the application has not already done so. +func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { + if err := c.prepWrite(messageType); err != nil { + return nil, err + } + + mw := &messageWriter{ + c: c, + frameType: messageType, + pos: maxFrameHeaderSize, + } + c.writer = mw + if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { + w := c.newCompressionWriter(c.writer, c.compressionLevel) + mw.compress = true + c.writer = w + } + return c.writer, nil +} + +type messageWriter struct { + c *Conn + compress bool // whether next call to flushFrame should set RSV1 + pos int // end of data in writeBuf. + frameType int // type of the current frame. + err error +} + +func (w *messageWriter) fatal(err error) error { + if w.err != nil { + w.err = err + w.c.writer = nil + } + return err +} + +// flushFrame writes buffered data and extra as a frame to the network. The +// final argument indicates that this is the last frame in the message. +func (w *messageWriter) flushFrame(final bool, extra []byte) error { + c := w.c + length := w.pos - maxFrameHeaderSize + len(extra) + + // Check for invalid control frames. + if isControl(w.frameType) && + (!final || length > maxControlFramePayloadSize) { + return w.fatal(errInvalidControlFrame) + } + + b0 := byte(w.frameType) + if final { + b0 |= finalBit + } + if w.compress { + b0 |= rsv1Bit + } + w.compress = false + + b1 := byte(0) + if !c.isServer { + b1 |= maskBit + } + + // Assume that the frame starts at beginning of c.writeBuf. + framePos := 0 + if c.isServer { + // Adjust up if mask not included in the header. + framePos = 4 + } + + switch { + case length >= 65536: + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 127 + binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) + case length > 125: + framePos += 6 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 126 + binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) + default: + framePos += 8 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | byte(length) + } + + if !c.isServer { + key := newMaskKey() + copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) + maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) + if len(extra) > 0 { + return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) + } + } + + // Write the buffers to the connection with best-effort detection of + // concurrent writes. See the concurrency section in the package + // documentation for more info. + + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + + err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) + + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + + if err != nil { + return w.fatal(err) + } + + if final { + c.writer = nil + return nil + } + + // Setup for next frame. + w.pos = maxFrameHeaderSize + w.frameType = continuationFrame + return nil +} + +func (w *messageWriter) ncopy(max int) (int, error) { + n := len(w.c.writeBuf) - w.pos + if n <= 0 { + if err := w.flushFrame(false, nil); err != nil { + return 0, err + } + n = len(w.c.writeBuf) - w.pos + } + if n > max { + n = max + } + return n, nil +} + +func (w *messageWriter) Write(p []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { + // Don't buffer large messages. + err := w.flushFrame(false, p) + if err != nil { + return 0, err + } + return len(p), nil + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) WriteString(p string) (int, error) { + if w.err != nil { + return 0, w.err + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { + if w.err != nil { + return 0, w.err + } + for { + if w.pos == len(w.c.writeBuf) { + err = w.flushFrame(false, nil) + if err != nil { + break + } + } + var n int + n, err = r.Read(w.c.writeBuf[w.pos:]) + w.pos += n + nn += int64(n) + if err != nil { + if err == io.EOF { + err = nil + } + break + } + } + return nn, err +} + +func (w *messageWriter) Close() error { + if w.err != nil { + return w.err + } + if err := w.flushFrame(true, nil); err != nil { + return err + } + w.err = errWriteClosed + return nil +} + +// WritePreparedMessage writes prepared message into connection. +func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { + frameType, frameData, err := pm.frame(prepareKey{ + isServer: c.isServer, + compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), + compressionLevel: c.compressionLevel, + }) + if err != nil { + return err + } + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + err = c.write(frameType, c.writeDeadline, frameData, nil) + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + return err +} + +// WriteMessage is a helper method for getting a writer using NextWriter, +// writing the message and closing the writer. +func (c *Conn) WriteMessage(messageType int, data []byte) error { + + if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { + // Fast path with no allocations and single frame. + + if err := c.prepWrite(messageType); err != nil { + return err + } + mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} + n := copy(c.writeBuf[mw.pos:], data) + mw.pos += n + data = data[n:] + return mw.flushFrame(true, data) + } + + w, err := c.NextWriter(messageType) + if err != nil { + return err + } + if _, err = w.Write(data); err != nil { + return err + } + return w.Close() +} + +// SetWriteDeadline sets the write deadline on the underlying network +// connection. After a write has timed out, the websocket state is corrupt and +// all future writes will return an error. A zero value for t means writes will +// not time out. +func (c *Conn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = t + return nil +} + +// Read methods + +func (c *Conn) advanceFrame() (int, error) { + + // 1. Skip remainder of previous frame. + + if c.readRemaining > 0 { + if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { + return noFrame, err + } + } + + // 2. Read and parse first two bytes of frame header. + + p, err := c.read(2) + if err != nil { + return noFrame, err + } + + final := p[0]&finalBit != 0 + frameType := int(p[0] & 0xf) + mask := p[1]&maskBit != 0 + c.readRemaining = int64(p[1] & 0x7f) + + c.readDecompress = false + if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { + c.readDecompress = true + p[0] &^= rsv1Bit + } + + if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { + return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) + } + + switch frameType { + case CloseMessage, PingMessage, PongMessage: + if c.readRemaining > maxControlFramePayloadSize { + return noFrame, c.handleProtocolError("control frame length > 125") + } + if !final { + return noFrame, c.handleProtocolError("control frame not final") + } + case TextMessage, BinaryMessage: + if !c.readFinal { + return noFrame, c.handleProtocolError("message start before final message frame") + } + c.readFinal = final + case continuationFrame: + if c.readFinal { + return noFrame, c.handleProtocolError("continuation after final message frame") + } + c.readFinal = final + default: + return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) + } + + // 3. Read and parse frame length. + + switch c.readRemaining { + case 126: + p, err := c.read(2) + if err != nil { + return noFrame, err + } + c.readRemaining = int64(binary.BigEndian.Uint16(p)) + case 127: + p, err := c.read(8) + if err != nil { + return noFrame, err + } + c.readRemaining = int64(binary.BigEndian.Uint64(p)) + } + + // 4. Handle frame masking. + + if mask != c.isServer { + return noFrame, c.handleProtocolError("incorrect mask flag") + } + + if mask { + c.readMaskPos = 0 + p, err := c.read(len(c.readMaskKey)) + if err != nil { + return noFrame, err + } + copy(c.readMaskKey[:], p) + } + + // 5. For text and binary messages, enforce read limit and return. + + if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { + + c.readLength += c.readRemaining + if c.readLimit > 0 && c.readLength > c.readLimit { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) + return noFrame, ErrReadLimit + } + + return frameType, nil + } + + // 6. Read control frame payload. + + var payload []byte + if c.readRemaining > 0 { + payload, err = c.read(int(c.readRemaining)) + c.readRemaining = 0 + if err != nil { + return noFrame, err + } + if c.isServer { + maskBytes(c.readMaskKey, 0, payload) + } + } + + // 7. Process control frame payload. + + switch frameType { + case PongMessage: + if err := c.handlePong(string(payload)); err != nil { + return noFrame, err + } + case PingMessage: + if err := c.handlePing(string(payload)); err != nil { + return noFrame, err + } + case CloseMessage: + closeCode := CloseNoStatusReceived + closeText := "" + if len(payload) >= 2 { + closeCode = int(binary.BigEndian.Uint16(payload)) + if !isValidReceivedCloseCode(closeCode) { + return noFrame, c.handleProtocolError("invalid close code") + } + closeText = string(payload[2:]) + if !utf8.ValidString(closeText) { + return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") + } + } + if err := c.handleClose(closeCode, closeText); err != nil { + return noFrame, err + } + return noFrame, &CloseError{Code: closeCode, Text: closeText} + } + + return frameType, nil +} + +func (c *Conn) handleProtocolError(message string) error { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) + return errors.New("websocket: " + message) +} + +// NextReader returns the next data message received from the peer. The +// returned messageType is either TextMessage or BinaryMessage. +// +// There can be at most one open reader on a connection. NextReader discards +// the previous message if the application has not already consumed it. +// +// Applications must break out of the application's read loop when this method +// returns a non-nil error value. Errors returned from this method are +// permanent. Once this method returns a non-nil error, all subsequent calls to +// this method return the same error. +func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { + // Close previous reader, only relevant for decompression. + if c.reader != nil { + c.reader.Close() + c.reader = nil + } + + c.messageReader = nil + c.readLength = 0 + + for c.readErr == nil { + frameType, err := c.advanceFrame() + if err != nil { + c.readErr = hideTempErr(err) + break + } + if frameType == TextMessage || frameType == BinaryMessage { + c.messageReader = &messageReader{c} + c.reader = c.messageReader + if c.readDecompress { + c.reader = c.newDecompressionReader(c.reader) + } + return frameType, c.reader, nil + } + } + + // Applications that do handle the error returned from this method spin in + // tight loop on connection failure. To help application developers detect + // this error, panic on repeated reads to the failed connection. + c.readErrCount++ + if c.readErrCount >= 1000 { + panic("repeated read on failed websocket connection") + } + + return noFrame, nil, c.readErr +} + +type messageReader struct{ c *Conn } + +func (r *messageReader) Read(b []byte) (int, error) { + c := r.c + if c.messageReader != r { + return 0, io.EOF + } + + for c.readErr == nil { + + if c.readRemaining > 0 { + if int64(len(b)) > c.readRemaining { + b = b[:c.readRemaining] + } + n, err := c.br.Read(b) + c.readErr = hideTempErr(err) + if c.isServer { + c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) + } + c.readRemaining -= int64(n) + if c.readRemaining > 0 && c.readErr == io.EOF { + c.readErr = errUnexpectedEOF + } + return n, c.readErr + } + + if c.readFinal { + c.messageReader = nil + return 0, io.EOF + } + + frameType, err := c.advanceFrame() + switch { + case err != nil: + c.readErr = hideTempErr(err) + case frameType == TextMessage || frameType == BinaryMessage: + c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") + } + } + + err := c.readErr + if err == io.EOF && c.messageReader == r { + err = errUnexpectedEOF + } + return 0, err +} + +func (r *messageReader) Close() error { + return nil +} + +// ReadMessage is a helper method for getting a reader using NextReader and +// reading from that reader to a buffer. +func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { + var r io.Reader + messageType, r, err = c.NextReader() + if err != nil { + return messageType, nil, err + } + p, err = ioutil.ReadAll(r) + return messageType, p, err +} + +// SetReadDeadline sets the read deadline on the underlying network connection. +// After a read has timed out, the websocket connection state is corrupt and +// all future reads will return an error. A zero value for t means reads will +// not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +// SetReadLimit sets the maximum size for a message read from the peer. If a +// message exceeds the limit, the connection sends a close frame to the peer +// and returns ErrReadLimit to the application. +func (c *Conn) SetReadLimit(limit int64) { + c.readLimit = limit +} + +// CloseHandler returns the current close handler +func (c *Conn) CloseHandler() func(code int, text string) error { + return c.handleClose +} + +// SetCloseHandler sets the handler for close messages received from the peer. +// The code argument to h is the received close code or CloseNoStatusReceived +// if the close message is empty. The default close handler sends a close frame +// back to the peer. +// +// The application must read the connection to process close messages as +// described in the section on Control Frames above. +// +// The connection read methods return a CloseError when a close frame is +// received. Most applications should handle close messages as part of their +// normal error handling. Applications should only set a close handler when the +// application must perform some action before sending a close frame back to +// the peer. +func (c *Conn) SetCloseHandler(h func(code int, text string) error) { + if h == nil { + h = func(code int, text string) error { + message := []byte{} + if code != CloseNoStatusReceived { + message = FormatCloseMessage(code, "") + } + c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) + return nil + } + } + c.handleClose = h +} + +// PingHandler returns the current ping handler +func (c *Conn) PingHandler() func(appData string) error { + return c.handlePing +} + +// SetPingHandler sets the handler for ping messages received from the peer. +// The appData argument to h is the PING frame application data. The default +// ping handler sends a pong to the peer. +// +// The application must read the connection to process ping messages as +// described in the section on Control Frames above. +func (c *Conn) SetPingHandler(h func(appData string) error) { + if h == nil { + h = func(message string) error { + err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) + if err == ErrCloseSent { + return nil + } else if e, ok := err.(net.Error); ok && e.Temporary() { + return nil + } + return err + } + } + c.handlePing = h +} + +// PongHandler returns the current pong handler +func (c *Conn) PongHandler() func(appData string) error { + return c.handlePong +} + +// SetPongHandler sets the handler for pong messages received from the peer. +// The appData argument to h is the PONG frame application data. The default +// pong handler does nothing. +// +// The application must read the connection to process ping messages as +// described in the section on Control Frames above. +func (c *Conn) SetPongHandler(h func(appData string) error) { + if h == nil { + h = func(string) error { return nil } + } + c.handlePong = h +} + +// UnderlyingConn returns the internal net.Conn. This can be used to further +// modifications to connection specific flags. +func (c *Conn) UnderlyingConn() net.Conn { + return c.conn +} + +// EnableWriteCompression enables and disables write compression of +// subsequent text and binary messages. This function is a noop if +// compression was not negotiated with the peer. +func (c *Conn) EnableWriteCompression(enable bool) { + c.enableWriteCompression = enable +} + +// SetCompressionLevel sets the flate compression level for subsequent text and +// binary messages. This function is a noop if compression was not negotiated +// with the peer. See the compress/flate package for a description of +// compression levels. +func (c *Conn) SetCompressionLevel(level int) error { + if !isValidCompressionLevel(level) { + return errors.New("websocket: invalid compression level") + } + c.compressionLevel = level + return nil +} + +// FormatCloseMessage formats closeCode and text as a WebSocket close message. +func FormatCloseMessage(closeCode int, text string) []byte { + buf := make([]byte, 2+len(text)) + binary.BigEndian.PutUint16(buf, uint16(closeCode)) + copy(buf[2:], text) + return buf +} diff --git a/vendor/github.com/gorilla/websocket/conn_broadcast_test.go b/vendor/github.com/gorilla/websocket/conn_broadcast_test.go new file mode 100644 index 00000000..45038e48 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_broadcast_test.go @@ -0,0 +1,134 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package websocket + +import ( + "io" + "io/ioutil" + "sync/atomic" + "testing" +) + +// broadcastBench allows to run broadcast benchmarks. +// In every broadcast benchmark we create many connections, then send the same +// message into every connection and wait for all writes complete. This emulates +// an application where many connections listen to the same data - i.e. PUB/SUB +// scenarios with many subscribers in one channel. +type broadcastBench struct { + w io.Writer + message *broadcastMessage + closeCh chan struct{} + doneCh chan struct{} + count int32 + conns []*broadcastConn + compression bool + usePrepared bool +} + +type broadcastMessage struct { + payload []byte + prepared *PreparedMessage +} + +type broadcastConn struct { + conn *Conn + msgCh chan *broadcastMessage +} + +func newBroadcastConn(c *Conn) *broadcastConn { + return &broadcastConn{ + conn: c, + msgCh: make(chan *broadcastMessage, 1), + } +} + +func newBroadcastBench(usePrepared, compression bool) *broadcastBench { + bench := &broadcastBench{ + w: ioutil.Discard, + doneCh: make(chan struct{}), + closeCh: make(chan struct{}), + usePrepared: usePrepared, + compression: compression, + } + msg := &broadcastMessage{ + payload: textMessages(1)[0], + } + if usePrepared { + pm, _ := NewPreparedMessage(TextMessage, msg.payload) + msg.prepared = pm + } + bench.message = msg + bench.makeConns(10000) + return bench +} + +func (b *broadcastBench) makeConns(numConns int) { + conns := make([]*broadcastConn, numConns) + + for i := 0; i < numConns; i++ { + c := newConn(fakeNetConn{Reader: nil, Writer: b.w}, true, 1024, 1024) + if b.compression { + c.enableWriteCompression = true + c.newCompressionWriter = compressNoContextTakeover + } + conns[i] = newBroadcastConn(c) + go func(c *broadcastConn) { + for { + select { + case msg := <-c.msgCh: + if b.usePrepared { + c.conn.WritePreparedMessage(msg.prepared) + } else { + c.conn.WriteMessage(TextMessage, msg.payload) + } + val := atomic.AddInt32(&b.count, 1) + if val%int32(numConns) == 0 { + b.doneCh <- struct{}{} + } + case <-b.closeCh: + return + } + } + }(conns[i]) + } + b.conns = conns +} + +func (b *broadcastBench) close() { + close(b.closeCh) +} + +func (b *broadcastBench) runOnce() { + for _, c := range b.conns { + c.msgCh <- b.message + } + <-b.doneCh +} + +func BenchmarkBroadcast(b *testing.B) { + benchmarks := []struct { + name string + usePrepared bool + compression bool + }{ + {"NoCompression", false, false}, + {"WithCompression", false, true}, + {"NoCompressionPrepared", true, false}, + {"WithCompressionPrepared", true, true}, + } + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + bench := newBroadcastBench(bm.usePrepared, bm.compression) + defer bench.close() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bench.runOnce() + } + b.ReportAllocs() + }) + } +} diff --git a/vendor/github.com/gorilla/websocket/conn_read.go b/vendor/github.com/gorilla/websocket/conn_read.go new file mode 100644 index 00000000..1ea15059 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_read.go @@ -0,0 +1,18 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.5 + +package websocket + +import "io" + +func (c *Conn) read(n int) ([]byte, error) { + p, err := c.br.Peek(n) + if err == io.EOF { + err = errUnexpectedEOF + } + c.br.Discard(len(p)) + return p, err +} diff --git a/vendor/github.com/gorilla/websocket/conn_read_legacy.go b/vendor/github.com/gorilla/websocket/conn_read_legacy.go new file mode 100644 index 00000000..018541cf --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_read_legacy.go @@ -0,0 +1,21 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package websocket + +import "io" + +func (c *Conn) read(n int) ([]byte, error) { + p, err := c.br.Peek(n) + if err == io.EOF { + err = errUnexpectedEOF + } + if len(p) > 0 { + // advance over the bytes just read + io.ReadFull(c.br, p) + } + return p, err +} diff --git a/vendor/github.com/gorilla/websocket/conn_test.go b/vendor/github.com/gorilla/websocket/conn_test.go new file mode 100644 index 00000000..06e9bc3f --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_test.go @@ -0,0 +1,497 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "reflect" + "testing" + "testing/iotest" + "time" +) + +var _ net.Error = errWriteTimeout + +type fakeNetConn struct { + io.Reader + io.Writer +} + +func (c fakeNetConn) Close() error { return nil } +func (c fakeNetConn) LocalAddr() net.Addr { return localAddr } +func (c fakeNetConn) RemoteAddr() net.Addr { return remoteAddr } +func (c fakeNetConn) SetDeadline(t time.Time) error { return nil } +func (c fakeNetConn) SetReadDeadline(t time.Time) error { return nil } +func (c fakeNetConn) SetWriteDeadline(t time.Time) error { return nil } + +type fakeAddr int + +var ( + localAddr = fakeAddr(1) + remoteAddr = fakeAddr(2) +) + +func (a fakeAddr) Network() string { + return "net" +} + +func (a fakeAddr) String() string { + return "str" +} + +func TestFraming(t *testing.T) { + frameSizes := []int{0, 1, 2, 124, 125, 126, 127, 128, 129, 65534, 65535, 65536, 65537} + var readChunkers = []struct { + name string + f func(io.Reader) io.Reader + }{ + {"half", iotest.HalfReader}, + {"one", iotest.OneByteReader}, + {"asis", func(r io.Reader) io.Reader { return r }}, + } + writeBuf := make([]byte, 65537) + for i := range writeBuf { + writeBuf[i] = byte(i) + } + var writers = []struct { + name string + f func(w io.Writer, n int) (int, error) + }{ + {"iocopy", func(w io.Writer, n int) (int, error) { + nn, err := io.Copy(w, bytes.NewReader(writeBuf[:n])) + return int(nn), err + }}, + {"write", func(w io.Writer, n int) (int, error) { + return w.Write(writeBuf[:n]) + }}, + {"string", func(w io.Writer, n int) (int, error) { + return io.WriteString(w, string(writeBuf[:n])) + }}, + } + + for _, compress := range []bool{false, true} { + for _, isServer := range []bool{true, false} { + for _, chunker := range readChunkers { + + var connBuf bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024) + rc := newConn(fakeNetConn{Reader: chunker.f(&connBuf), Writer: nil}, !isServer, 1024, 1024) + if compress { + wc.newCompressionWriter = compressNoContextTakeover + rc.newDecompressionReader = decompressNoContextTakeover + } + for _, n := range frameSizes { + for _, writer := range writers { + name := fmt.Sprintf("z:%v, s:%v, r:%s, n:%d w:%s", compress, isServer, chunker.name, n, writer.name) + + w, err := wc.NextWriter(TextMessage) + if err != nil { + t.Errorf("%s: wc.NextWriter() returned %v", name, err) + continue + } + nn, err := writer.f(w, n) + if err != nil || nn != n { + t.Errorf("%s: w.Write(writeBuf[:n]) returned %d, %v", name, nn, err) + continue + } + err = w.Close() + if err != nil { + t.Errorf("%s: w.Close() returned %v", name, err) + continue + } + + opCode, r, err := rc.NextReader() + if err != nil || opCode != TextMessage { + t.Errorf("%s: NextReader() returned %d, r, %v", name, opCode, err) + continue + } + rbuf, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("%s: ReadFull() returned rbuf, %v", name, err) + continue + } + + if len(rbuf) != n { + t.Errorf("%s: len(rbuf) is %d, want %d", name, len(rbuf), n) + continue + } + + for i, b := range rbuf { + if byte(i) != b { + t.Errorf("%s: bad byte at offset %d", name, i) + break + } + } + } + } + } + } + } +} + +func TestControl(t *testing.T) { + const message = "this is a ping/pong messsage" + for _, isServer := range []bool{true, false} { + for _, isWriteControl := range []bool{true, false} { + name := fmt.Sprintf("s:%v, wc:%v", isServer, isWriteControl) + var connBuf bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024) + rc := newConn(fakeNetConn{Reader: &connBuf, Writer: nil}, !isServer, 1024, 1024) + if isWriteControl { + wc.WriteControl(PongMessage, []byte(message), time.Now().Add(time.Second)) + } else { + w, err := wc.NextWriter(PongMessage) + if err != nil { + t.Errorf("%s: wc.NextWriter() returned %v", name, err) + continue + } + if _, err := w.Write([]byte(message)); err != nil { + t.Errorf("%s: w.Write() returned %v", name, err) + continue + } + if err := w.Close(); err != nil { + t.Errorf("%s: w.Close() returned %v", name, err) + continue + } + var actualMessage string + rc.SetPongHandler(func(s string) error { actualMessage = s; return nil }) + rc.NextReader() + if actualMessage != message { + t.Errorf("%s: pong=%q, want %q", name, actualMessage, message) + continue + } + } + } + } +} + +func TestCloseFrameBeforeFinalMessageFrame(t *testing.T) { + const bufSize = 512 + + expectedErr := &CloseError{Code: CloseNormalClosure, Text: "hello"} + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(make([]byte, bufSize+bufSize/2)) + wc.WriteControl(CloseMessage, FormatCloseMessage(expectedErr.Code, expectedErr.Text), time.Now().Add(10*time.Second)) + w.Close() + + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("NextReader() returned %d, %v", op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("io.Copy() returned %v, want %v", err, expectedErr) + } + _, _, err = rc.NextReader() + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("NextReader() returned %v, want %v", err, expectedErr) + } +} + +func TestEOFWithinFrame(t *testing.T) { + const bufSize = 64 + + for n := 0; ; n++ { + var b bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b}, false, 1024, 1024) + rc := newConn(fakeNetConn{Reader: &b, Writer: nil}, true, 1024, 1024) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(make([]byte, bufSize)) + w.Close() + + if n >= b.Len() { + break + } + b.Truncate(n) + + op, r, err := rc.NextReader() + if err == errUnexpectedEOF { + continue + } + if op != BinaryMessage || err != nil { + t.Fatalf("%d: NextReader() returned %d, %v", n, op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if err != errUnexpectedEOF { + t.Fatalf("%d: io.Copy() returned %v, want %v", n, err, errUnexpectedEOF) + } + _, _, err = rc.NextReader() + if err != errUnexpectedEOF { + t.Fatalf("%d: NextReader() returned %v, want %v", n, err, errUnexpectedEOF) + } + } +} + +func TestEOFBeforeFinalFrame(t *testing.T) { + const bufSize = 512 + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(make([]byte, bufSize+bufSize/2)) + + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("NextReader() returned %d, %v", op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if err != errUnexpectedEOF { + t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF) + } + _, _, err = rc.NextReader() + if err != errUnexpectedEOF { + t.Fatalf("NextReader() returned %v, want %v", err, errUnexpectedEOF) + } +} + +func TestWriteAfterMessageWriterClose(t *testing.T) { + wc := newConn(fakeNetConn{Reader: nil, Writer: &bytes.Buffer{}}, false, 1024, 1024) + w, _ := wc.NextWriter(BinaryMessage) + io.WriteString(w, "hello") + if err := w.Close(); err != nil { + t.Fatalf("unxpected error closing message writer, %v", err) + } + + if _, err := io.WriteString(w, "world"); err == nil { + t.Fatalf("no error writing after close") + } + + w, _ = wc.NextWriter(BinaryMessage) + io.WriteString(w, "hello") + + // close w by getting next writer + _, err := wc.NextWriter(BinaryMessage) + if err != nil { + t.Fatalf("unexpected error getting next writer, %v", err) + } + + if _, err := io.WriteString(w, "world"); err == nil { + t.Fatalf("no error writing after close") + } +} + +func TestReadLimit(t *testing.T) { + + const readLimit = 512 + message := make([]byte, readLimit+1) + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, readLimit-2) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) + rc.SetReadLimit(readLimit) + + // Send message at the limit with interleaved pong. + w, _ := wc.NextWriter(BinaryMessage) + w.Write(message[:readLimit-1]) + wc.WriteControl(PongMessage, []byte("this is a pong"), time.Now().Add(10*time.Second)) + w.Write(message[:1]) + w.Close() + + // Send message larger than the limit. + wc.WriteMessage(BinaryMessage, message[:readLimit+1]) + + op, _, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("1: NextReader() returned %d, %v", op, err) + } + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("2: NextReader() returned %d, %v", op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if err != ErrReadLimit { + t.Fatalf("io.Copy() returned %v", err) + } +} + +func TestAddrs(t *testing.T) { + c := newConn(&fakeNetConn{}, true, 1024, 1024) + if c.LocalAddr() != localAddr { + t.Errorf("LocalAddr = %v, want %v", c.LocalAddr(), localAddr) + } + if c.RemoteAddr() != remoteAddr { + t.Errorf("RemoteAddr = %v, want %v", c.RemoteAddr(), remoteAddr) + } +} + +func TestUnderlyingConn(t *testing.T) { + var b1, b2 bytes.Buffer + fc := fakeNetConn{Reader: &b1, Writer: &b2} + c := newConn(fc, true, 1024, 1024) + ul := c.UnderlyingConn() + if ul != fc { + t.Fatalf("Underlying conn is not what it should be.") + } +} + +func TestBufioReadBytes(t *testing.T) { + + // Test calling bufio.ReadBytes for value longer than read buffer size. + + m := make([]byte, 512) + m[len(m)-1] = '\n' + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, len(m)+64, len(m)+64) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, len(m)-64, len(m)-64) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(m) + w.Close() + + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("NextReader() returned %d, %v", op, err) + } + + br := bufio.NewReader(r) + p, err := br.ReadBytes('\n') + if err != nil { + t.Fatalf("ReadBytes() returned %v", err) + } + if len(p) != len(m) { + t.Fatalf("read returnd %d bytes, want %d bytes", len(p), len(m)) + } +} + +var closeErrorTests = []struct { + err error + codes []int + ok bool +}{ + {&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, true}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, false}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, true}, + {errors.New("hello"), []int{CloseNormalClosure}, false}, +} + +func TestCloseError(t *testing.T) { + for _, tt := range closeErrorTests { + ok := IsCloseError(tt.err, tt.codes...) + if ok != tt.ok { + t.Errorf("IsCloseError(%#v, %#v) returned %v, want %v", tt.err, tt.codes, ok, tt.ok) + } + } +} + +var unexpectedCloseErrorTests = []struct { + err error + codes []int + ok bool +}{ + {&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, false}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, true}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, false}, + {errors.New("hello"), []int{CloseNormalClosure}, false}, +} + +func TestUnexpectedCloseErrors(t *testing.T) { + for _, tt := range unexpectedCloseErrorTests { + ok := IsUnexpectedCloseError(tt.err, tt.codes...) + if ok != tt.ok { + t.Errorf("IsUnexpectedCloseError(%#v, %#v) returned %v, want %v", tt.err, tt.codes, ok, tt.ok) + } + } +} + +type blockingWriter struct { + c1, c2 chan struct{} +} + +func (w blockingWriter) Write(p []byte) (int, error) { + // Allow main to continue + close(w.c1) + // Wait for panic in main + <-w.c2 + return len(p), nil +} + +func TestConcurrentWritePanic(t *testing.T) { + w := blockingWriter{make(chan struct{}), make(chan struct{})} + c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024) + go func() { + c.WriteMessage(TextMessage, []byte{}) + }() + + // wait for goroutine to block in write. + <-w.c1 + + defer func() { + close(w.c2) + if v := recover(); v != nil { + return + } + }() + + c.WriteMessage(TextMessage, []byte{}) + t.Fatal("should not get here") +} + +type failingReader struct{} + +func (r failingReader) Read(p []byte) (int, error) { + return 0, io.EOF +} + +func TestFailedConnectionReadPanic(t *testing.T) { + c := newConn(fakeNetConn{Reader: failingReader{}, Writer: nil}, false, 1024, 1024) + + defer func() { + if v := recover(); v != nil { + return + } + }() + + for i := 0; i < 20000; i++ { + c.ReadMessage() + } + t.Fatal("should not get here") +} + +func TestBufioReuse(t *testing.T) { + brw := bufio.NewReadWriter(bufio.NewReader(nil), bufio.NewWriter(nil)) + c := newConnBRW(nil, false, 0, 0, brw) + + if c.br != brw.Reader { + t.Error("connection did not reuse bufio.Reader") + } + + var wh writeHook + brw.Writer.Reset(&wh) + brw.WriteByte(0) + brw.Flush() + if &c.writeBuf[0] != &wh.p[0] { + t.Error("connection did not reuse bufio.Writer") + } + + brw = bufio.NewReadWriter(bufio.NewReaderSize(nil, 0), bufio.NewWriterSize(nil, 0)) + c = newConnBRW(nil, false, 0, 0, brw) + + if c.br == brw.Reader { + t.Error("connection used bufio.Reader with small size") + } + + brw.Writer.Reset(&wh) + brw.WriteByte(0) + brw.Flush() + if &c.writeBuf[0] != &wh.p[0] { + t.Error("connection used bufio.Writer with small size") + } + +} diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go new file mode 100644 index 00000000..e291a952 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/doc.go @@ -0,0 +1,180 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package websocket implements the WebSocket protocol defined in RFC 6455. +// +// Overview +// +// The Conn type represents a WebSocket connection. A server application uses +// the Upgrade function from an Upgrader object with a HTTP request handler +// to get a pointer to a Conn: +// +// var upgrader = websocket.Upgrader{ +// ReadBufferSize: 1024, +// WriteBufferSize: 1024, +// } +// +// func handler(w http.ResponseWriter, r *http.Request) { +// conn, err := upgrader.Upgrade(w, r, nil) +// if err != nil { +// log.Println(err) +// return +// } +// ... Use conn to send and receive messages. +// } +// +// Call the connection's WriteMessage and ReadMessage methods to send and +// receive messages as a slice of bytes. This snippet of code shows how to echo +// messages using these methods: +// +// for { +// messageType, p, err := conn.ReadMessage() +// if err != nil { +// return +// } +// if err = conn.WriteMessage(messageType, p); err != nil { +// return err +// } +// } +// +// In above snippet of code, p is a []byte and messageType is an int with value +// websocket.BinaryMessage or websocket.TextMessage. +// +// An application can also send and receive messages using the io.WriteCloser +// and io.Reader interfaces. To send a message, call the connection NextWriter +// method to get an io.WriteCloser, write the message to the writer and close +// the writer when done. To receive a message, call the connection NextReader +// method to get an io.Reader and read until io.EOF is returned. This snippet +// shows how to echo messages using the NextWriter and NextReader methods: +// +// for { +// messageType, r, err := conn.NextReader() +// if err != nil { +// return +// } +// w, err := conn.NextWriter(messageType) +// if err != nil { +// return err +// } +// if _, err := io.Copy(w, r); err != nil { +// return err +// } +// if err := w.Close(); err != nil { +// return err +// } +// } +// +// Data Messages +// +// The WebSocket protocol distinguishes between text and binary data messages. +// Text messages are interpreted as UTF-8 encoded text. The interpretation of +// binary messages is left to the application. +// +// This package uses the TextMessage and BinaryMessage integer constants to +// identify the two data message types. The ReadMessage and NextReader methods +// return the type of the received message. The messageType argument to the +// WriteMessage and NextWriter methods specifies the type of a sent message. +// +// It is the application's responsibility to ensure that text messages are +// valid UTF-8 encoded text. +// +// Control Messages +// +// The WebSocket protocol defines three types of control messages: close, ping +// and pong. Call the connection WriteControl, WriteMessage or NextWriter +// methods to send a control message to the peer. +// +// Connections handle received close messages by sending a close message to the +// peer and returning a *CloseError from the the NextReader, ReadMessage or the +// message Read method. +// +// Connections handle received ping and pong messages by invoking callback +// functions set with SetPingHandler and SetPongHandler methods. The callback +// functions are called from the NextReader, ReadMessage and the message Read +// methods. +// +// The default ping handler sends a pong to the peer. The application's reading +// goroutine can block for a short time while the handler writes the pong data +// to the connection. +// +// The application must read the connection to process ping, pong and close +// messages sent from the peer. If the application is not otherwise interested +// in messages from the peer, then the application should start a goroutine to +// read and discard messages from the peer. A simple example is: +// +// func readLoop(c *websocket.Conn) { +// for { +// if _, _, err := c.NextReader(); err != nil { +// c.Close() +// break +// } +// } +// } +// +// Concurrency +// +// Connections support one concurrent reader and one concurrent writer. +// +// Applications are responsible for ensuring that no more than one goroutine +// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, +// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and +// that no more than one goroutine calls the read methods (NextReader, +// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) +// concurrently. +// +// The Close and WriteControl methods can be called concurrently with all other +// methods. +// +// Origin Considerations +// +// Web browsers allow Javascript applications to open a WebSocket connection to +// any host. It's up to the server to enforce an origin policy using the Origin +// request header sent by the browser. +// +// The Upgrader calls the function specified in the CheckOrigin field to check +// the origin. If the CheckOrigin function returns false, then the Upgrade +// method fails the WebSocket handshake with HTTP status 403. +// +// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail +// the handshake if the Origin request header is present and not equal to the +// Host request header. +// +// An application can allow connections from any origin by specifying a +// function that always returns true: +// +// var upgrader = websocket.Upgrader{ +// CheckOrigin: func(r *http.Request) bool { return true }, +// } +// +// The deprecated Upgrade function does not enforce an origin policy. It's the +// application's responsibility to check the Origin header before calling +// Upgrade. +// +// Compression EXPERIMENTAL +// +// Per message compression extensions (RFC 7692) are experimentally supported +// by this package in a limited capacity. Setting the EnableCompression option +// to true in Dialer or Upgrader will attempt to negotiate per message deflate +// support. +// +// var upgrader = websocket.Upgrader{ +// EnableCompression: true, +// } +// +// If compression was successfully negotiated with the connection's peer, any +// message received in compressed form will be automatically decompressed. +// All Read methods will return uncompressed bytes. +// +// Per message compression of messages written to a connection can be enabled +// or disabled by calling the corresponding Conn method: +// +// conn.EnableWriteCompression(false) +// +// Currently this package does not support compression with "context takeover". +// This means that messages must be compressed and decompressed in isolation, +// without retaining sliding window or dictionary state across messages. For +// more details refer to RFC 7692. +// +// Use of compression is experimental and may result in decreased performance. +package websocket diff --git a/vendor/github.com/gorilla/websocket/example_test.go b/vendor/github.com/gorilla/websocket/example_test.go new file mode 100644 index 00000000..96449eac --- /dev/null +++ b/vendor/github.com/gorilla/websocket/example_test.go @@ -0,0 +1,46 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket_test + +import ( + "log" + "net/http" + "testing" + + "github.com/gorilla/websocket" +) + +var ( + c *websocket.Conn + req *http.Request +) + +// The websocket.IsUnexpectedCloseError function is useful for identifying +// application and protocol errors. +// +// This server application works with a client application running in the +// browser. The client application does not explicitly close the websocket. The +// only expected close message from the client has the code +// websocket.CloseGoingAway. All other other close messages are likely the +// result of an application or protocol error and are logged to aid debugging. +func ExampleIsUnexpectedCloseError() { + + for { + messageType, p, err := c.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { + log.Printf("error: %v, user-agent: %v", err, req.Header.Get("User-Agent")) + } + return + } + processMesage(messageType, p) + } +} + +func processMesage(mt int, p []byte) {} + +// TestX prevents godoc from showing this entire file in the example. Remove +// this function when a second example is added. +func TestX(t *testing.T) {} diff --git a/vendor/github.com/gorilla/websocket/examples/autobahn/README.md b/vendor/github.com/gorilla/websocket/examples/autobahn/README.md new file mode 100644 index 00000000..075ac153 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/autobahn/README.md @@ -0,0 +1,13 @@ +# Test Server + +This package contains a server for the [Autobahn WebSockets Test Suite](http://autobahn.ws/testsuite). + +To test the server, run + + go run server.go + +and start the client test driver + + wstest -m fuzzingclient -s fuzzingclient.json + +When the client completes, it writes a report to reports/clients/index.html. diff --git a/vendor/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json b/vendor/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json new file mode 100644 index 00000000..aa3a0bc0 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json @@ -0,0 +1,15 @@ + +{ + "options": {"failByDrop": false}, + "outdir": "./reports/clients", + "servers": [ + {"agent": "ReadAllWriteMessage", "url": "ws://localhost:9000/m", "options": {"version": 18}}, + {"agent": "ReadAllWritePreparedMessage", "url": "ws://localhost:9000/p", "options": {"version": 18}}, + {"agent": "ReadAllWrite", "url": "ws://localhost:9000/r", "options": {"version": 18}}, + {"agent": "CopyFull", "url": "ws://localhost:9000/f", "options": {"version": 18}}, + {"agent": "CopyWriterOnly", "url": "ws://localhost:9000/c", "options": {"version": 18}} + ], + "cases": ["*"], + "exclude-cases": [], + "exclude-agent-cases": {} +} diff --git a/vendor/github.com/gorilla/websocket/examples/autobahn/server.go b/vendor/github.com/gorilla/websocket/examples/autobahn/server.go new file mode 100644 index 00000000..3db880f9 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/autobahn/server.go @@ -0,0 +1,265 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Command server is a test server for the Autobahn WebSockets Test Suite. +package main + +import ( + "errors" + "flag" + "io" + "log" + "net/http" + "time" + "unicode/utf8" + + "github.com/gorilla/websocket" +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 4096, + WriteBufferSize: 4096, + EnableCompression: true, + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +// echoCopy echoes messages from the client using io.Copy. +func echoCopy(w http.ResponseWriter, r *http.Request, writerOnly bool) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("Upgrade:", err) + return + } + defer conn.Close() + for { + mt, r, err := conn.NextReader() + if err != nil { + if err != io.EOF { + log.Println("NextReader:", err) + } + return + } + if mt == websocket.TextMessage { + r = &validator{r: r} + } + w, err := conn.NextWriter(mt) + if err != nil { + log.Println("NextWriter:", err) + return + } + if mt == websocket.TextMessage { + r = &validator{r: r} + } + if writerOnly { + _, err = io.Copy(struct{ io.Writer }{w}, r) + } else { + _, err = io.Copy(w, r) + } + if err != nil { + if err == errInvalidUTF8 { + conn.WriteControl(websocket.CloseMessage, + websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), + time.Time{}) + } + log.Println("Copy:", err) + return + } + err = w.Close() + if err != nil { + log.Println("Close:", err) + return + } + } +} + +func echoCopyWriterOnly(w http.ResponseWriter, r *http.Request) { + echoCopy(w, r, true) +} + +func echoCopyFull(w http.ResponseWriter, r *http.Request) { + echoCopy(w, r, false) +} + +// echoReadAll echoes messages from the client by reading the entire message +// with ioutil.ReadAll. +func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage, writePrepared bool) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("Upgrade:", err) + return + } + defer conn.Close() + for { + mt, b, err := conn.ReadMessage() + if err != nil { + if err != io.EOF { + log.Println("NextReader:", err) + } + return + } + if mt == websocket.TextMessage { + if !utf8.Valid(b) { + conn.WriteControl(websocket.CloseMessage, + websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), + time.Time{}) + log.Println("ReadAll: invalid utf8") + } + } + if writeMessage { + if !writePrepared { + err = conn.WriteMessage(mt, b) + if err != nil { + log.Println("WriteMessage:", err) + } + } else { + pm, err := websocket.NewPreparedMessage(mt, b) + if err != nil { + log.Println("NewPreparedMessage:", err) + return + } + err = conn.WritePreparedMessage(pm) + if err != nil { + log.Println("WritePreparedMessage:", err) + } + } + } else { + w, err := conn.NextWriter(mt) + if err != nil { + log.Println("NextWriter:", err) + return + } + if _, err := w.Write(b); err != nil { + log.Println("Writer:", err) + return + } + if err := w.Close(); err != nil { + log.Println("Close:", err) + return + } + } + } +} + +func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { + echoReadAll(w, r, false, false) +} + +func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) { + echoReadAll(w, r, true, false) +} + +func echoReadAllWritePreparedMessage(w http.ResponseWriter, r *http.Request) { + echoReadAll(w, r, true, true) +} + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found.", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + io.WriteString(w, "Echo Server") +} + +var addr = flag.String("addr", ":9000", "http service address") + +func main() { + flag.Parse() + http.HandleFunc("/", serveHome) + http.HandleFunc("/c", echoCopyWriterOnly) + http.HandleFunc("/f", echoCopyFull) + http.HandleFunc("/r", echoReadAllWriter) + http.HandleFunc("/m", echoReadAllWriteMessage) + http.HandleFunc("/p", echoReadAllWritePreparedMessage) + err := http.ListenAndServe(*addr, nil) + if err != nil { + log.Fatal("ListenAndServe: ", err) + } +} + +type validator struct { + state int + x rune + r io.Reader +} + +var errInvalidUTF8 = errors.New("invalid utf8") + +func (r *validator) Read(p []byte) (int, error) { + n, err := r.r.Read(p) + state := r.state + x := r.x + for _, b := range p[:n] { + state, x = decode(state, x, b) + if state == utf8Reject { + break + } + } + r.state = state + r.x = x + if state == utf8Reject || (err == io.EOF && state != utf8Accept) { + return n, errInvalidUTF8 + } + return n, err +} + +// UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ +// +// Copyright (c) 2008-2009 Bjoern Hoehrmann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +var utf8d = [...]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df + 0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef + 0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8 +} + +const ( + utf8Accept = 0 + utf8Reject = 1 +) + +func decode(state int, x rune, b byte) (int, rune) { + t := utf8d[b] + if state != utf8Accept { + x = rune(b&0x3f) | (x << 6) + } else { + x = rune((0xff >> t) & b) + } + state = int(utf8d[256+state*16+int(t)]) + return state, x +} diff --git a/vendor/github.com/gorilla/websocket/examples/chat/README.md b/vendor/github.com/gorilla/websocket/examples/chat/README.md new file mode 100644 index 00000000..47c82f90 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/README.md @@ -0,0 +1,102 @@ +# Chat Example + +This application shows how to use use the +[websocket](https://github.com/gorilla/websocket) package to implement a simple +web chat application. + +## Running the example + +The example requires a working Go development environment. The [Getting +Started](http://golang.org/doc/install) page describes how to install the +development environment. + +Once you have Go up and running, you can download, build and run the example +using the following commands. + + $ go get github.com/gorilla/websocket + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat` + $ go run *.go + +To use the chat example, open http://localhost:8080/ in your browser. + +## Server + +The server application defines two types, `Client` and `Hub`. The server +creates an instance of the `Client` type for each websocket connection. A +`Client` acts as an intermediary between the websocket connection and a single +instance of the `Hub` type. The `Hub` maintains a set of registered clients and +broadcasts messages to the clients. + +The application runs one goroutine for the `Hub` and two goroutines for each +`Client`. The goroutines communicate with each other using channels. The `Hub` +has channels for registering clients, unregistering clients and broadcasting +messages. A `Client` has a buffered channel of outbound messages. One of the +client's goroutines reads messages from this channel and writes the messages to +the websocket. The other client goroutine reads messages from the websocket and +sends them to the hub. + +### Hub + +The code for the `Hub` type is in +[hub.go](https://github.com/gorilla/websocket/blob/master/examples/chat/hub.go). +The application's `main` function starts the hub's `run` method as a goroutine. +Clients send requests to the hub using the `register`, `unregister` and +`broadcast` channels. + +The hub registers clients by adding the client pointer as a key in the +`clients` map. The map value is always true. + +The unregister code is a little more complicated. In addition to deleting the +client pointer from the `clients` map, the hub closes the clients's `send` +channel to signal the client that no more messages will be sent to the client. + +The hub handles messages by looping over the registered clients and sending the +message to the client's `send` channel. If the client's `send` buffer is full, +then the hub assumes that the client is dead or stuck. In this case, the hub +unregisters the client and closes the websocket. + +### Client + +The code for the `Client` type is in [client.go](https://github.com/gorilla/websocket/blob/master/examples/chat/client.go). + +The `serveWs` function is registered by the application's `main` function as +an HTTP handler. The handler upgrades the HTTP connection to the WebSocket +protocol, creates a client, registers the client with the hub and schedules the +client to be unregistered using a defer statement. + +Next, the HTTP handler starts the client's `writePump` method as a goroutine. +This method transfers messages from the client's send channel to the websocket +connection. The writer method exits when the channel is closed by the hub or +there's an error writing to the websocket connection. + +Finally, the HTTP handler calls the client's `readPump` method. This method +transfers inbound messages from the websocket to the hub. + +WebSocket connections [support one concurrent reader and one concurrent +writer](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The +application ensures that these concurrency requirements are met by executing +all reads from the `readPump` goroutine and all writes from the `writePump` +goroutine. + +To improve efficiency under high load, the `writePump` function coalesces +pending chat messages in the `send` channel to a single WebSocket message. This +reduces the number of system calls and the amount of data sent over the +network. + +## Frontend + +The frontend code is in [home.html](https://github.com/gorilla/websocket/blob/master/examples/chat/home.html). + +On document load, the script checks for websocket functionality in the browser. +If websocket functionality is available, then the script opens a connection to +the server and registers a callback to handle messages from the server. The +callback appends the message to the chat log using the appendLog function. + +To allow the user to manually scroll through the chat log without interruption +from new messages, the `appendLog` function checks the scroll position before +adding new content. If the chat log is scrolled to the bottom, then the +function scrolls new content into view after adding the content. Otherwise, the +scroll position is not changed. + +The form handler writes the user input to the websocket and clears the input +field. diff --git a/vendor/github.com/gorilla/websocket/examples/chat/client.go b/vendor/github.com/gorilla/websocket/examples/chat/client.go new file mode 100644 index 00000000..ecfd9a7a --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/client.go @@ -0,0 +1,137 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "log" + "net/http" + "time" + + "github.com/gorilla/websocket" +) + +const ( + // Time allowed to write a message to the peer. + writeWait = 10 * time.Second + + // Time allowed to read the next pong message from the peer. + pongWait = 60 * time.Second + + // Send pings to peer with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 + + // Maximum message size allowed from peer. + maxMessageSize = 512 +) + +var ( + newline = []byte{'\n'} + space = []byte{' '} +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +// Client is a middleman between the websocket connection and the hub. +type Client struct { + hub *Hub + + // The websocket connection. + conn *websocket.Conn + + // Buffered channel of outbound messages. + send chan []byte +} + +// readPump pumps messages from the websocket connection to the hub. +// +// The application runs readPump in a per-connection goroutine. The application +// ensures that there is at most one reader on a connection by executing all +// reads from this goroutine. +func (c *Client) readPump() { + defer func() { + c.hub.unregister <- c + c.conn.Close() + }() + c.conn.SetReadLimit(maxMessageSize) + c.conn.SetReadDeadline(time.Now().Add(pongWait)) + c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, message, err := c.conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { + log.Printf("error: %v", err) + } + break + } + message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1)) + c.hub.broadcast <- message + } +} + +// writePump pumps messages from the hub to the websocket connection. +// +// A goroutine running writePump is started for each connection. The +// application ensures that there is at most one writer to a connection by +// executing all writes from this goroutine. +func (c *Client) writePump() { + ticker := time.NewTicker(pingPeriod) + defer func() { + ticker.Stop() + c.conn.Close() + }() + for { + select { + case message, ok := <-c.send: + c.conn.SetWriteDeadline(time.Now().Add(writeWait)) + if !ok { + // The hub closed the channel. + c.conn.WriteMessage(websocket.CloseMessage, []byte{}) + return + } + + w, err := c.conn.NextWriter(websocket.TextMessage) + if err != nil { + return + } + w.Write(message) + + // Add queued chat messages to the current websocket message. + n := len(c.send) + for i := 0; i < n; i++ { + w.Write(newline) + w.Write(<-c.send) + } + + if err := w.Close(); err != nil { + return + } + case <-ticker.C: + c.conn.SetWriteDeadline(time.Now().Add(writeWait)) + if err := c.conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil { + return + } + } + } +} + +// serveWs handles websocket requests from the peer. +func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println(err) + return + } + client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)} + client.hub.register <- client + + // Allow collection of memory referenced by the caller by doing all work in + // new goroutines. + go client.writePump() + go client.readPump() +} diff --git a/vendor/github.com/gorilla/websocket/examples/chat/home.html b/vendor/github.com/gorilla/websocket/examples/chat/home.html new file mode 100644 index 00000000..a39a0c27 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/home.html @@ -0,0 +1,98 @@ + + + +Chat Example + + + + +
+
+ + +
+ + diff --git a/vendor/github.com/gorilla/websocket/examples/chat/hub.go b/vendor/github.com/gorilla/websocket/examples/chat/hub.go new file mode 100644 index 00000000..7f07ea07 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/hub.go @@ -0,0 +1,53 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// hub maintains the set of active clients and broadcasts messages to the +// clients. +type Hub struct { + // Registered clients. + clients map[*Client]bool + + // Inbound messages from the clients. + broadcast chan []byte + + // Register requests from the clients. + register chan *Client + + // Unregister requests from clients. + unregister chan *Client +} + +func newHub() *Hub { + return &Hub{ + broadcast: make(chan []byte), + register: make(chan *Client), + unregister: make(chan *Client), + clients: make(map[*Client]bool), + } +} + +func (h *Hub) run() { + for { + select { + case client := <-h.register: + h.clients[client] = true + case client := <-h.unregister: + if _, ok := h.clients[client]; ok { + delete(h.clients, client) + close(client.send) + } + case message := <-h.broadcast: + for client := range h.clients { + select { + case client.send <- message: + default: + close(client.send) + delete(h.clients, client) + } + } + } + } +} diff --git a/vendor/github.com/gorilla/websocket/examples/chat/main.go b/vendor/github.com/gorilla/websocket/examples/chat/main.go new file mode 100644 index 00000000..74615d59 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/main.go @@ -0,0 +1,40 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "log" + "net/http" +) + +var addr = flag.String("addr", ":8080", "http service address") + +func serveHome(w http.ResponseWriter, r *http.Request) { + log.Println(r.URL) + if r.URL.Path != "/" { + http.Error(w, "Not found", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + http.ServeFile(w, r, "home.html") +} + +func main() { + flag.Parse() + hub := newHub() + go hub.run() + http.HandleFunc("/", serveHome) + http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { + serveWs(hub, w, r) + }) + err := http.ListenAndServe(*addr, nil) + if err != nil { + log.Fatal("ListenAndServe: ", err) + } +} diff --git a/vendor/github.com/gorilla/websocket/examples/command/README.md b/vendor/github.com/gorilla/websocket/examples/command/README.md new file mode 100644 index 00000000..ed6f7868 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/command/README.md @@ -0,0 +1,19 @@ +# Command example + +This example connects a websocket connection to stdin and stdout of a command. +Received messages are written to stdin followed by a `\n`. Each line read from +standard out is sent as a message to the client. + + $ go get github.com/gorilla/websocket + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/command` + $ go run main.go + # Open http://localhost:8080/ . + +Try the following commands. + + # Echo sent messages to the output area. + $ go run main.go cat + + # Run a shell.Try sending "ls" and "cat main.go". + $ go run main.go sh + diff --git a/vendor/github.com/gorilla/websocket/examples/command/home.html b/vendor/github.com/gorilla/websocket/examples/command/home.html new file mode 100644 index 00000000..19c46128 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/command/home.html @@ -0,0 +1,102 @@ + + + +Command Example + + + + +
+
+ + +
+ + diff --git a/vendor/github.com/gorilla/websocket/examples/command/main.go b/vendor/github.com/gorilla/websocket/examples/command/main.go new file mode 100644 index 00000000..239c5c85 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/command/main.go @@ -0,0 +1,193 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "flag" + "io" + "log" + "net/http" + "os" + "os/exec" + "time" + + "github.com/gorilla/websocket" +) + +var ( + addr = flag.String("addr", "127.0.0.1:8080", "http service address") + cmdPath string +) + +const ( + // Time allowed to write a message to the peer. + writeWait = 10 * time.Second + + // Maximum message size allowed from peer. + maxMessageSize = 8192 + + // Time allowed to read the next pong message from the peer. + pongWait = 60 * time.Second + + // Send pings to peer with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 + + // Time to wait before force close on connection. + closeGracePeriod = 10 * time.Second +) + +func pumpStdin(ws *websocket.Conn, w io.Writer) { + defer ws.Close() + ws.SetReadLimit(maxMessageSize) + ws.SetReadDeadline(time.Now().Add(pongWait)) + ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, message, err := ws.ReadMessage() + if err != nil { + break + } + message = append(message, '\n') + if _, err := w.Write(message); err != nil { + break + } + } +} + +func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) { + defer func() { + }() + s := bufio.NewScanner(r) + for s.Scan() { + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { + ws.Close() + break + } + } + if s.Err() != nil { + log.Println("scan:", s.Err()) + } + close(done) + + ws.SetWriteDeadline(time.Now().Add(writeWait)) + ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + time.Sleep(closeGracePeriod) + ws.Close() +} + +func ping(ws *websocket.Conn, done chan struct{}) { + ticker := time.NewTicker(pingPeriod) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { + log.Println("ping:", err) + } + case <-done: + return + } + } +} + +func internalError(ws *websocket.Conn, msg string, err error) { + log.Println(msg, err) + ws.WriteMessage(websocket.TextMessage, []byte("Internal server error.")) +} + +var upgrader = websocket.Upgrader{} + +func serveWs(w http.ResponseWriter, r *http.Request) { + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("upgrade:", err) + return + } + + defer ws.Close() + + outr, outw, err := os.Pipe() + if err != nil { + internalError(ws, "stdout:", err) + return + } + defer outr.Close() + defer outw.Close() + + inr, inw, err := os.Pipe() + if err != nil { + internalError(ws, "stdin:", err) + return + } + defer inr.Close() + defer inw.Close() + + proc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{ + Files: []*os.File{inr, outw, outw}, + }) + if err != nil { + internalError(ws, "start:", err) + return + } + + inr.Close() + outw.Close() + + stdoutDone := make(chan struct{}) + go pumpStdout(ws, outr, stdoutDone) + go ping(ws, stdoutDone) + + pumpStdin(ws, inw) + + // Some commands will exit when stdin is closed. + inw.Close() + + // Other commands need a bonk on the head. + if err := proc.Signal(os.Interrupt); err != nil { + log.Println("inter:", err) + } + + select { + case <-stdoutDone: + case <-time.After(time.Second): + // A bigger bonk on the head. + if err := proc.Signal(os.Kill); err != nil { + log.Println("term:", err) + } + <-stdoutDone + } + + if _, err := proc.Wait(); err != nil { + log.Println("wait:", err) + } +} + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + http.ServeFile(w, r, "home.html") +} + +func main() { + flag.Parse() + if len(flag.Args()) < 1 { + log.Fatal("must specify at least one argument") + } + var err error + cmdPath, err = exec.LookPath(flag.Args()[0]) + if err != nil { + log.Fatal(err) + } + http.HandleFunc("/", serveHome) + http.HandleFunc("/ws", serveWs) + log.Fatal(http.ListenAndServe(*addr, nil)) +} diff --git a/vendor/github.com/gorilla/websocket/examples/echo/README.md b/vendor/github.com/gorilla/websocket/examples/echo/README.md new file mode 100644 index 00000000..6ad79ed7 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/echo/README.md @@ -0,0 +1,17 @@ +# Client and server example + +This example shows a simple client and server. + +The server echoes messages sent to it. The client sends a message every second +and prints all messages received. + +To run the example, start the server: + + $ go run server.go + +Next, start the client: + + $ go run client.go + +The server includes a simple web client. To use the client, open +http://127.0.0.1:8080 in the browser and follow the instructions on the page. diff --git a/vendor/github.com/gorilla/websocket/examples/echo/client.go b/vendor/github.com/gorilla/websocket/examples/echo/client.go new file mode 100644 index 00000000..6578094e --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/echo/client.go @@ -0,0 +1,81 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "flag" + "log" + "net/url" + "os" + "os/signal" + "time" + + "github.com/gorilla/websocket" +) + +var addr = flag.String("addr", "localhost:8080", "http service address") + +func main() { + flag.Parse() + log.SetFlags(0) + + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt) + + u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"} + log.Printf("connecting to %s", u.String()) + + c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + log.Fatal("dial:", err) + } + defer c.Close() + + done := make(chan struct{}) + + go func() { + defer c.Close() + defer close(done) + for { + _, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + return + } + log.Printf("recv: %s", message) + } + }() + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + for { + select { + case t := <-ticker.C: + err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) + if err != nil { + log.Println("write:", err) + return + } + case <-interrupt: + log.Println("interrupt") + // To cleanly close a connection, a client should send a close + // frame and wait for the server to close the connection. + err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + if err != nil { + log.Println("write close:", err) + return + } + select { + case <-done: + case <-time.After(time.Second): + } + c.Close() + return + } + } +} diff --git a/vendor/github.com/gorilla/websocket/examples/echo/server.go b/vendor/github.com/gorilla/websocket/examples/echo/server.go new file mode 100644 index 00000000..a685b097 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/echo/server.go @@ -0,0 +1,132 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "flag" + "html/template" + "log" + "net/http" + + "github.com/gorilla/websocket" +) + +var addr = flag.String("addr", "localhost:8080", "http service address") + +var upgrader = websocket.Upgrader{} // use default options + +func echo(w http.ResponseWriter, r *http.Request) { + c, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Print("upgrade:", err) + return + } + defer c.Close() + for { + mt, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", message) + err = c.WriteMessage(mt, message) + if err != nil { + log.Println("write:", err) + break + } + } +} + +func home(w http.ResponseWriter, r *http.Request) { + homeTemplate.Execute(w, "ws://"+r.Host+"/echo") +} + +func main() { + flag.Parse() + log.SetFlags(0) + http.HandleFunc("/echo", echo) + http.HandleFunc("/", home) + log.Fatal(http.ListenAndServe(*addr, nil)) +} + +var homeTemplate = template.Must(template.New("").Parse(` + + + + + + + +
+

Click "Open" to create a connection to the server, +"Send" to send a message to the server and "Close" to close the connection. +You can change the message and send multiple times. +

+

+ + +

+ +

+
+
+
+ + +`)) diff --git a/vendor/github.com/gorilla/websocket/examples/filewatch/README.md b/vendor/github.com/gorilla/websocket/examples/filewatch/README.md new file mode 100644 index 00000000..ca4931f3 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/filewatch/README.md @@ -0,0 +1,9 @@ +# File Watch example. + +This example sends a file to the browser client for display whenever the file is modified. + + $ go get github.com/gorilla/websocket + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/filewatch` + $ go run main.go + # Open http://localhost:8080/ . + # Modify the file to see it update in the browser. diff --git a/vendor/github.com/gorilla/websocket/examples/filewatch/main.go b/vendor/github.com/gorilla/websocket/examples/filewatch/main.go new file mode 100644 index 00000000..f5f9da5c --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/filewatch/main.go @@ -0,0 +1,193 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "html/template" + "io/ioutil" + "log" + "net/http" + "os" + "strconv" + "time" + + "github.com/gorilla/websocket" +) + +const ( + // Time allowed to write the file to the client. + writeWait = 10 * time.Second + + // Time allowed to read the next pong message from the client. + pongWait = 60 * time.Second + + // Send pings to client with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 + + // Poll file for changes with this period. + filePeriod = 10 * time.Second +) + +var ( + addr = flag.String("addr", ":8080", "http service address") + homeTempl = template.Must(template.New("").Parse(homeHTML)) + filename string + upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + } +) + +func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) { + fi, err := os.Stat(filename) + if err != nil { + return nil, lastMod, err + } + if !fi.ModTime().After(lastMod) { + return nil, lastMod, nil + } + p, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fi.ModTime(), err + } + return p, fi.ModTime(), nil +} + +func reader(ws *websocket.Conn) { + defer ws.Close() + ws.SetReadLimit(512) + ws.SetReadDeadline(time.Now().Add(pongWait)) + ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, _, err := ws.ReadMessage() + if err != nil { + break + } + } +} + +func writer(ws *websocket.Conn, lastMod time.Time) { + lastError := "" + pingTicker := time.NewTicker(pingPeriod) + fileTicker := time.NewTicker(filePeriod) + defer func() { + pingTicker.Stop() + fileTicker.Stop() + ws.Close() + }() + for { + select { + case <-fileTicker.C: + var p []byte + var err error + + p, lastMod, err = readFileIfModified(lastMod) + + if err != nil { + if s := err.Error(); s != lastError { + lastError = s + p = []byte(lastError) + } + } else { + lastError = "" + } + + if p != nil { + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.TextMessage, p); err != nil { + return + } + } + case <-pingTicker.C: + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { + return + } + } + } +} + +func serveWs(w http.ResponseWriter, r *http.Request) { + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + if _, ok := err.(websocket.HandshakeError); !ok { + log.Println(err) + } + return + } + + var lastMod time.Time + if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil { + lastMod = time.Unix(0, n) + } + + go writer(ws, lastMod) + reader(ws) +} + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + p, lastMod, err := readFileIfModified(time.Time{}) + if err != nil { + p = []byte(err.Error()) + lastMod = time.Unix(0, 0) + } + var v = struct { + Host string + Data string + LastMod string + }{ + r.Host, + string(p), + strconv.FormatInt(lastMod.UnixNano(), 16), + } + homeTempl.Execute(w, &v) +} + +func main() { + flag.Parse() + if flag.NArg() != 1 { + log.Fatal("filename not specified") + } + filename = flag.Args()[0] + http.HandleFunc("/", serveHome) + http.HandleFunc("/ws", serveWs) + if err := http.ListenAndServe(*addr, nil); err != nil { + log.Fatal(err) + } +} + +const homeHTML = ` + + + WebSocket Example + + +
{{.Data}}
+ + + +` diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go new file mode 100644 index 00000000..4f0e3687 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/json.go @@ -0,0 +1,55 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "encoding/json" + "io" +) + +// WriteJSON is deprecated, use c.WriteJSON instead. +func WriteJSON(c *Conn, v interface{}) error { + return c.WriteJSON(v) +} + +// WriteJSON writes the JSON encoding of v to the connection. +// +// See the documentation for encoding/json Marshal for details about the +// conversion of Go values to JSON. +func (c *Conn) WriteJSON(v interface{}) error { + w, err := c.NextWriter(TextMessage) + if err != nil { + return err + } + err1 := json.NewEncoder(w).Encode(v) + err2 := w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +// ReadJSON is deprecated, use c.ReadJSON instead. +func ReadJSON(c *Conn, v interface{}) error { + return c.ReadJSON(v) +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// See the documentation for the encoding/json Unmarshal function for details +// about the conversion of JSON to a Go value. +func (c *Conn) ReadJSON(v interface{}) error { + _, r, err := c.NextReader() + if err != nil { + return err + } + err = json.NewDecoder(r).Decode(v) + if err == io.EOF { + // One value is expected in the message. + err = io.ErrUnexpectedEOF + } + return err +} diff --git a/vendor/github.com/gorilla/websocket/json_test.go b/vendor/github.com/gorilla/websocket/json_test.go new file mode 100644 index 00000000..61100e48 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/json_test.go @@ -0,0 +1,119 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "encoding/json" + "io" + "reflect" + "testing" +) + +func TestJSON(t *testing.T) { + var buf bytes.Buffer + c := fakeNetConn{&buf, &buf} + wc := newConn(c, true, 1024, 1024) + rc := newConn(c, false, 1024, 1024) + + var actual, expect struct { + A int + B string + } + expect.A = 1 + expect.B = "hello" + + if err := wc.WriteJSON(&expect); err != nil { + t.Fatal("write", err) + } + + if err := rc.ReadJSON(&actual); err != nil { + t.Fatal("read", err) + } + + if !reflect.DeepEqual(&actual, &expect) { + t.Fatal("equal", actual, expect) + } +} + +func TestPartialJSONRead(t *testing.T) { + var buf bytes.Buffer + c := fakeNetConn{&buf, &buf} + wc := newConn(c, true, 1024, 1024) + rc := newConn(c, false, 1024, 1024) + + var v struct { + A int + B string + } + v.A = 1 + v.B = "hello" + + messageCount := 0 + + // Partial JSON values. + + data, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + for i := len(data) - 1; i >= 0; i-- { + if err := wc.WriteMessage(TextMessage, data[:i]); err != nil { + t.Fatal(err) + } + messageCount++ + } + + // Whitespace. + + if err := wc.WriteMessage(TextMessage, []byte(" ")); err != nil { + t.Fatal(err) + } + messageCount++ + + // Close. + + if err := wc.WriteMessage(CloseMessage, FormatCloseMessage(CloseNormalClosure, "")); err != nil { + t.Fatal(err) + } + + for i := 0; i < messageCount; i++ { + err := rc.ReadJSON(&v) + if err != io.ErrUnexpectedEOF { + t.Error("read", i, err) + } + } + + err = rc.ReadJSON(&v) + if _, ok := err.(*CloseError); !ok { + t.Error("final", err) + } +} + +func TestDeprecatedJSON(t *testing.T) { + var buf bytes.Buffer + c := fakeNetConn{&buf, &buf} + wc := newConn(c, true, 1024, 1024) + rc := newConn(c, false, 1024, 1024) + + var actual, expect struct { + A int + B string + } + expect.A = 1 + expect.B = "hello" + + if err := WriteJSON(wc, &expect); err != nil { + t.Fatal("write", err) + } + + if err := ReadJSON(rc, &actual); err != nil { + t.Fatal("read", err) + } + + if !reflect.DeepEqual(&actual, &expect) { + t.Fatal("equal", actual, expect) + } +} diff --git a/vendor/github.com/gorilla/websocket/mask.go b/vendor/github.com/gorilla/websocket/mask.go new file mode 100644 index 00000000..6a88bbc7 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/mask.go @@ -0,0 +1,55 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// +build !appengine + +package websocket + +import "unsafe" + +const wordSize = int(unsafe.Sizeof(uintptr(0))) + +func maskBytes(key [4]byte, pos int, b []byte) int { + + // Mask one byte at a time for small buffers. + if len(b) < 2*wordSize { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 + } + + // Mask one byte at a time to word boundary. + if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { + n = wordSize - n + for i := range b[:n] { + b[i] ^= key[pos&3] + pos++ + } + b = b[n:] + } + + // Create aligned word size key. + var k [wordSize]byte + for i := range k { + k[i] = key[(pos+i)&3] + } + kw := *(*uintptr)(unsafe.Pointer(&k)) + + // Mask one word at a time. + n := (len(b) / wordSize) * wordSize + for i := 0; i < n; i += wordSize { + *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw + } + + // Mask one byte at a time for remaining bytes. + b = b[n:] + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + + return pos & 3 +} diff --git a/vendor/github.com/gorilla/websocket/mask_safe.go b/vendor/github.com/gorilla/websocket/mask_safe.go new file mode 100644 index 00000000..2aac060e --- /dev/null +++ b/vendor/github.com/gorilla/websocket/mask_safe.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// +build appengine + +package websocket + +func maskBytes(key [4]byte, pos int, b []byte) int { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 +} diff --git a/vendor/github.com/gorilla/websocket/mask_test.go b/vendor/github.com/gorilla/websocket/mask_test.go new file mode 100644 index 00000000..298a1e50 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/mask_test.go @@ -0,0 +1,73 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// Require 1.7 for sub-bencmarks +// +build go1.7,!appengine + +package websocket + +import ( + "fmt" + "testing" +) + +func maskBytesByByte(key [4]byte, pos int, b []byte) int { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 +} + +func notzero(b []byte) int { + for i := range b { + if b[i] != 0 { + return i + } + } + return -1 +} + +func TestMaskBytes(t *testing.T) { + key := [4]byte{1, 2, 3, 4} + for size := 1; size <= 1024; size++ { + for align := 0; align < wordSize; align++ { + for pos := 0; pos < 4; pos++ { + b := make([]byte, size+align)[align:] + maskBytes(key, pos, b) + maskBytesByByte(key, pos, b) + if i := notzero(b); i >= 0 { + t.Errorf("size:%d, align:%d, pos:%d, offset:%d", size, align, pos, i) + } + } + } + } +} + +func BenchmarkMaskBytes(b *testing.B) { + for _, size := range []int{2, 4, 8, 16, 32, 512, 1024} { + b.Run(fmt.Sprintf("size-%d", size), func(b *testing.B) { + for _, align := range []int{wordSize / 2} { + b.Run(fmt.Sprintf("align-%d", align), func(b *testing.B) { + for _, fn := range []struct { + name string + fn func(key [4]byte, pos int, b []byte) int + }{ + {"byte", maskBytesByByte}, + {"word", maskBytes}, + } { + b.Run(fn.name, func(b *testing.B) { + key := newMaskKey() + data := make([]byte, size+align)[align:] + for i := 0; i < b.N; i++ { + fn.fn(key, 0, data) + } + b.SetBytes(int64(len(data))) + }) + } + }) + } + }) + } +} diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go new file mode 100644 index 00000000..1efffbd1 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/prepared.go @@ -0,0 +1,103 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "net" + "sync" + "time" +) + +// PreparedMessage caches on the wire representations of a message payload. +// Use PreparedMessage to efficiently send a message payload to multiple +// connections. PreparedMessage is especially useful when compression is used +// because the CPU and memory expensive compression operation can be executed +// once for a given set of compression options. +type PreparedMessage struct { + messageType int + data []byte + err error + mu sync.Mutex + frames map[prepareKey]*preparedFrame +} + +// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. +type prepareKey struct { + isServer bool + compress bool + compressionLevel int +} + +// preparedFrame contains data in wire representation. +type preparedFrame struct { + once sync.Once + data []byte +} + +// NewPreparedMessage returns an initialized PreparedMessage. You can then send +// it to connection using WritePreparedMessage method. Valid wire +// representation will be calculated lazily only once for a set of current +// connection options. +func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { + pm := &PreparedMessage{ + messageType: messageType, + frames: make(map[prepareKey]*preparedFrame), + data: data, + } + + // Prepare a plain server frame. + _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) + if err != nil { + return nil, err + } + + // To protect against caller modifying the data argument, remember the data + // copied to the plain server frame. + pm.data = frameData[len(frameData)-len(data):] + return pm, nil +} + +func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { + pm.mu.Lock() + frame, ok := pm.frames[key] + if !ok { + frame = &preparedFrame{} + pm.frames[key] = frame + } + pm.mu.Unlock() + + var err error + frame.once.Do(func() { + // Prepare a frame using a 'fake' connection. + // TODO: Refactor code in conn.go to allow more direct construction of + // the frame. + mu := make(chan bool, 1) + mu <- true + var nc prepareConn + c := &Conn{ + conn: &nc, + mu: mu, + isServer: key.isServer, + compressionLevel: key.compressionLevel, + enableWriteCompression: true, + writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), + } + if key.compress { + c.newCompressionWriter = compressNoContextTakeover + } + err = c.WriteMessage(pm.messageType, pm.data) + frame.data = nc.buf.Bytes() + }) + return pm.messageType, frame.data, err +} + +type prepareConn struct { + buf bytes.Buffer + net.Conn +} + +func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } +func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/vendor/github.com/gorilla/websocket/prepared_test.go b/vendor/github.com/gorilla/websocket/prepared_test.go new file mode 100644 index 00000000..cf98c6c1 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/prepared_test.go @@ -0,0 +1,74 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "compress/flate" + "math/rand" + "testing" +) + +var preparedMessageTests = []struct { + messageType int + isServer bool + enableWriteCompression bool + compressionLevel int +}{ + // Server + {TextMessage, true, false, flate.BestSpeed}, + {TextMessage, true, true, flate.BestSpeed}, + {TextMessage, true, true, flate.BestCompression}, + {PingMessage, true, false, flate.BestSpeed}, + {PingMessage, true, true, flate.BestSpeed}, + + // Client + {TextMessage, false, false, flate.BestSpeed}, + {TextMessage, false, true, flate.BestSpeed}, + {TextMessage, false, true, flate.BestCompression}, + {PingMessage, false, false, flate.BestSpeed}, + {PingMessage, false, true, flate.BestSpeed}, +} + +func TestPreparedMessage(t *testing.T) { + for _, tt := range preparedMessageTests { + var data = []byte("this is a test") + var buf bytes.Buffer + c := newConn(fakeNetConn{Reader: nil, Writer: &buf}, tt.isServer, 1024, 1024) + if tt.enableWriteCompression { + c.newCompressionWriter = compressNoContextTakeover + } + c.SetCompressionLevel(tt.compressionLevel) + + // Seed random number generator for consistent frame mask. + rand.Seed(1234) + + if err := c.WriteMessage(tt.messageType, data); err != nil { + t.Fatal(err) + } + want := buf.String() + + pm, err := NewPreparedMessage(tt.messageType, data) + if err != nil { + t.Fatal(err) + } + + // Scribble on data to ensure that NewPreparedMessage takes a snapshot. + copy(data, "hello world") + + // Seed random number generator for consistent frame mask. + rand.Seed(1234) + + buf.Reset() + if err := c.WritePreparedMessage(pm); err != nil { + t.Fatal(err) + } + got := buf.String() + + if got != want { + t.Errorf("write message != prepared message for %+v", tt) + } + } +} diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go new file mode 100644 index 00000000..3495e0f1 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/server.go @@ -0,0 +1,291 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "errors" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +// HandshakeError describes an error with the handshake from the peer. +type HandshakeError struct { + message string +} + +func (e HandshakeError) Error() string { return e.message } + +// Upgrader specifies parameters for upgrading an HTTP connection to a +// WebSocket connection. +type Upgrader struct { + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // size is zero, then buffers allocated by the HTTP server are used. The + // I/O buffer sizes do not limit the size of the messages that can be sent + // or received. + ReadBufferSize, WriteBufferSize int + + // Subprotocols specifies the server's supported protocols in order of + // preference. If this field is set, then the Upgrade method negotiates a + // subprotocol by selecting the first match in this list with a protocol + // requested by the client. + Subprotocols []string + + // Error specifies the function for generating HTTP error responses. If Error + // is nil, then http.Error is used to generate the HTTP response. + Error func(w http.ResponseWriter, r *http.Request, status int, reason error) + + // CheckOrigin returns true if the request Origin header is acceptable. If + // CheckOrigin is nil, the host in the Origin header must not be set or + // must match the host of the request. + CheckOrigin func(r *http.Request) bool + + // EnableCompression specify if the server should attempt to negotiate per + // message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool +} + +func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { + err := HandshakeError{reason} + if u.Error != nil { + u.Error(w, r, status, err) + } else { + w.Header().Set("Sec-Websocket-Version", "13") + http.Error(w, http.StatusText(status), status) + } + return nil, err +} + +// checkSameOrigin returns true if the origin is not set or is equal to the request host. +func checkSameOrigin(r *http.Request) bool { + origin := r.Header["Origin"] + if len(origin) == 0 { + return true + } + u, err := url.Parse(origin[0]) + if err != nil { + return false + } + return u.Host == r.Host +} + +func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { + if u.Subprotocols != nil { + clientProtocols := Subprotocols(r) + for _, serverProtocol := range u.Subprotocols { + for _, clientProtocol := range clientProtocols { + if clientProtocol == serverProtocol { + return clientProtocol + } + } + } + } else if responseHeader != nil { + return responseHeader.Get("Sec-Websocket-Protocol") + } + return "" +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// application negotiated subprotocol (Sec-Websocket-Protocol). +// +// If the upgrade fails, then Upgrade replies to the client with an HTTP error +// response. +func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { + if r.Method != "GET" { + return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET") + } + + if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported") + } + + if !tokenListContainsValue(r.Header, "Connection", "upgrade") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header") + } + + if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header") + } + + if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") + } + + checkOrigin := u.CheckOrigin + if checkOrigin == nil { + checkOrigin = checkSameOrigin + } + if !checkOrigin(r) { + return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed") + } + + challengeKey := r.Header.Get("Sec-Websocket-Key") + if challengeKey == "" { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank") + } + + subprotocol := u.selectSubprotocol(r, responseHeader) + + // Negotiate PMCE + var compress bool + if u.EnableCompression { + for _, ext := range parseExtensions(r.Header) { + if ext[""] != "permessage-deflate" { + continue + } + compress = true + break + } + } + + var ( + netConn net.Conn + err error + ) + + h, ok := w.(http.Hijacker) + if !ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") + } + var brw *bufio.ReadWriter + netConn, brw, err = h.Hijack() + if err != nil { + return u.returnError(w, r, http.StatusInternalServerError, err.Error()) + } + + if brw.Reader.Buffered() > 0 { + netConn.Close() + return nil, errors.New("websocket: client sent data before handshake is complete") + } + + c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw) + c.subprotocol = subprotocol + + if compress { + c.newCompressionWriter = compressNoContextTakeover + c.newDecompressionReader = decompressNoContextTakeover + } + + p := c.writeBuf[:0] + p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) + p = append(p, computeAcceptKey(challengeKey)...) + p = append(p, "\r\n"...) + if c.subprotocol != "" { + p = append(p, "Sec-Websocket-Protocol: "...) + p = append(p, c.subprotocol...) + p = append(p, "\r\n"...) + } + if compress { + p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) + } + for k, vs := range responseHeader { + if k == "Sec-Websocket-Protocol" { + continue + } + for _, v := range vs { + p = append(p, k...) + p = append(p, ": "...) + for i := 0; i < len(v); i++ { + b := v[i] + if b <= 31 { + // prevent response splitting. + b = ' ' + } + p = append(p, b) + } + p = append(p, "\r\n"...) + } + } + p = append(p, "\r\n"...) + + // Clear deadlines set by HTTP server. + netConn.SetDeadline(time.Time{}) + + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) + } + if _, err = netConn.Write(p); err != nil { + netConn.Close() + return nil, err + } + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Time{}) + } + + return c, nil +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// This function is deprecated, use websocket.Upgrader instead. +// +// The application is responsible for checking the request origin before +// calling Upgrade. An example implementation of the same origin policy is: +// +// if req.Header.Get("Origin") != "http://"+req.Host { +// http.Error(w, "Origin not allowed", 403) +// return +// } +// +// If the endpoint supports subprotocols, then the application is responsible +// for negotiating the protocol used on the connection. Use the Subprotocols() +// function to get the subprotocols requested by the client. Use the +// Sec-Websocket-Protocol response header to specify the subprotocol selected +// by the application. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// negotiated subprotocol (Sec-Websocket-Protocol). +// +// The connection buffers IO to the underlying network connection. The +// readBufSize and writeBufSize parameters specify the size of the buffers to +// use. Messages can be larger than the buffers. +// +// If the request is not a valid WebSocket handshake, then Upgrade returns an +// error of type HandshakeError. Applications should handle this error by +// replying to the client with an HTTP error response. +func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { + u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} + u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { + // don't return errors to maintain backwards compatibility + } + u.CheckOrigin = func(r *http.Request) bool { + // allow all connections by default + return true + } + return u.Upgrade(w, r, responseHeader) +} + +// Subprotocols returns the subprotocols requested by the client in the +// Sec-Websocket-Protocol header. +func Subprotocols(r *http.Request) []string { + h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) + if h == "" { + return nil + } + protocols := strings.Split(h, ",") + for i := range protocols { + protocols[i] = strings.TrimSpace(protocols[i]) + } + return protocols +} + +// IsWebSocketUpgrade returns true if the client requested upgrade to the +// WebSocket protocol. +func IsWebSocketUpgrade(r *http.Request) bool { + return tokenListContainsValue(r.Header, "Connection", "upgrade") && + tokenListContainsValue(r.Header, "Upgrade", "websocket") +} diff --git a/vendor/github.com/gorilla/websocket/server_test.go b/vendor/github.com/gorilla/websocket/server_test.go new file mode 100644 index 00000000..0a28141d --- /dev/null +++ b/vendor/github.com/gorilla/websocket/server_test.go @@ -0,0 +1,51 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "net/http" + "reflect" + "testing" +) + +var subprotocolTests = []struct { + h string + protocols []string +}{ + {"", nil}, + {"foo", []string{"foo"}}, + {"foo,bar", []string{"foo", "bar"}}, + {"foo, bar", []string{"foo", "bar"}}, + {" foo, bar", []string{"foo", "bar"}}, + {" foo, bar ", []string{"foo", "bar"}}, +} + +func TestSubprotocols(t *testing.T) { + for _, st := range subprotocolTests { + r := http.Request{Header: http.Header{"Sec-Websocket-Protocol": {st.h}}} + protocols := Subprotocols(&r) + if !reflect.DeepEqual(st.protocols, protocols) { + t.Errorf("SubProtocols(%q) returned %#v, want %#v", st.h, protocols, st.protocols) + } + } +} + +var isWebSocketUpgradeTests = []struct { + ok bool + h http.Header +}{ + {false, http.Header{"Upgrade": {"websocket"}}}, + {false, http.Header{"Connection": {"upgrade"}}}, + {true, http.Header{"Connection": {"upgRade"}, "Upgrade": {"WebSocket"}}}, +} + +func TestIsWebSocketUpgrade(t *testing.T) { + for _, tt := range isWebSocketUpgradeTests { + ok := IsWebSocketUpgrade(&http.Request{Header: tt.h}) + if tt.ok != ok { + t.Errorf("IsWebSocketUpgrade(%v) returned %v, want %v", tt.h, ok, tt.ok) + } + } +} diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go new file mode 100644 index 00000000..9a4908df --- /dev/null +++ b/vendor/github.com/gorilla/websocket/util.go @@ -0,0 +1,214 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "io" + "net/http" + "strings" +) + +var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") + +func computeAcceptKey(challengeKey string) string { + h := sha1.New() + h.Write([]byte(challengeKey)) + h.Write(keyGUID) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +func generateChallengeKey() (string, error) { + p := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, p); err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(p), nil +} + +// Octet types from RFC 2616. +var octetTypes [256]byte + +const ( + isTokenOctet = 1 << iota + isSpaceOctet +) + +func init() { + // From RFC 2616 + // + // OCTET = + // CHAR = + // CTL = + // CR = + // LF = + // SP = + // HT = + // <"> = + // CRLF = CR LF + // LWS = [CRLF] 1*( SP | HT ) + // TEXT = + // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT + // token = 1* + // qdtext = > + + for c := 0; c < 256; c++ { + var t byte + isCtl := c <= 31 || c == 127 + isChar := 0 <= c && c <= 127 + isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 + if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { + t |= isSpaceOctet + } + if isChar && !isCtl && !isSeparator { + t |= isTokenOctet + } + octetTypes[c] = t + } +} + +func skipSpace(s string) (rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isSpaceOctet == 0 { + break + } + } + return s[i:] +} + +func nextToken(s string) (token, rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isTokenOctet == 0 { + break + } + } + return s[:i], s[i:] +} + +func nextTokenOrQuoted(s string) (value string, rest string) { + if !strings.HasPrefix(s, "\"") { + return nextToken(s) + } + s = s[1:] + for i := 0; i < len(s); i++ { + switch s[i] { + case '"': + return s[:i], s[i+1:] + case '\\': + p := make([]byte, len(s)-1) + j := copy(p, s[:i]) + escape := true + for i = i + 1; i < len(s); i++ { + b := s[i] + switch { + case escape: + escape = false + p[j] = b + j += 1 + case b == '\\': + escape = true + case b == '"': + return string(p[:j]), s[i+1:] + default: + p[j] = b + j += 1 + } + } + return "", "" + } + } + return "", "" +} + +// tokenListContainsValue returns true if the 1#token header with the given +// name contains token. +func tokenListContainsValue(header http.Header, name string, value string) bool { +headers: + for _, s := range header[name] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + s = skipSpace(s) + if s != "" && s[0] != ',' { + continue headers + } + if strings.EqualFold(t, value) { + return true + } + if s == "" { + continue headers + } + s = s[1:] + } + } + return false +} + +// parseExtensiosn parses WebSocket extensions from a header. +func parseExtensions(header http.Header) []map[string]string { + + // From RFC 6455: + // + // Sec-WebSocket-Extensions = extension-list + // extension-list = 1#extension + // extension = extension-token *( ";" extension-param ) + // extension-token = registered-token + // registered-token = token + // extension-param = token [ "=" (token | quoted-string) ] + // ;When using the quoted-string syntax variant, the value + // ;after quoted-string unescaping MUST conform to the + // ;'token' ABNF. + + var result []map[string]string +headers: + for _, s := range header["Sec-Websocket-Extensions"] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + ext := map[string]string{"": t} + for { + s = skipSpace(s) + if !strings.HasPrefix(s, ";") { + break + } + var k string + k, s = nextToken(skipSpace(s[1:])) + if k == "" { + continue headers + } + s = skipSpace(s) + var v string + if strings.HasPrefix(s, "=") { + v, s = nextTokenOrQuoted(skipSpace(s[1:])) + s = skipSpace(s) + } + if s != "" && s[0] != ',' && s[0] != ';' { + continue headers + } + ext[k] = v + } + if s != "" && s[0] != ',' { + continue headers + } + result = append(result, ext) + if s == "" { + continue headers + } + s = s[1:] + } + } + return result +} diff --git a/vendor/github.com/gorilla/websocket/util_test.go b/vendor/github.com/gorilla/websocket/util_test.go new file mode 100644 index 00000000..610e613c --- /dev/null +++ b/vendor/github.com/gorilla/websocket/util_test.go @@ -0,0 +1,74 @@ +// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "net/http" + "reflect" + "testing" +) + +var tokenListContainsValueTests = []struct { + value string + ok bool +}{ + {"WebSocket", true}, + {"WEBSOCKET", true}, + {"websocket", true}, + {"websockets", false}, + {"x websocket", false}, + {"websocket x", false}, + {"other,websocket,more", true}, + {"other, websocket, more", true}, +} + +func TestTokenListContainsValue(t *testing.T) { + for _, tt := range tokenListContainsValueTests { + h := http.Header{"Upgrade": {tt.value}} + ok := tokenListContainsValue(h, "Upgrade", "websocket") + if ok != tt.ok { + t.Errorf("tokenListContainsValue(h, n, %q) = %v, want %v", tt.value, ok, tt.ok) + } + } +} + +var parseExtensionTests = []struct { + value string + extensions []map[string]string +}{ + {`foo`, []map[string]string{map[string]string{"": "foo"}}}, + {`foo, bar; baz=2`, []map[string]string{ + map[string]string{"": "foo"}, + map[string]string{"": "bar", "baz": "2"}}}, + {`foo; bar="b,a;z"`, []map[string]string{ + map[string]string{"": "foo", "bar": "b,a;z"}}}, + {`foo , bar; baz = 2`, []map[string]string{ + map[string]string{"": "foo"}, + map[string]string{"": "bar", "baz": "2"}}}, + {`foo, bar; baz=2 junk`, []map[string]string{ + map[string]string{"": "foo"}}}, + {`foo junk, bar; baz=2 junk`, nil}, + {`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{ + map[string]string{"": "mux", "max-channels": "4", "flow-control": ""}, + map[string]string{"": "deflate-stream"}}}, + {`permessage-foo; x="10"`, []map[string]string{ + map[string]string{"": "permessage-foo", "x": "10"}}}, + {`permessage-foo; use_y, permessage-foo`, []map[string]string{ + map[string]string{"": "permessage-foo", "use_y": ""}, + map[string]string{"": "permessage-foo"}}}, + {`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{ + map[string]string{"": "permessage-deflate", "client_max_window_bits": "", "server_max_window_bits": "10"}, + map[string]string{"": "permessage-deflate", "client_max_window_bits": ""}}}, +} + +func TestParseExtensions(t *testing.T) { + for _, tt := range parseExtensionTests { + h := http.Header{http.CanonicalHeaderKey("Sec-WebSocket-Extensions"): {tt.value}} + extensions := parseExtensions(h) + if !reflect.DeepEqual(extensions, tt.extensions) { + t.Errorf("parseExtensions(%q)\n = %v,\nwant %v", tt.value, extensions, tt.extensions) + } + } +} diff --git a/vendor/github.com/inconshreveable/go-vhost/LICENSE b/vendor/github.com/inconshreveable/go-vhost/LICENSE new file mode 100644 index 00000000..5f0d1fb6 --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/LICENSE @@ -0,0 +1,13 @@ +Copyright 2014 Alan Shreve + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/inconshreveable/go-vhost/README.md b/vendor/github.com/inconshreveable/go-vhost/README.md new file mode 100644 index 00000000..df8a9795 --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/README.md @@ -0,0 +1,110 @@ +# go-vhost +go-vhost is a simple library that lets you implement virtual hosting functionality for different protocols (HTTP and TLS so far). go-vhost has a high-level and a low-level interface. The high-level interface lets you wrap existing net.Listeners with "muxer" objects. You can then Listen() on a muxer for a particular virtual host name of interest which will return to you a net.Listener for just connections with the virtual hostname of interest. + +The lower-level go-vhost interface are just functions which extract the name/routing information for the given protocol and return an object implementing net.Conn which works as if no bytes had been consumed. + +### [API Documentation](https://godoc.org/github.com/inconshreveable/go-vhost) + +### Usage +```go +l, _ := net.Listen("tcp", *listen) + +// start multiplexing on it +mux, _ := vhost.NewHTTPMuxer(l, muxTimeout) + +// listen for connections to different domains +for _, v := range virtualHosts { + vhost := v + + // vhost.Name is a virtual hostname like "foo.example.com" + muxListener, _ := mux.Listen(vhost.Name()) + + go func(vh virtualHost, ml net.Listener) { + for { + conn, _ := ml.Accept() + go vh.Handle(conn) + } + }(vhost, muxListener) +} + +for { + conn, err := mux.NextError() + + switch err.(type) { + case vhost.BadRequest: + log.Printf("got a bad request!") + conn.Write([]byte("bad request")) + case vhost.NotFound: + log.Printf("got a connection for an unknown vhost") + conn.Write([]byte("vhost not found")) + case vhost.Closed: + log.Printf("closed conn: %s", err) + default: + if conn != nil { + conn.Write([]byte("server error")) + } + } + + if conn != nil { + conn.Close() + } +} +``` +### Low-level API usage +```go +// accept a new connection +conn, _ := listener.Accept() + +// parse out the HTTP request and the Host header +if vhostConn, err = vhost.HTTP(conn); err != nil { + panic("Not a valid http connection!") +} + +fmt.Printf("Target Host: ", vhostConn.Host()) +// Target Host: example.com + +// vhostConn contains the entire request as if no bytes had been consumed +bytes, _ := ioutil.ReadAll(vhostConn) +fmt.Printf("%s", bytes) +// GET / HTTP/1.1 +// Host: example.com +// User-Agent: ... +// ... +``` + +### Advanced introspection +The entire HTTP request headers are available for inspection in case you want to mux on something besides the Host header: +```go +// parse out the HTTP request and the Host header +if vhostConn, err = vhost.HTTP(conn); err != nil { + panic("Not a valid http connection!") +} + +httpVersion := vhost.Request.MinorVersion +customRouting := vhost.Request.Header["X-Custom-Routing-Header"] +``` + +Likewise for TLS, you can look at detailed information about the ClientHello message: +```go +if vhostConn, err = vhost.TLS(conn); err != nil { + panic("Not a valid TLS connection!") +} + +cipherSuites := vhost.ClientHelloMsg.CipherSuites +sessionId := vhost.ClientHelloMsg.SessionId +``` + +##### Memory reduction with Free +After you're done muxing, you probably don't need to inspect the header data anymore, so you can make it available for garbage collection: + +```go +// look up the upstream host +upstreamHost := hostMapping[vhostConn.Host()] + +// free up the muxing data +vhostConn.Free() + +// vhostConn.Host() == "" +// vhostConn.Request == nil (HTTP) +// vhostConn.ClientHelloMsg == nil (TLS) +``` diff --git a/vendor/github.com/inconshreveable/go-vhost/http.go b/vendor/github.com/inconshreveable/go-vhost/http.go new file mode 100644 index 00000000..d491fc00 --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/http.go @@ -0,0 +1,42 @@ +package vhost + +import ( + "bufio" + "net" + "net/http" +) + +type HTTPConn struct { + *sharedConn + Request *http.Request +} + +// HTTP parses the head of the first HTTP request on conn and returns +// a new, unread connection with metadata for virtual host muxing +func HTTP(conn net.Conn) (httpConn *HTTPConn, err error) { + c, rd := newShared(conn) + + httpConn = &HTTPConn{sharedConn: c} + if httpConn.Request, err = http.ReadRequest(bufio.NewReader(rd)); err != nil { + return + } + + // You probably don't need access to the request body and this makes the API + // simpler by allowing you to call Free() optionally + httpConn.Request.Body.Close() + + return +} + +// Free sets Request to nil so that it can be garbage collected +func (c *HTTPConn) Free() { + c.Request = nil +} + +func (c *HTTPConn) Host() string { + if c.Request == nil { + return "" + } + + return c.Request.Host +} diff --git a/vendor/github.com/inconshreveable/go-vhost/http_test.go b/vendor/github.com/inconshreveable/go-vhost/http_test.go new file mode 100644 index 00000000..8bd04eab --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/http_test.go @@ -0,0 +1,45 @@ +package vhost + +import ( + "net" + "net/http" + "testing" +) + +func TestHTTPHost(t *testing.T) { + var testHostname string = "foo.example.com" + + l, err := net.Listen("tcp", "127.0.0.1:12345") + if err != nil { + panic(err) + } + defer l.Close() + + go func() { + conn, err := net.Dial("tcp", "127.0.0.1:12345") + if err != nil { + panic(err) + } + defer conn.Close() + req, err := http.NewRequest("GET", "http://"+testHostname+"/bar", nil) + if err != nil { + panic(err) + } + if err = req.Write(conn); err != nil { + panic(err) + } + }() + + conn, err := l.Accept() + if err != nil { + panic(err) + } + c, err := HTTP(conn) + if err != nil { + panic(err) + } + + if c.Host() != testHostname { + t.Errorf("Connection Host() is %s, expected %s", c.Host(), testHostname) + } +} diff --git a/vendor/github.com/inconshreveable/go-vhost/interface.go b/vendor/github.com/inconshreveable/go-vhost/interface.go new file mode 100644 index 00000000..063121d4 --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/interface.go @@ -0,0 +1,11 @@ +package vhost + +import ( + "net" +) + +type Conn interface { + net.Conn + Host() string + Free() +} diff --git a/vendor/github.com/inconshreveable/go-vhost/mux.go b/vendor/github.com/inconshreveable/go-vhost/mux.go new file mode 100644 index 00000000..7b75c20d --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/mux.go @@ -0,0 +1,337 @@ +package vhost + +import ( + "fmt" + "net" + "strings" + "sync" + "time" +) + +var ( + normalize = strings.ToLower + isClosed = func(err error) bool { + netErr, ok := err.(net.Error) + if ok { + return netErr.Temporary() + } + return false + } +) + +// NotFound is returned when a vhost is not found +type NotFound struct { + error +} + +// BadRequest is returned when extraction of the vhost name fails +type BadRequest struct { + error +} + +// Closed is returned when the underlying connection is closed +type Closed struct { + error +} + +type ( + // this is the function you apply to a net.Conn to get + // a new virtual-host multiplexed connection + muxFn func(net.Conn) (Conn, error) + + // an error encountered when multiplexing a connection + muxErr struct { + err error + conn net.Conn + } +) + +type VhostMuxer struct { + listener net.Listener // listener on which we mux connections + muxTimeout time.Duration // a connection fails if it doesn't send enough data to mux after this timeout + vhostFn muxFn // new connections are multiplexed by applying this function + muxErrors chan muxErr // all muxing errors are sent over this channel + registry map[string]*Listener // registry of name -> listener + sync.RWMutex // protects the registry +} + +func NewVhostMuxer(listener net.Listener, vhostFn muxFn, muxTimeout time.Duration) (*VhostMuxer, error) { + mux := &VhostMuxer{ + listener: listener, + muxTimeout: muxTimeout, + vhostFn: vhostFn, + muxErrors: make(chan muxErr), + registry: make(map[string]*Listener), + } + + go mux.run() + return mux, nil +} + +// Listen begins multiplexing the underlying connection to send new +// connections for the given name over the returned listener. +func (m *VhostMuxer) Listen(name string) (net.Listener, error) { + name = normalize(name) + + vhost := &Listener{ + name: name, + mux: m, + accept: make(chan Conn), + } + + if err := m.set(name, vhost); err != nil { + return nil, err + } + + return vhost, nil +} + +// NextError returns the next error encountered while mux'ing a connection. +// The net.Conn may be nil if the wrapped listener returned an error from Accept() +func (m *VhostMuxer) NextError() (net.Conn, error) { + muxErr := <-m.muxErrors + return muxErr.conn, muxErr.err +} + +// Close closes the underlying listener +func (m *VhostMuxer) Close() { + m.listener.Close() +} + +// run is the VhostMuxer's main loop for accepting new connections from the wrapped listener +func (m *VhostMuxer) run() { + for { + conn, err := m.listener.Accept() + if err != nil { + if isClosed(err) { + m.sendError(nil, Closed{err}) + return + } else { + m.sendError(nil, err) + continue + } + } + go m.handle(conn) + } +} + +// handle muxes a connection accepted from the listener +func (m *VhostMuxer) handle(conn net.Conn) { + defer func() { + // recover from failures + if r := recover(); r != nil { + m.sendError(conn, fmt.Errorf("NameMux.handle failed with error %v", r)) + } + }() + + // Make sure we detect dead connections while we decide how to multiplex + if err := conn.SetDeadline(time.Now().Add(m.muxTimeout)); err != nil { + m.sendError(conn, fmt.Errorf("Failed to set deadline: %v", err)) + return + } + + // extract the name + vconn, err := m.vhostFn(conn) + if err != nil { + m.sendError(conn, BadRequest{fmt.Errorf("Failed to extract vhost name: %v", err)}) + return + } + + // normalize the name + host := normalize(vconn.Host()) + + // look up the correct listener + l, ok := m.get(host) + if !ok { + m.sendError(vconn, NotFound{fmt.Errorf("Host not found: %v", host)}) + return + } + + if err = vconn.SetDeadline(time.Time{}); err != nil { + m.sendError(vconn, fmt.Errorf("Failed unset connection deadline: %v", err)) + return + } + + l.accept <- vconn +} + +func (m *VhostMuxer) sendError(conn net.Conn, err error) { + m.muxErrors <- muxErr{conn: conn, err: err} +} + +func (m *VhostMuxer) get(name string) (l *Listener, ok bool) { + m.RLock() + defer m.RUnlock() + l, ok = m.registry[name] + if !ok { + // look for a matching wildcard + parts := strings.Split(name, ".") + for i := 0; i < len(parts)-1; i++ { + parts[i] = "*" + name = strings.Join(parts[i:], ".") + l, ok = m.registry[name] + if ok { + break + } + } + } + return +} + +func (m *VhostMuxer) set(name string, l *Listener) error { + m.Lock() + defer m.Unlock() + if _, exists := m.registry[name]; exists { + return fmt.Errorf("name %s is already bound", name) + } + m.registry[name] = l + return nil +} + +func (m *VhostMuxer) del(name string) { + m.Lock() + defer m.Unlock() + delete(m.registry, name) +} + +const ( + serverError = `HTTP/1.0 500 Internal Server Error +Content-Length: 22 + +Internal Server Error +` + + notFound = `HTTP/1.0 404 Not Found +Content-Length: 14 + +404 not found +` + + badRequest = `HTTP/1.0 400 Bad Request +Content-Length: 12 + +Bad Request +` +) + +type HTTPMuxer struct { + *VhostMuxer +} + +// HandleErrors handles muxing errors by calling .NextError(). You must +// invoke this function if you do not want to handle the errors yourself. +func (m *HTTPMuxer) HandleErrors() { + for { + m.HandleError(m.NextError()) + } +} + +func (m *HTTPMuxer) HandleError(conn net.Conn, err error) { + switch err.(type) { + case Closed: + return + case NotFound: + conn.Write([]byte(notFound)) + case BadRequest: + conn.Write([]byte(badRequest)) + default: + if conn != nil { + conn.Write([]byte(serverError)) + } + } + + if conn != nil { + conn.Close() + } +} + +// NewHTTPMuxer begins muxing HTTP connections on the given listener by inspecting +// the HTTP Host header in new connections. +func NewHTTPMuxer(listener net.Listener, muxTimeout time.Duration) (*HTTPMuxer, error) { + fn := func(c net.Conn) (Conn, error) { return HTTP(c) } + mux, err := NewVhostMuxer(listener, fn, muxTimeout) + return &HTTPMuxer{mux}, err +} + +type TLSMuxer struct { + *VhostMuxer +} + +// HandleErrors is the default error handler for TLS muxers. At the moment, it simply +// closes connections which are invalid or destined for virtual host names that it is +// not listening for. +// You must invoke this function if you do not want to handle the errors yourself. +func (m *TLSMuxer) HandleErrors() { + for { + conn, err := m.NextError() + + if conn == nil { + if _, ok := err.(Closed); ok { + return + } else { + continue + } + } else { + // XXX: respond with valid TLS close messages + conn.Close() + } + } +} + +func (m *TLSMuxer) Listen(name string) (net.Listener, error) { + // TLS SNI never includes the port + host, _, err := net.SplitHostPort(name) + if err != nil { + host = name + } + return m.VhostMuxer.Listen(host) +} + +// NewTLSMuxer begins muxing TLS connections by inspecting the SNI extension. +func NewTLSMuxer(listener net.Listener, muxTimeout time.Duration) (*TLSMuxer, error) { + fn := func(c net.Conn) (Conn, error) { return TLS(c) } + mux, err := NewVhostMuxer(listener, fn, muxTimeout) + return &TLSMuxer{mux}, err +} + +// Listener is returned by a call to Listen() on a muxer. A Listener +// only receives connections that were made to the name passed into the muxer's +// Listen call. +// +// Listener implements the net.Listener interface, so you can Accept() new +// connections and Close() it when finished. When you Close() a Listener, +// the parent muxer will stop listening for connections to the Listener's name. +type Listener struct { + name string + mux *VhostMuxer + accept chan Conn +} + +// Accept returns the next mux'd connection for this listener and blocks +// until one is available. +func (l *Listener) Accept() (net.Conn, error) { + conn, ok := <-l.accept + if !ok { + return nil, fmt.Errorf("Listener closed") + } + return conn, nil +} + +// Close stops the parent muxer from listening for connections to the mux'd +// virtual host name. +func (l *Listener) Close() error { + l.mux.del(l.name) + close(l.accept) + return nil +} + +// Addr returns the address of the bound listener used by the parent muxer. +func (l *Listener) Addr() net.Addr { + // XXX: include name in address? + return l.mux.listener.Addr() +} + +// Name returns the name of the virtual host this listener receives connections on. +func (l *Listener) Name() string { + return l.name +} diff --git a/vendor/github.com/inconshreveable/go-vhost/mux_test.go b/vendor/github.com/inconshreveable/go-vhost/mux_test.go new file mode 100644 index 00000000..79616543 --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/mux_test.go @@ -0,0 +1,195 @@ +package vhost + +import ( + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "strconv" + "strings" + + "testing" + "time" +) + +// TestErrors ensures that error types for this package are implemented properly +func TestErrors(t *testing.T) { + // test case for https://github.com/inconshreveable/go-vhost/pull/2 + // create local err vars of error interface type + var notFoundErr error + var badRequestErr error + var closedErr error + + // stuff local error types in to interface values + notFoundErr = NotFound{fmt.Errorf("test NotFound")} + badRequestErr = BadRequest{fmt.Errorf("test BadRequest")} + closedErr = Closed{fmt.Errorf("test Closed")} + + // assert the types + switch errType := notFoundErr.(type) { + case NotFound: + default: + t.Fatalf("expected NotFound, got: %s", errType) + } + switch errType := badRequestErr.(type) { + case BadRequest: + default: + t.Fatalf("expected BadRequest, got: %s", errType) + } + switch errType := closedErr.(type) { + case Closed: + default: + t.Fatalf("expected Closed, got: %s", errType) + } +} + +func localListener(t *testing.T) (net.Listener, string) { + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("failed to listen: %v", err) + } + return l, strconv.Itoa(l.Addr().(*net.TCPAddr).Port) +} + +func TestHTTPMux(t *testing.T) { + l, port := localListener(t) + mux, err := NewHTTPMuxer(l, time.Second) + if err != nil { + t.Fatalf("failed to start muxer: %v", err) + } + go mux.HandleErrors() + + muxed, err := mux.Listen("example.com") + if err != nil { + t.Fatalf("failed to listen on muxer: %v", muxed) + } + + go http.Serve(muxed, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + io.Copy(w, r.Body) + })) + + msg := "test" + url := "http://localhost:" + port + resp, err := http.Post(url, "text/plain", strings.NewReader(msg)) + if err != nil { + t.Fatalf("failed to post: %v", err) + } + + if resp.StatusCode != 404 { + t.Fatalf("sent incorrect host header, expected 404 but got %d", resp.StatusCode) + } + + req, err := http.NewRequest("POST", url, strings.NewReader(msg)) + if err != nil { + t.Fatalf("failed to construct HTTP request: %v", err) + } + req.Host = "example.com" + req.Header.Set("Content-Type", "text/plain") + + resp, err = new(http.Client).Do(req) + if err != nil { + t.Fatalf("failed to make HTTP request", err) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("failed to read: %v", err) + } + + got := string(body) + if got != msg { + t.Fatalf("unexpected resposne. got: %v, expected: %v", got, msg) + } +} + +func testMux(t *testing.T, listen, dial string) { + muxFn := func(c net.Conn) (Conn, error) { + return fakeConn{c, dial}, nil + } + + fakel := make(fakeListener, 1) + mux, err := NewVhostMuxer(fakel, muxFn, time.Second) + if err != nil { + t.Fatalf("failed to start vhost muxer: %v", err) + } + + l, err := mux.Listen(listen) + if err != nil { + t.Fatalf("failed to listen for %s", err) + } + + done := make(chan struct{}) + go func() { + conn, err := l.Accept() + if err != nil { + t.Fatalf("failed to accept connection: %v", err) + return + } + + got := conn.(Conn).Host() + expected := dial + if got != expected { + t.Fatalf("got connection with unexpected host. got: %s, expected: %s", got, expected) + return + } + + close(done) + }() + + go func() { + _, err := mux.NextError() + if err != nil { + t.Fatalf("muxing error: %v", err) + } + }() + + fakel <- struct{}{} + select { + case <-done: + case <-time.After(time.Second): + t.Fatalf("test timed out: dial: %s listen: %s", dial, listen) + } +} + +func TestMuxingPatterns(t *testing.T) { + var tests = []struct { + listen string + dial string + }{ + {"example.com", "example.com"}, + {"sub.example.com", "sub.example.com"}, + {"*.example.com", "sub.example.com"}, + {"*.example.com", "nested.sub.example.com"}, + } + + for _, test := range tests { + testMux(t, test.listen, test.dial) + } +} + +type fakeConn struct { + net.Conn + host string +} + +func (c fakeConn) SetDeadline(d time.Time) error { return nil } +func (c fakeConn) Host() string { return c.host } +func (c fakeConn) Free() {} + +type fakeNetConn struct { + net.Conn +} + +func (fakeNetConn) SetDeadline(time.Time) error { return nil } + +type fakeListener chan struct{} + +func (l fakeListener) Accept() (net.Conn, error) { + for _ = range l { + return fakeNetConn{nil}, nil + } + select {} +} +func (fakeListener) Addr() net.Addr { return nil } +func (fakeListener) Close() error { return nil } diff --git a/vendor/github.com/inconshreveable/go-vhost/shared.go b/vendor/github.com/inconshreveable/go-vhost/shared.go new file mode 100644 index 00000000..5a054156 --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/shared.go @@ -0,0 +1,52 @@ +package vhost + +import ( + "bytes" + "io" + "net" + "sync" +) + +const ( + initVhostBufSize = 1024 // allocate 1 KB up front to try to avoid resizing +) + +type sharedConn struct { + sync.Mutex + net.Conn // the raw connection + vhostBuf *bytes.Buffer // all of the initial data that has to be read in order to vhost a connection is saved here +} + +func newShared(conn net.Conn) (*sharedConn, io.Reader) { + c := &sharedConn{ + Conn: conn, + vhostBuf: bytes.NewBuffer(make([]byte, 0, initVhostBufSize)), + } + + return c, io.TeeReader(conn, c.vhostBuf) +} + +func (c *sharedConn) Read(p []byte) (n int, err error) { + c.Lock() + if c.vhostBuf == nil { + c.Unlock() + return c.Conn.Read(p) + } + n, err = c.vhostBuf.Read(p) + + // end of the request buffer + if err == io.EOF { + // let the request buffer get garbage collected + // and make sure we don't read from it again + c.vhostBuf = nil + + // continue reading from the connection + var n2 int + n2, err = c.Conn.Read(p[n:]) + + // update total read + n += n2 + } + c.Unlock() + return +} diff --git a/vendor/github.com/inconshreveable/go-vhost/shared_test.go b/vendor/github.com/inconshreveable/go-vhost/shared_test.go new file mode 100644 index 00000000..9f05937f --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/shared_test.go @@ -0,0 +1,64 @@ +package vhost + +import ( + "bytes" + "io" + "net" + "reflect" + "testing" +) + +func TestHeaderPreserved(t *testing.T) { + var msg string = "TestHeaderPreserved message! Hello world!" + var headerLen int = 15 + + l, err := net.Listen("tcp", "127.0.0.1:12345") + if err != nil { + panic(err) + } + defer l.Close() + + go func() { + conn, err := net.Dial("tcp", "127.0.0.1:12345") + if err != nil { + panic(err) + } + if _, err := conn.Write([]byte(msg)); err != nil { + panic(err) + } + if err = conn.Close(); err != nil { + panic(err) + } + }() + + conn, err := l.Accept() + if err != nil { + panic(err) + } + + // create a shared connection object + c, rd := newShared(conn) + + // read out a "header" + p := make([]byte, headerLen) + _, err = io.ReadFull(rd, p) + if err != nil { + panic(err) + } + + // make sure we got the header + expectedHeader := []byte(msg[:headerLen]) + if !reflect.DeepEqual(p, expectedHeader) { + t.Errorf("Read header bytes %s, expected %s", p, expectedHeader) + return + } + + // read out the entire connection. make sure it includes the header + buf := bytes.NewBuffer([]byte{}) + io.Copy(buf, c) + + expected := []byte(msg) + if !reflect.DeepEqual(buf.Bytes(), expected) { + t.Errorf("Read full connection bytes %s, expected %s", buf.Bytes(), expected) + } +} diff --git a/vendor/github.com/inconshreveable/go-vhost/tls.go b/vendor/github.com/inconshreveable/go-vhost/tls.go new file mode 100644 index 00000000..7e28b15e --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/tls.go @@ -0,0 +1,434 @@ +// Portions of the TLS code are: +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TLS virtual hosting + +package vhost + +import ( + "bytes" + "errors" + "io" + "net" + "strconv" +) + +const ( + maxPlaintext = 16384 // maximum plaintext payload length + maxCiphertext = 16384 + 2048 // maximum ciphertext payload length + recordHeaderLen = 5 // record header length + maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) +) + +type alert uint8 + +const ( + alertUnexpectedMessage alert = 10 + alertRecordOverflow alert = 22 + alertInternalError alert = 80 +) + +var alertText = map[alert]string{ + alertUnexpectedMessage: "unexpected message", + alertRecordOverflow: "record overflow", + alertInternalError: "internal error", +} + +func (e alert) String() string { + s, ok := alertText[e] + if ok { + return s + } + return "alert(" + strconv.Itoa(int(e)) + ")" +} + +func (e alert) Error() string { + return e.String() +} + +// TLS record types. +type recordType uint8 + +const ( + recordTypeHandshake recordType = 22 +) + +// TLS handshake message types. +const ( + typeClientHello uint8 = 1 +) + +// TLS extension numbers +var ( + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 + extensionSupportedPoints uint16 = 11 + extensionSessionTicket uint16 = 35 + extensionNextProtoNeg uint16 = 13172 // not IANA assigned +) + +// TLS CertificateStatusType (RFC 3546) +const ( + statusTypeOCSP uint8 = 1 +) + +// A Conn represents a secured connection. +// It implements the net.Conn interface. +type TLSConn struct { + *sharedConn + ClientHelloMsg *ClientHelloMsg +} + +// TLS parses the ClientHello message on conn and returns +// a new, unread connection with metadata for virtual host muxing +func TLS(conn net.Conn) (tlsConn *TLSConn, err error) { + c, rd := newShared(conn) + + tlsConn = &TLSConn{sharedConn: c} + if tlsConn.ClientHelloMsg, err = readClientHello(rd); err != nil { + return + } + + return +} + +func (c *TLSConn) Host() string { + if c.ClientHelloMsg == nil { + return "" + } + return c.ClientHelloMsg.ServerName +} + +func (c *TLSConn) Free() { + c.ClientHelloMsg = nil +} + +// A block is a simple data buffer. +type block struct { + data []byte + off int // index for Read +} + +// resize resizes block to be n bytes, growing if necessary. +func (b *block) resize(n int) { + if n > cap(b.data) { + b.reserve(n) + } + b.data = b.data[0:n] +} + +// reserve makes sure that block contains a capacity of at least n bytes. +func (b *block) reserve(n int) { + if cap(b.data) >= n { + return + } + m := cap(b.data) + if m == 0 { + m = 1024 + } + for m < n { + m *= 2 + } + data := make([]byte, len(b.data), m) + copy(data, b.data) + b.data = data +} + +// readFromUntil reads from r into b until b contains at least n bytes +// or else returns an error. +func (b *block) readFromUntil(r io.Reader, n int) error { + // quick case + if len(b.data) >= n { + return nil + } + + // read until have enough. + b.reserve(n) + for { + m, err := r.Read(b.data[len(b.data):cap(b.data)]) + b.data = b.data[0 : len(b.data)+m] + if len(b.data) >= n { + break + } + if err != nil { + return err + } + } + return nil +} + +func (b *block) Read(p []byte) (n int, err error) { + n = copy(p, b.data[b.off:]) + b.off += n + return +} + +// newBlock allocates a new block +func newBlock() *block { + return new(block) +} + +// splitBlock splits a block after the first n bytes, +// returning a block with those n bytes and a +// block with the remainder. the latter may be nil. +func splitBlock(b *block, n int) (*block, *block) { + if len(b.data) <= n { + return b, nil + } + bb := newBlock() + bb.resize(len(b.data) - n) + copy(bb.data, b.data[n:]) + b.data = b.data[0:n] + return b, bb +} + +// readHandshake reads the next handshake message from +// the record layer. +func readClientHello(rd io.Reader) (*ClientHelloMsg, error) { + var nextBlock *block // raw input, right off the wire + var hand bytes.Buffer // handshake data waiting to be read + + // readRecord reads the next TLS record from the connection + // and updates the record layer state. + readRecord := func() error { + // Caller must be in sync with connection: + // handshake data if handshake not yet completed, + // else application data. (We don't support renegotiation.) + if nextBlock == nil { + nextBlock = newBlock() + } + b := nextBlock + + // Read header, payload. + if err := b.readFromUntil(rd, recordHeaderLen); err != nil { + return err + } + typ := recordType(b.data[0]) + + // No valid TLS record has a type of 0x80, however SSLv2 handshakes + // start with a uint16 length where the MSB is set and the first record + // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests + // an SSLv2 client. + if typ == 0x80 { + return errors.New("tls: unsupported SSLv2 handshake received") + } + + vers := uint16(b.data[1])<<8 | uint16(b.data[2]) + n := int(b.data[3])<<8 | int(b.data[4]) + if n > maxCiphertext { + return alertRecordOverflow + } + + // First message, be extra suspicious: + // this might not be a TLS client. + // Bail out before reading a full 'body', if possible. + // The current max version is 3.1. + // If the version is >= 16.0, it's probably not real. + // Similarly, a clientHello message encodes in + // well under a kilobyte. If the length is >= 12 kB, + // it's probably not real. + if (typ != recordTypeHandshake) || vers >= 0x1000 || n >= 0x3000 { + return alertUnexpectedMessage + } + + if err := b.readFromUntil(rd, recordHeaderLen+n); err != nil { + return err + } + + // Process message. + b, nextBlock = splitBlock(b, recordHeaderLen+n) + b.off = recordHeaderLen + data := b.data[b.off:] + if len(data) > maxPlaintext { + return alertRecordOverflow + } + + hand.Write(data) + + return nil + } + + if err := readRecord(); err != nil { + return nil, err + } + + data := hand.Bytes() + n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if n > maxHandshake { + return nil, alertInternalError + } + for hand.Len() < 4+n { + if err := readRecord(); err != nil { + return nil, err + } + } + + data = hand.Next(4 + n) + if data[0] != typeClientHello { + return nil, alertUnexpectedMessage + } + + msg := new(ClientHelloMsg) + if !msg.unmarshal(data) { + return nil, alertUnexpectedMessage + } + + return msg, nil +} + +type ClientHelloMsg struct { + Raw []byte + Vers uint16 + Random []byte + SessionId []byte + CipherSuites []uint16 + CompressionMethods []uint8 + NextProtoNeg bool + ServerName string + OcspStapling bool + SupportedCurves []uint16 + SupportedPoints []uint8 + TicketSupported bool + SessionTicket []uint8 +} + +func (m *ClientHelloMsg) unmarshal(data []byte) bool { + if len(data) < 42 { + return false + } + m.Raw = data + m.Vers = uint16(data[4])<<8 | uint16(data[5]) + m.Random = data[6:38] + sessionIdLen := int(data[38]) + if sessionIdLen > 32 || len(data) < 39+sessionIdLen { + return false + } + m.SessionId = data[39 : 39+sessionIdLen] + data = data[39+sessionIdLen:] + if len(data) < 2 { + return false + } + // cipherSuiteLen is the number of bytes of cipher suite numbers. Since + // they are uint16s, the number must be even. + cipherSuiteLen := int(data[0])<<8 | int(data[1]) + if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { + return false + } + numCipherSuites := cipherSuiteLen / 2 + m.CipherSuites = make([]uint16, numCipherSuites) + for i := 0; i < numCipherSuites; i++ { + m.CipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i]) + } + data = data[2+cipherSuiteLen:] + if len(data) < 1 { + return false + } + compressionMethodsLen := int(data[0]) + if len(data) < 1+compressionMethodsLen { + return false + } + m.CompressionMethods = data[1 : 1+compressionMethodsLen] + + data = data[1+compressionMethodsLen:] + + m.NextProtoNeg = false + m.ServerName = "" + m.OcspStapling = false + m.TicketSupported = false + m.SessionTicket = nil + + if len(data) == 0 { + // ClientHello is optionally followed by extension data + return true + } + if len(data) < 2 { + return false + } + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if extensionsLength != len(data) { + return false + } + + for len(data) != 0 { + if len(data) < 4 { + return false + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return false + } + + switch extension { + case extensionServerName: + if length < 2 { + return false + } + numNames := int(data[0])<<8 | int(data[1]) + d := data[2:] + for i := 0; i < numNames; i++ { + if len(d) < 3 { + return false + } + nameType := d[0] + nameLen := int(d[1])<<8 | int(d[2]) + d = d[3:] + if len(d) < nameLen { + return false + } + if nameType == 0 { + m.ServerName = string(d[0:nameLen]) + break + } + d = d[nameLen:] + } + case extensionNextProtoNeg: + if length > 0 { + return false + } + m.NextProtoNeg = true + case extensionStatusRequest: + m.OcspStapling = length > 0 && data[0] == statusTypeOCSP + case extensionSupportedCurves: + // http://tools.ietf.org/html/rfc4492#section-5.5.1 + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l%2 == 1 || length != l+2 { + return false + } + numCurves := l / 2 + m.SupportedCurves = make([]uint16, numCurves) + d := data[2:] + for i := 0; i < numCurves; i++ { + m.SupportedCurves[i] = uint16(d[0])<<8 | uint16(d[1]) + d = d[2:] + } + case extensionSupportedPoints: + // http://tools.ietf.org/html/rfc4492#section-5.5.2 + if length < 1 { + return false + } + l := int(data[0]) + if length != l+1 { + return false + } + m.SupportedPoints = make([]uint8, l) + copy(m.SupportedPoints, data[1:]) + case extensionSessionTicket: + // http://tools.ietf.org/html/rfc5077#section-3.2 + m.TicketSupported = true + m.SessionTicket = data[:length] + } + data = data[length:] + } + + return true +} diff --git a/vendor/github.com/inconshreveable/go-vhost/tls_test.go b/vendor/github.com/inconshreveable/go-vhost/tls_test.go new file mode 100644 index 00000000..cdf83760 --- /dev/null +++ b/vendor/github.com/inconshreveable/go-vhost/tls_test.go @@ -0,0 +1,39 @@ +package vhost + +import ( + "crypto/tls" + "net" + "testing" +) + +func TestSNI(t *testing.T) { + var testHostname string = "foo.example.com" + + l, err := net.Listen("tcp", "127.0.0.1:12345") + if err != nil { + panic(err) + } + defer l.Close() + + go func() { + conf := &tls.Config{ServerName: testHostname} + conn, err := tls.Dial("tcp", "127.0.0.1:12345", conf) + if err != nil { + panic(err) + } + conn.Close() + }() + + conn, err := l.Accept() + if err != nil { + panic(err) + } + c, err := TLS(conn) + if err != nil { + panic(err) + } + + if c.Host() != testHostname { + t.Errorf("Connection Host() is %s, expected %s", c.Host(), testHostname) + } +} diff --git a/vendor/github.com/jpillora/go-tld/README.md b/vendor/github.com/jpillora/go-tld/README.md new file mode 100644 index 00000000..422da279 --- /dev/null +++ b/vendor/github.com/jpillora/go-tld/README.md @@ -0,0 +1,63 @@ +This was written for fun, please use + +http://golang.org/x/net/publicsuffix + +instead. + +--- + +# TLD Parser in Go + +The `tld` package has the same API ([see godoc](http://godoc.org/github.com/jpillora/go-tld)) as `net/url` except `tld.URL` contains extra fields: `Subdomain`, `Domain`, `TLD` and `Port`. + +### Install + +``` +go get github.com/jpillora/go-tld +``` + +### Usage + +``` go +package main + +import ( + "fmt" + + "github.com/jpillora/go-tld" +) + +func main() { + u, _ := tld.Parse("http://a.very.complex-domain.co.uk:8080/foo/bar") + + fmt.Printf("[ %s ] [ %s ] [ %s ] [ %s ] [ %s ]", u.Subdomain, u.Domain, u.TLD, u.Port, u.Path) +} +``` + +``` +$ go run main.go +[ a.very ] [ complex-domain ] [ co.uk ] [ 8080 ] [ /foo/bar ] +``` + +#### MIT License + +Copyright © 2015 Jaime Pillora <dev@jpillora.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/jpillora/go-tld/example/main.go b/vendor/github.com/jpillora/go-tld/example/main.go new file mode 100644 index 00000000..4076604c --- /dev/null +++ b/vendor/github.com/jpillora/go-tld/example/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + + "github.com/jpillora/go-tld" +) + +func main() { + u, _ := tld.Parse("http://a.very.complex-domain.co.uk:8080/foo/bar") + + fmt.Printf("%s\n = [ %s ] [ %s ] [ %s ] [ %s ] [ %s ]", + u, u.Subdomain, u.Domain, u.TLD, u.Port, u.Path) +} diff --git a/vendor/github.com/jpillora/go-tld/generate.sh b/vendor/github.com/jpillora/go-tld/generate.sh new file mode 100644 index 00000000..6039f3e6 --- /dev/null +++ b/vendor/github.com/jpillora/go-tld/generate.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +#pull the official TLD list, remove comments and blanks, reverse each line, then sort +words=`curl -# https://www.publicsuffix.org/list/effective_tld_names.dat | + grep -v "^//" | + grep -v "^\$" | + grep -v "^!" | + grep -v "^*" | + rev | + sort` + +#convert each line into Go strings +strings=`for w in $words; do + echo " \"$w\"," +done` + +#output the generated file +echo "package tld +//generated on '`date -u`' + +//list contains all TLDs reversed, then sorted +var list = []string{ +$strings +} + +var count = len(list) +" > parse_list.go diff --git a/vendor/github.com/jpillora/go-tld/parse.go b/vendor/github.com/jpillora/go-tld/parse.go new file mode 100644 index 00000000..e4a7b062 --- /dev/null +++ b/vendor/github.com/jpillora/go-tld/parse.go @@ -0,0 +1,109 @@ +//go:generate sh generate.sh + +//The tld package has the same API as net/url except +//tld.URL contains extra fields: Subdomain, Domain, TLD and Port. +package tld + +import ( + "errors" + "net/url" +) + +//URL embeds net/url and adds extra fields ontop +type URL struct { + Subdomain, Domain, TLD, Port string + *url.URL +} + +//Parse mirrors net/url.Parse except instead it returns +//a tld.URL, which contains extra fields. +func Parse(s string) (*URL, error) { + + url, err := url.Parse(s) + if err != nil { + return nil, err + } + + if url.Host == "" { + return &URL{URL: url}, nil + } + + //index of tld + tld := 0 + + dom, port := domainPort(url.Host) + + i := 0 + l := len(dom) - 1 + + //binary search the TLD list + lo := 0 + hi := count - 1 + for lo != hi && lo+1 != hi { + + mid := (hi + lo) / 2 + guess := list[mid] + + //for binary search debugging... + // log.Printf("[%d - %d - %d] %s == %s (%s)", lo, mid, hi, string(dom[l-i]), string(guess[i]), guess) + + if i < len(guess) && guess[i] == dom[l-i] { + //store partial match + if i > tld && guess[i] == '.' { + tld = i + } + //advance! + i++ + //checked all is in guess + if len(guess) == i && dom[l-i] == '.' { + tld = i + break + } + } else if i >= len(guess) || guess[i] < dom[l-i] { + lo = mid + i = 0 + } else { + hi = mid + i = 0 + } + } + + if tld == 0 { + return nil, errors.New("tld not found") + } + + //extract the tld + t := dom[l-tld+1:] + //we can calculate the root domain + dom = dom[:l-tld] + //and subdomain + sub := "" + for i := len(dom) - 1; i >= 0; i-- { + if dom[i] == '.' { + sub = dom[:i] + dom = dom[i+1:] + break + } + } + + return &URL{ + Subdomain: sub, + Domain: dom, + TLD: t, + Port: port, + URL: url, + }, nil +} + +func domainPort(host string) (string, string) { + for i := len(host) - 1; i >= 0; i-- { + if host[i] == ':' { + return host[:i], host[i+1:] + } else if host[i] < '0' || host[i] > '9' { + return host, "" + } + } + //will only land here if the string is all digits, + //net/url should prevent that from happening + return host, "" +} diff --git a/vendor/github.com/jpillora/go-tld/parse_list.go b/vendor/github.com/jpillora/go-tld/parse_list.go new file mode 100644 index 00000000..c80bf6a2 --- /dev/null +++ b/vendor/github.com/jpillora/go-tld/parse_list.go @@ -0,0 +1,7056 @@ +package tld +//generated on 'Tue 27 Jan 2015 01:54:07 UTC' + +//list contains all TLDs reversed, then sorted +var list = []string{ + "ab", + "ab.asnu", + "ab.gro", + "ab.ibnu", + "ab.lim", + "ab.moc", + "ab.oc", + "ab.sr", + "ab.ten", + "ab.ude", + "ab.vog", + "abc", + "abihsot", + "ac", + "ac.ba", + "ac.bm", + "ac.bn", + "ac.cb", + "ac.cg", + "ac.cq", + "ac.ep", + "ac.fn", + "ac.ks", + "ac.ky", + "ac.ln", + "ac.no", + "ac.oc", + "ac.sn", + "ac.tn", + "ac.topsgolb", + "ac.un", + "acinofelet", + "acirfa", + "acisroc", + "acs", + "adats", + "adneit", + "adnoh", + "adom", + "adtl", + "aeg", + "afc", + "ag", + "agnaripi", + "agoy", + "ahod", + "ahskihs", + "aidem", + "aihs", + "aisa", + "ajnin", + "akaso", + "akdov", + "akede", + "akusto", + "al", + "al.c", + "al.gro", + "al.moc", + "al.ofni", + "al.rep", + "al.ten", + "al.tni", + "al.ude", + "al.vog", + "allenisiuc", + "am", + "am.ca", + "am.gro", + "am.oc", + "am.sserp", + "am.ten", + "am.vog", + "amahokoy", + "amm", + "an", + "an.ac", + "an.cc", + "an.eman", + "an.gro", + "an.ibom", + "an.loohcs", + "an.moc", + "an.ni", + "an.oc", + "an.ofni", + "an.orp", + "an.rd", + "an.ro", + "an.su", + "an.sw", + "an.vt", + "an.xm", + "anav", + "anolecrab", + "ap", + "ap.bog", + "ap.ca", + "ap.dem", + "ap.dls", + "ap.gni", + "ap.gro", + "ap.moc", + "ap.mon", + "ap.oba", + "ap.ten", + "ap.ude", + "apra", + "apra.461e", + "apra.6pi", + "apra.iru", + "apra.nru", + "apra.rdda-ni", + "apra.siri", + "aq", + "aq.eman", + "aq.gro", + "aq.hcs", + "aq.lim", + "aq.moc", + "aq.ten", + "aq.ude", + "aq.vog", + "araz", + "aremac", + "arn", + "arukas", + "as", + "as.bup", + "as.dem", + "as.gro", + "as.hcs", + "as.moc", + "as.ten", + "as.ude", + "as.vog", + "asac", + "atsiv", + "au", + "au.acinniv", + "au.aemirc", + "au.aihzhziropaz", + "au.aistynniv", + "au.asedo", + "au.assedo", + "au.atlay", + "au.avatlop", + "au.bs", + "au.cinimod", + "au.dargovorik", + "au.do", + "au.doroghzu", + "au.ehzhziropaz", + "au.envir", + "au.et", + "au.fi", + "au.gl", + "au.gro", + "au.hk", + "au.istvinrehc", + "au.iykstynlemhk", + "au.kc", + "au.km", + "au.ksnagul", + "au.kstenod", + "au.kstul", + "au.ksviknarf-onavi", + "au.ksvorteporpend", + "au.ksvorteporpind", + "au.liponret", + "au.lopotsabes", + "au.lopotsaves", + "au.lp", + "au.mk", + "au.moc", + "au.ms", + "au.myrk", + "au.nc", + "au.nd", + "au.ni", + "au.nosrehk", + "au.nv", + "au.nylov", + "au.oc", + "au.onvor", + "au.pd", + "au.pp", + "au.pz", + "au.rc", + "au.rimotihz", + "au.rk", + "au.rymotyhz", + "au.sk", + "au.ten", + "au.tl", + "au.tz", + "au.ude", + "au.vc", + "au.vealokin", + "au.veik", + "au.vialokym", + "au.vihinrehc", + "au.vikrahk", + "au.vivl", + "au.viyk", + "au.vk", + "au.vl", + "au.vog", + "au.voginrehc", + "au.vokrahk", + "au.vr", + "au.yikstinlemhk", + "au.ymus", + "au.ysakrehc", + "au.yssakrehc", + "au.ystvonrehc", + "au.zu", + "av", + "avaj", + "avbb", + "aviv", + "awaniko", + "axa", + "axiacal", + "ayogan", + "azzip", + "bac", + "baher", + "bat", + "bb", + "bb.erots", + "bb.gro", + "bb.moc", + "bb.oc", + "bb.ofni", + "bb.ten", + "bb.ude", + "bb.vog", + "bb.vt", + "bb.zib", + "bba", + "bcj", + "bcs", + "bg", + "bl", + "bl.gro", + "bl.moc", + "bl.ten", + "bl.ude", + "bl.vog", + "bs", + "bs.gro", + "bs.moc", + "bs.ten", + "bs.ude", + "bs.vog", + "bulc", + "bup", + "ca", + "ca.gro", + "ca.lim", + "ca.moc", + "ca.ten", + "ca.ude", + "ca.vog", + "cbb", + "cbsh", + "cc", + "cc.gnipparcs", + "cc.revres-emag", + "cc.sotohpym", + "cc.sseccaptf", + "ce", + "ce.21k", + "ce.bog", + "ce.dem", + "ce.gro", + "ce.lim", + "ce.moc", + "ce.nif", + "ce.ofni", + "ce.orp", + "ce.ten", + "ce.ude", + "ce.vog", + "cebeuq", + "cetnamys", + "cinagro", + "cinilc", + "citic", + "cl", + "cl.gro", + "cl.moc", + "cl.oc", + "cl.ten", + "cl.ude", + "cl.vog", + "clj", + "cm", + "cm.mt", + "cm.ossa", + "cn", + "cn.ossa", + "cnalbtnom", + "cptm", + "crelcel", + "cs", + "cs.gro", + "cs.moc", + "cs.ten", + "cs.ude", + "cs.vog", + "csc", + "ct", + "cts", + "ctw", + "cv", + "cv.gro", + "cv.lim", + "cv.moc", + "cv.ten", + "cv.ude", + "cv.vog", + "cwi", + "cyn", + "da", + "da.mon", + "dad", + "daer", + "daolnwod", + "dc", + "dc.vog", + "der", + "derk", + "desopxe", + "detimil", + "dew", + "dfc", + "dg", + "di", + "di.ased", + "di.bew", + "di.ca", + "di.hcs", + "di.lim", + "di.oc", + "di.og", + "di.ro", + "di.ten", + "di.ym", + "di.zib", + "dib", + "diordna", + "dirdam", + "dliub", + "dlrow", + "dm", + "dnab", + "dnal", + "dnalraas", + "dnob", + "dnuf", + "dorp", + "dractiderc", + "dracyalcrab", + "drk", + "drof", + "ds", + "ds.dem", + "ds.gro", + "ds.moc", + "ds.ofni", + "ds.ten", + "ds.ude", + "ds.vog", + "ds.vt", + "dt", + "dt.topsgolb", + "dtl", + "ea", + "ea.ca", + "ea.gro", + "ea.hcs", + "ea.lim", + "ea.oc", + "ea.ten", + "ea.topsgolb", + "ea.vog", + "eb", + "eb.ca", + "eb.topsgolb", + "ebortal", + "ebutuoy", + "ecalp", + "ecaps", + "ecasla", + "eci", + "ecnad", + "ecnanif", + "ecneics", + "ecrofria", + "ed", + "ed.edaregtmueart", + "ed.keegnietsi", + "ed.moc", + "ed.nesgnutiel", + "ed.niemtsi", + "ed.topsgolb", + "ed.ztenmitbel", + "ed.ztensadtretteuf", + "edart", + "ediug", + "ednil", + "ee", + "ee.bil", + "ee.dem", + "ee.eif", + "ee.gro", + "ee.irp", + "ee.kiir", + "ee.moc", + "ee.pia", + "ee.ude", + "ee.vog", + "eeffoc", + "eegg", + "eerged", + "efas", + "efil", + "eg", + "eg.gro", + "eg.lim", + "eg.moc", + "eg.ten", + "eg.tvp", + "eg.ude", + "eg.vog", + "egaf", + "egagtrom", + "egap", + "egayov", + "egelloc", + "egnahcxe", + "egug", + "ei", + "ei.topsgolb", + "ei.vog", + "ej", + "ej.gro", + "ej.oc", + "ej.ten", + "ekib", + "ekil", + "eladmrif", + "elas", + "elasrof", + "elbib", + "elcaro", + "elcric", + "elg", + "elgoog", + "elims", + "ellerauqa", + "eluhcs", + "elyts", + "elytsefil", + "em", + "em.ca", + "em.gro", + "em.oc", + "em.sti", + "em.ten", + "em.ude", + "em.virp", + "em.vog", + "eman", + "eman.reh.togrof", + "eman.sih.togrof", + "emem", + "emorhc", + "emw", + "en", + "engoloc", + "eno", + "enotsder", + "enotsegdirb", + "enotserif", + "enoz", + "enruoblem", + "eolhc", + "eom", + "ep", + "ep.bog", + "ep.gro", + "ep.lim", + "ep.moc", + "ep.mon", + "ep.ten", + "ep.ude", + "epirg", + "epyks", + "er", + "er.moc", + "er.mon", + "er.ossa", + "er.topsgolb", + "erac", + "erachtlaeh", + "erawtfos", + "ereh", + "erusni", + "erutinruf", + "erutnecca", + "eruza", + "es", + "es.a", + "es.b", + "es.bibnal", + "es.brofmok", + "es.c", + "es.ca", + "es.d", + "es.db", + "es.dnarb", + "es.dnubroflanummok", + "es.e", + "es.f", + "es.g", + "es.gro", + "es.h", + "es.hf", + "es.i", + "es.itrap", + "es.k", + "es.kshf", + "es.l", + "es.m", + "es.moc", + "es.mt", + "es.n", + "es.nmygskurbrutan", + "es.o", + "es.p", + "es.pp", + "es.r", + "es.s", + "es.sserp", + "es.t", + "es.topsgolb", + "es.u", + "es.vhf", + "es.w", + "es.x", + "es.xuvmok", + "es.y", + "es.z", + "esael", + "esier", + "esroh", + "esuoh", + "etad", + "etatse", + "etisbew", + "etov", + "etra", + "ettol", + "etutitsni", + "eulb", + "euqituob", + "ev", + "ev.21e", + "ev.bew", + "ev.bog", + "ev.cer", + "ev.cet", + "ev.erots", + "ev.gro", + "ev.lim", + "ev.moc", + "ev.mrif", + "ev.oc", + "ev.ofni", + "ev.stra", + "ev.ten", + "ev.tni", + "ev.ude", + "ev.vog", + "evil", + "evitca", + "exul", + "fa", + "fa.gro", + "fa.moc", + "fa.ten", + "fa.ude", + "fa.vog", + "fb", + "fb.vog", + "fc", + "fc.topsgolb", + "fg", + "fiam", + "flog", + "fn", + "fn.bew", + "fn.cer", + "fn.erots", + "fn.moc", + "fn.mrif", + "fn.ofni", + "fn.rehto", + "fn.rep", + "fn.stra", + "fn.ten", + "forp", + "fp", + "fp.gro", + "fp.moc", + "fp.ude", + "frus", + "ft", + "ftw", + "fw", + "ga", + "ga.gro", + "ga.moc", + "ga.mon", + "ga.oc", + "ga.ten", + "gavd", + "gb", + "gb.0", + "gb.1", + "gb.2", + "gb.3", + "gb.4", + "gb.5", + "gb.6", + "gb.7", + "gb.8", + "gb.9", + "gb.a", + "gb.b", + "gb.c", + "gb.d", + "gb.e", + "gb.f", + "gb.g", + "gb.h", + "gb.i", + "gb.j", + "gb.k", + "gb.l", + "gb.m", + "gb.n", + "gb.o", + "gb.p", + "gb.q", + "gb.r", + "gb.s", + "gb.t", + "gb.u", + "gb.v", + "gb.w", + "gb.x", + "gb.y", + "gb.z", + "gc", + "ge", + "ge.eman", + "ge.gro", + "ge.ics", + "ge.lim", + "ge.moc", + "ge.nue", + "ge.ten", + "ge.ude", + "ge.vog", + "gg", + "gg.gro", + "gg.oc", + "gg.ten", + "gia", + "gk", + "gk.gro", + "gk.lim", + "gk.moc", + "gk.ten", + "gk.ude", + "gk.vog", + "gm", + "gm.drp", + "gm.gro", + "gm.lim", + "gm.moc", + "gm.mon", + "gm.mt", + "gm.ude", + "gm.vog", + "gn", + "gn.eman", + "gn.gro", + "gn.hcs", + "gn.ibom", + "gn.lim", + "gn.moc", + "gn.ten", + "gn.ude", + "gn.vog", + "gnaw", + "gni", + "gnib", + "gnibmulp", + "gnicar", + "gnidart", + "gniddew", + "gnihsif", + "gnihtolc", + "gnikooc", + "gninaelc", + "gniniart", + "gnireenigne", + "gniretac", + "gnitad", + "gnitekram", + "gnithgil", + "gnitlusnoc", + "gnitov", + "gnitsoh", + "gnittebdaerps", + "gnivig", + "gno", + "gnurehcisrev", + "gnusmas", + "gnutarebsnegömrev", + "god", + "goog", + "grebmoolb", + "gro", + "gro.az", + "gro.dab-yrev-si", + "gro.desufnocsim", + "gro.devas-si", + "gro.dnuof-si", + "gro.doog-yrev-si", + "gro.ea", + "gro.ecin-yrev-si", + "gro.elas-4-ffuts", + "gro.em-morf", + "gro.emagevres", + "gro.emohruoyslles", + "gro.enozdop", + "gro.erehwongniogyldlob", + "gro.etadidnac-a-si", + "gro.etis-ybboh", + "gro.etisgolb", + "gro.fehc-a-si", + "gro.golbymdaer", + "gro.keeg-a-si", + "gro.keeg-asi", + "gro.kh", + "gro.live-yrev-si", + "gro.nafscitlec-a-si", + "gro.nafsniurb-a-si", + "gro.nafstap-a-si", + "gro.nafxos-a-si", + "gro.ojodsnd", + "gro.pifles", + "gro.pohbew", + "gro.ptfemoh", + "gro.ptfevres", + "gro.resu-xunil-a-si", + "gro.sailanyd", + "gro.sailasnd", + "gro.sbbevres", + "gro.sndemoh", + "gro.sndgolb", + "gro.sndmood", + "gro.sndnyd", + "gro.sndnyd.emoh", + "gro.sndnyd.og", + "gro.sndrvd", + "gro.sndtog", + "gro.ssa-skcik", + "gro.su", + "gro.teews-yrev-si", + "gro.tenretniehtfodne", + "gro.tenretnifodne", + "gro.thgink-a-si", + "gro.tsixetnod", + "gro.tsixetnseod", + "gro.tsoh-emag", + "gro.tsol-si", + "gro.xinuemoh", + "gro.xunilemoh", + "grubmah", + "gruboj", + "gs", + "gs.gro", + "gs.moc", + "gs.rep", + "gs.ten", + "gs.topsgolb", + "gs.ude", + "gs.vog", + "gt", + "gu", + "gu.ca", + "gu.cs", + "gu.en", + "gu.gro", + "gu.moc", + "gu.oc", + "gu.og", + "gu.ro", + "gv", + "hb", + "hb.gro", + "hb.moc", + "hb.ten", + "hb.ude", + "hb.vog", + "hc", + "hc.topsgolb", + "hcaoc", + "hcir", + "hcireuz", + "hcraeserrecnac", + "hcruhc", + "hctaw", + "hfk", + "hg", + "hg.gro", + "hg.lim", + "hg.moc", + "hg.ude", + "hg.vog", + "hm", + "hocir", + "hp", + "hp.gro", + "hp.i", + "hp.lim", + "hp.moc", + "hp.ogn", + "hp.ten", + "hp.ude", + "hp.vog", + "hs", + "hs.gro", + "hs.lim", + "hs.moc", + "hs.ten", + "hs.vog", + "hsac", + "hsanom", + "hsif", + "hsiri", + "ht", + "ht.ca", + "ht.im", + "ht.ni", + "ht.oc", + "ht.og", + "ht.ro", + "ht.ten", + "htdimslf", + "htiaf", + "htrae", + "hvo", + "hzb", + "ia", + "ia.ffo", + "ia.gro", + "ia.moc", + "ia.ten", + "iarenap", + "ib", + "ib.gro", + "ib.moc", + "ib.oc", + "ib.ro", + "ib.ude", + "ibo", + "ibom", + "ic", + "ic.ca", + "ic.de", + "ic.dm", + "ic.esserp", + "ic.gro", + "ic.moc", + "ic.oc", + "ic.og", + "ic.ossa", + "ic.ro", + "ic.ten", + "ic.tni", + "ic.troporéa", + "ic.ude", + "ic.vuog", + "iccug", + "ict", + "iddk", + "iepiat", + "if", + "if.dnala", + "if.iki", + "if.topsgolb", + "ifonas", + "ig", + "ig.dom", + "ig.dtl", + "ig.gro", + "ig.moc", + "ig.ude", + "ig.vog", + "ihcatih", + "ihcra", + "ihsabodoy", + "ik", + "ik.gro", + "ik.moc", + "ik.ofni", + "ik.ten", + "ik.ude", + "ik.vog", + "ik.zib", + "ikiw", + "ikuzus", + "il", + "imaim", + "inim", + "inre", + "iom", + "is", + "ised", + "itinifni", + "itrahb", + "iut", + "iv", + "iv.21k", + "iv.gro", + "iv.moc", + "iv.oc", + "iv.ten", + "iwik", + "ixarp", + "jb", + "jb.ossa", + "jb.topsgolb", + "jb.uaerrab", + "jb.vuog", + "jd", + "js", + "jt", + "jt.bew", + "jt.ca", + "jt.cin", + "jt.eman", + "jt.gro", + "jt.lim", + "jt.moc", + "jt.oc", + "jt.og", + "jt.ten", + "jt.tni", + "jt.tset", + "jt.ude", + "jt.vog", + "jt.zib", + "kcabdeef", + "kcalb", + "kcilc", + "kcreme", + "kd", + "kd.topsgolb", + "kees", + "kesamet", + "kh", + "kh.cni", + "kh.dtl", + "kh.gro", + "kh.moc", + "kh.ten", + "kh.topsgolb", + "kh.ude", + "kh.vdi", + "kh.vog", + "kh.人个", + "kh.人個", + "kh.人箇", + "kh.å¸å…¬", + "kh.府政", + "kh.絡網", + "kh.絡网", + "kh.織組", + "kh.織组", + "kh.织組", + "kh.织组", + "kh.络網", + "kh.络网", + "kh.育敎", + "kh.育教", + "khn", + "kivdnas", + "kl", + "kl.bew", + "kl.cos", + "kl.dtl", + "kl.gro", + "kl.hcs", + "kl.letoh", + "kl.moc", + "kl.nssa", + "kl.ogn", + "kl.prg", + "kl.ten", + "kl.tni", + "kl.ude", + "kl.vog", + "klcd", + "km", + "km.eman", + "km.fni", + "km.gro", + "km.moc", + "km.ten", + "km.ude", + "km.vog", + "knab", + "knabmmoc", + "knabreve", + "knabten", + "kni", + "knil", + "knip", + "kp", + "kp.bew", + "kp.bog", + "kp.gro", + "kp.kog", + "kp.maf", + "kp.moc", + "kp.nog", + "kp.ofni", + "kp.pog", + "kp.sog", + "kp.ten", + "kp.ude", + "kp.vog", + "kp.zib", + "krow", + "krowten", + "ks", + "ks.topsgolb", + "kt", + "ku", + "ku.ca", + "ku.clp", + "ku.dtl", + "ku.ecilop", + "ku.em", + "ku.gro", + "ku.oc", + "ku.oc.topsgolb", + "ku.shn", + "ku.ten", + "ku.vog", + "ku.vog.ecivres", + "la", + "la.gro", + "la.lim", + "la.moc", + "la.ten", + "la.ude", + "la.vog", + "labolg", + "lac", + "lag", + "lagel", + "laicnanif", + "laicos", + "lairomem", + "lanif", + "lanoitanretni", + "latigid", + "latipac", + "latned", + "lc", + "lc.bog", + "lc.lim", + "lc.oc", + "lc.vog", + "ldil", + "legeips", + "lennahc", + "let", + "letria", + "levart", + "lfa", + "lg", + "lhop", + "li.oc.topsgolb", + "liaf", + "liame", + "liamg", + "liamtoh", + "lim", + "liotats", + "llabtoof", + "llac", + "lled", + "llihmailliw", + "lm", + "lm.esserp", + "lm.gro", + "lm.moc", + "lm.ten", + "lm.ude", + "lm.vog", + "lm.vuog", + "ln", + "ln.oc", + "ln.topsgolb", + "ln.vb", + "lnb", + "lno", + "lobtuf", + "looc", + "loohcs", + "lorit", + "lou", + "lp", + "lp.acindiws", + "lp.acingel", + "lp.adg", + "lp.adortso", + "lp.adurawon", + "lp.aidem", + "lp.aimraw", + "lp.ainydg", + "lp.akeloguld", + "lp.akelortso", + "lp.akslopolam", + "lp.aktsu", + "lp.akytsyrut", + "lp.alip", + "lp.alokzs", + "lp.alow-awolats", + "lp.alowoksnok", + "lp.animg", + "lp.arog-aibab", + "lp.arog-ainelej", + "lp.arogj", + "lp.arogz", + "lp.asyn", + "lp.atsaim", + "lp.awaleib", + "lp.awali", + "lp.awalo", + "lp.awazsraw", + "lp.awonamil", + "lp.azeiwolaib", + "lp.azmol", + "lp.ceiwalselob", + "lp.ceiwonsos", + "lp.ceiwortso", + "lp.celeim", + "lp.celezrogz", + "lp.corw", + "lp.cp", + "lp.dem", + "lp.dia", + "lp.dragrats", + "lp.ecilrog", + "lp.eciwilg", + "lp.eciwohcarats", + "lp.eciwohcorp", + "lp.eciwoklop", + "lp.eciwotak", + "lp.ecyzreibok", + "lp.eicsjuoniws", + "lp.eiksromop", + "lp.eisaldop", + "lp.elahdop", + "lp.elopo", + "lp.enapokaz", + "lp.etatselaer", + "lp.ezromop", + "lp.ezswozam", + "lp.galble", + "lp.gezrbolok", + "lp.gezrbonrat", + "lp.gro", + "lp.hcyzrblaw", + "lp.icsomohcurein", + "lp.igrat", + "lp.iklawus", + "lp.kerut", + "lp.kewalcolw", + "lp.kinbyr", + "lp.kle", + "lp.konas", + "lp.kotsylaib", + "lp.krobel", + "lp.kroblam", + "lp.ksals", + "lp.ksjazel", + "lp.ksnadg", + "lp.kspuls", + "lp.ksrowezrp", + "lp.lcolw", + "lp.ler", + "lp.levart", + "lp.liam", + "lp.lim", + "lp.moc", + "lp.modar", + "lp.mon", + "lp.motyb", + "lp.msg", + "lp.msiruot", + "lp.mt", + "lp.mta", + "lp.nagaz", + "lp.nanzop", + "lp.nibul", + "lp.nicezczs", + "lp.nimolow", + "lp.ninok", + "lp.nizdeb", + "lp.nizdobeiws", + "lp.nuleiw", + "lp.nytzslo", + "lp.nyzrtek", + "lp.nyzseic", + "lp.oc", + "lp.ofni", + "lp.okcelo", + "lp.okzdolk", + "lp.olkan", + "lp.onleim", + "lp.onpek", + "lp.ontuk", + "lp.ontyzczs", + "lp.onzcopo", + "lp.onzeing", + "lp.onzrowaj", + "lp.orga", + "lp.otua", + "lp.owejarg", + "lp.owogarm", + "lp.pelks", + "lp.pklwwortso", + "lp.pohs", + "lp.romophcaz", + "lp.sos", + "lp.taiwop", + "lp.ten", + "lp.topos", + "lp.tra", + "lp.tsezc", + "lp.ude", + "lp.virp", + "lp.vog", + "lp.vog.ap", + "lp.vog.gu", + "lp.vog.mu", + "lp.vog.op", + "lp.vog.os", + "lp.vog.owtsorats", + "lp.vog.rs", + "lp.vog.wopu", + "lp.vog.wu", + "lp.walcorw", + "lp.walsizdow", + "lp.waw", + "lp.wogolg", + "lp.wokark", + "lp.wokul", + "lp.wokzsurp", + "lp.woraz", + "lp.worgew", + "lp.wotsugua", + "lp.wozcoks", + "lp.wozsezr", + "lp.xes", + "lp.ybuzsak", + "lp.ydazczseib", + "lp.ydikseb", + "lp.yhcyt", + "lp.ynjes", + "lp.ynlod-zreimizak", + "lp.ypal", + "lp.yrogt", + "lp.yruzam", + "lp.ywalup", + "lp.yzutrak", + "lp.zam-awar", + "lp.zcaprak", + "lp.zciwol", + "lp.zczsogdyb", + "lp.zdalezc", + "lp.zib", + "lp.zsilak", + "lp.zsip", + "lp.zsuklo", + "lras", + "lrf", + "ls", + "ls.gro", + "ls.moc", + "ls.ten", + "ls.ude", + "ls.vog", + "lt", + "lt.vog", + "lubnatsi", + "ma", + "macbew", + "madretsma", + "marirhs", + "mb", + "mb.gro", + "mb.moc", + "mb.ten", + "mb.ude", + "mb.vog", + "mbi", + "mc", + "mc.moc", + "mc.oc", + "mc.ten", + "mc.vog", + "md", + "md.gro", + "md.moc", + "md.ten", + "md.ude", + "md.vog", + "mf", + "mfi", + "mg", + "mh", + "mi", + "mi.ca", + "mi.gro", + "mi.moc", + "mi.oc", + "mi.oc.clp", + "mi.oc.dtl", + "mi.ten", + "mi.tt", + "mi.vt", + "mik", + "mk", + "mk.drp", + "mk.erianiretev", + "mk.esserp", + "mk.gro", + "mk.lim", + "mk.moc", + "mk.mon", + "mk.mt", + "mk.nicedem", + "mk.ossa", + "mk.pooc", + "mk.seriaton", + "mk.sneicamrahp", + "mk.ssa", + "mk.ude", + "mk.vog", + "mk.vuog", + "mlohkcots", + "mo", + "mo.dem", + "mo.gro", + "mo.moc", + "mo.muesum", + "mo.oc", + "mo.orp", + "mo.ten", + "mo.ude", + "mo.vog", + "mob", + "moc", + "moc.ac-morf", + "moc.acirfa", + "moc.ag-morf", + "moc.agoy-sehcaet", + "moc.ai-morf", + "moc.am-morf", + "moc.amall-a-si", + "moc.amallamai", + "moc.ap-morf", + "moc.apc-a-si", + "moc.as", + "moc.av-morf", + "moc.aw-morf", + "moc.az", + "moc.bew-sndnyd", + "moc.bg", + "moc.buhnnylf", + "moc.cd-morf", + "moc.cn-morf", + "moc.cq", + "moc.cs-morf", + "moc.deifitrec-si", + "moc.deifitrec-ton-si", + "moc.dellortnocduolc", + "moc.di-morf", + "moc.dm-morf", + "moc.dn-morf", + "moc.dnabeht-htiw-si", + "moc.ds-morf", + "moc.duolchr", + "moc.duolcsmetsystuo", + "moc.eciffo-sndnyd", + "moc.ed", + "moc.ed-morf", + "moc.edocelgoog", + "moc.eerf-sndnyd", + "moc.eihcet-a-si", + "moc.eip-sekil", + "moc.elgooghtiw", + "moc.emina-otni-si", + "moc.emoh-sndnyd", + "moc.emoh-ta-sndnyd", + "moc.en-morf", + "moc.enog-si", + "moc.erihcec", + "moc.es", + "moc.esrun-a-si", + "moc.etinuarepo", + "moc.etis-ybboh", + "moc.etisaloy", + "moc.etomer-sndnyd", + "moc.evals-elcibuc-a-si", + "moc.evitavresnoc-a-si", + "moc.fehc-a-si", + "moc.golb-sndnyd", + "moc.golbsihtsetirw", + "moc.hn-morf", + "moc.ho-morf", + "moc.ih-morf", + "moc.ikiw-sndnyd", + "moc.im-morf", + "moc.ir-morf", + "moc.iw-morf", + "moc.jn-morf", + "moc.ka-morf", + "moc.kcils-si", + "moc.keeg-a-si", + "moc.keeg-asi", + "moc.kh", + "moc.klatsnaebcitsale", + "moc.ko-morf", + "moc.krow-sndnyd", + "moc.krow-ta-sndnyd", + "moc.ku", + "moc.la-morf", + "moc.larebil-a-si", + "moc.lf-morf", + "moc.li-morf", + "moc.liam-sndnyd", + "moc.lru-elpmis", + "moc.lru-taen", + "moc.lssukoreh", + "moc.mn-morf", + "moc.nacilbuper-a-si", + "moc.naf-sllub-a-si", + "moc.nafracsan-a-si", + "moc.naicisum-a-si", + "moc.nairatrebil-a-si", + "moc.nc", + "moc.neerg-a-si", + "moc.ni-morf", + "moc.nm-morf", + "moc.noisam-al-a-tse", + "moc.nortap-el-tse", + "moc.nosiam-al-a-tse", + "moc.npj", + "moc.nt-morf", + "moc.oc", + "moc.ojodsnd", + "moc.om-morf", + "moc.on", + "moc.or", + "moc.pi-sndnyd", + "moc.pifles", + "moc.piymteg", + "moc.ppaesaberif", + "moc.ppalortnocduolc", + "moc.ppaukoreh", + "moc.ra", + "moc.ra-morf", + "moc.ratskcor-a-si", + "moc.rb", + "moc.reenigne-na-si", + "moc.reggolb-a-si", + "moc.rehcaet-a-si", + "moc.rehpargotohp-a-si", + "moc.rekrow-drah-a-si", + "moc.rengised-a-si", + "moc.reniartlanosrep-a-si", + "moc.reniatretne-na-si", + "moc.repacsdnal-a-si", + "moc.repeekkoob-a-si", + "moc.reretac-a-si", + "moc.retniap-a-si", + "moc.retnuh-a-si", + "moc.revres-sndnyd", + "moc.reyalp-a-si", + "moc.reywal-a-si", + "moc.rezilibomdeepsegap", + "moc.rg", + "moc.rk", + "moc.ro-morf", + "moc.rosivdalaicnanif-a-si", + "moc.rotca-na-si", + "moc.rotcod-a-si", + "moc.rp-morf", + "moc.ruas-o-nyd", + "moc.rueugolb-nom-tse", + "moc.sailanyd", + "moc.sailasnd", + "moc.sbbevres", + "moc.scip-sndnyd", + "moc.selahw-eht-sevas", + "moc.semag-otni-si", + "moc.sipaelgoog", + "moc.sk-morf", + "moc.sm-morf", + "moc.sndgolb", + "moc.sndmood", + "moc.sndtog", + "moc.snootrac-otni-si", + "moc.srac-otni-si", + "moc.sretsohmaerd", + "moc.ssel-rof-slles", + "moc.ssertca-na-si", + "moc.su", + "moc.swanozama.1-etupmoc", + "moc.swanozama.1-etupmoc.1-z", + "moc.swanozama.1-etupmoc.2-z", + "moc.swanozama.1-tsae-as-3s", + "moc.swanozama.1-tsae-as-etisbew-3s", + "moc.swanozama.1-tsae-su", + "moc.swanozama.1-tsae-su-etisbew-3s", + "moc.swanozama.1-tsaehtron-pa-3s", + "moc.swanozama.1-tsaehtron-pa-etisbew-3s", + "moc.swanozama.1-tsaehtuos-pa-3s", + "moc.swanozama.1-tsaehtuos-pa-etisbew-3s", + "moc.swanozama.1-tsew-su-3s", + "moc.swanozama.1-tsew-su-etisbew-3s", + "moc.swanozama.1-tsew-ue-3s", + "moc.swanozama.1-tsew-ue-etisbew-3s", + "moc.swanozama.1-tsew-vog-su-3s", + "moc.swanozama.1-tsew-vog-su-etisbew-3s", + "moc.swanozama.1-tsew-vog-su-spif-3s", + "moc.swanozama.2-tsaehtuos-pa-3s", + "moc.swanozama.2-tsaehtuos-pa-etisbew-3s", + "moc.swanozama.2-tsew-su-3s", + "moc.swanozama.2-tsew-su-etisbew-3s", + "moc.swanozama.3s", + "moc.swanozama.ble", + "moc.swanozama.etupmoc", + "moc.swanozama.etupmoc.1-lartnec-ue", + "moc.swanozama.etupmoc.1-tsae-as", + "moc.swanozama.etupmoc.1-tsaehtron-pa", + "moc.swanozama.etupmoc.1-tsaehtuos-pa", + "moc.swanozama.etupmoc.1-tsew-su", + "moc.swanozama.etupmoc.1-tsew-ue", + "moc.swanozama.etupmoc.1-tsew-vog-su", + "moc.swanozama.etupmoc.2-tsaehtuos-pa", + "moc.swanozama.etupmoc.2-tsew-su", + "moc.tarcomed-a-si", + "moc.tc-morf", + "moc.teel-si", + "moc.teelrebu-si", + "moc.tm-morf", + "moc.tnatnuocca-na-si", + "moc.tneduts-a-si", + "moc.tner-ot-ecaps", + "moc.tnetnocresubuhtig", + "moc.topsedoc", + "moc.topsgolb", + "moc.topsppa", + "moc.tsihcrana-a-si", + "moc.tsihcrana-na-si", + "moc.tsilaicos-a-si", + "moc.tsipareht-a-si", + "moc.tsitra-na-si", + "moc.tsixetnod", + "moc.tsixetnseod", + "moc.tsohsfn", + "moc.tu-morf", + "moc.tunyekcoh-asi", + "moc.tv-morf", + "moc.u-rof-slles", + "moc.ue", + "moc.uh", + "moc.uoynahtretramssi", + "moc.ur", + "moc.urug-a-si", + "moc.vn-morf", + "moc.vw-morf", + "moc.xem", + "moc.xinuemoh", + "moc.xobaniateb", + "moc.xt-morf", + "moc.xunilemoh", + "moc.ydnacsekil", + "moc.yk-morf", + "moc.yu", + "moc.yw-morf", + "moor", + "mp", + "mraf", + "ms", + "mt", + "mt.gro", + "mt.lim", + "mt.moc", + "mt.mon", + "mt.oc", + "mt.ten", + "mt.ude", + "mt.vog", + "muesum", + "muesum.aatnav", + "muesum.aciaduj", + "muesum.acirfatsae", + "muesum.acrollam", + "muesum.adanac", + "muesum.adenomaledasac", + "muesum.adirolf", + "muesum.aeraaihpledalihp", + "muesum.aesrednu", + "muesum.aghannavas", + "muesum.agoonattahc", + "muesum.ahamo", + "muesum.aiauhsu", + "muesum.aibmuloc", + "muesum.aibmulochsitirb", + "muesum.aidem", + "muesum.aigroeg", + "muesum.aihpledalihp", + "muesum.ailartsua", + "muesum.ailetalif", + "muesum.ainigriv", + "muesum.ainrofilac", + "muesum.aissur", + "muesum.aitsonod", + "muesum.aksala", + "muesum.aksarben", + "muesum.allojal", + "muesum.aluossim", + "muesum.aluossimtrof", + "muesum.amabala", + "muesum.amanap", + "muesum.amenic", + "muesum.amom", + "muesum.amor", + "muesum.anacirema", + "muesum.anaidni", + "muesum.anedasap", + "muesum.anilorachtuos", + "muesum.anolecrab", + "muesum.arabrabatnas", + "muesum.arezzivs", + "muesum.asu", + "muesum.atnalta", + "muesum.atosennim", + "muesum.azalp", + "muesum.cdnotgnihsaw", + "muesum.cebeuq", + "muesum.cidepolcycne", + "muesum.cificap", + "muesum.cihpargonaeco", + "muesum.cilbup", + "muesum.cisum", + "muesum.citcarporihc", + "muesum.citlec", + "muesum.citnaltadim", + "muesum.civu", + "muesum.cyn", + "muesum.dadhgab", + "muesum.daetsmraf", + "muesum.dam", + "muesum.daorliar", + "muesum.dirdam", + "muesum.diulegnedleeb", + "muesum.dleif", + "muesum.dna", + "muesum.dnalgne", + "muesum.dnalnif", + "muesum.dnalragyduj", + "muesum.dnaltocs", + "muesum.dnaltrop", + "muesum.dnalyram", + "muesum.dnubrofsdgybmeh", + "muesum.dnuosdnaegami", + "muesum.draugria", + "muesum.drofxo", + "muesum.ecalap", + "muesum.ecalphtrib", + "muesum.ecaps", + "muesum.ecnalubma", + "muesum.ecnatsiser", + "muesum.ecnefedlatsaoc", + "muesum.ecnegilletni", + "muesum.ecneics", + "muesum.ecneicsfoyrotsih", + "muesum.ecpein", + "muesum.ecrof", + "muesum.ednukneklov", + "muesum.edrevasem", + "muesum.eert", + "muesum.eetsurt", + "muesum.efatnas", + "muesum.efildliw", + "muesum.egalliv", + "muesum.egatireh", + "muesum.egatirehlanoitan", + "muesum.egdirbmac", + "muesum.egrog", + "muesum.eicnum", + "muesum.einollaw", + "muesum.ekoorbrehs", + "muesum.elab", + "muesum.elbib", + "muesum.elcycrotom", + "muesum.elissim", + "muesum.elitxet", + "muesum.eloks", + "muesum.eltsac", + "muesum.emaffollah", + "muesum.emit", + "muesum.emitiram", + "muesum.emutsoc", + "muesum.engolos", + "muesum.enilno", + "muesum.enrecul", + "muesum.erauqs", + "muesum.erawaled", + "muesum.erawaledfoetats", + "muesum.erihsacnal", + "muesum.erihskroy", + "muesum.erihspmahwen", + "muesum.eriotsih", + "muesum.eromitlab", + "muesum.erutan", + "muesum.erutcetihcra", + "muesum.erutinruf", + "muesum.erutluc", + "muesum.erutlucirga", + "muesum.erutlucsu", + "muesum.ervuol", + "muesum.esabatad", + "muesum.esiacnarf", + "muesum.essius", + "muesum.esuoh", + "muesum.esuohlum", + "muesum.etalocohc", + "muesum.etarak", + "muesum.etats", + "muesum.etatse", + "muesum.etatseyrtnuoc", + "muesum.etatseyrtnuocsu", + "muesum.etimesoy", + "muesum.ettevroc", + "muesum.euqihpargonaeco", + "muesum.euvelleb", + "muesum.evitcaretni", + "muesum.evitomotua", + "muesum.fiuj", + "muesum.fohgrub", + "muesum.gnidliub", + "muesum.gniginerevmuesum", + "muesum.gnikiv", + "muesum.gnilahw", + "muesum.gninim", + "muesum.gnipeekemit", + "muesum.gnivil", + "muesum.gnulmmastsnuk", + "muesum.gorf", + "muesum.grebmerun", + "muesum.grebnrats", + "muesum.grebnreun", + "muesum.grubierf", + "muesum.gruble", + "muesum.grubmah", + "muesum.grubmuan", + "muesum.grubram", + "muesum.grubsmailliw", + "muesum.grubsmailliwlainoloc", + "muesum.grubsnaitsirhc", + "muesum.grubsretepts", + "muesum.grubzlas", + "muesum.gruobirf", + "muesum.gruobmexul", + "muesum.hatu", + "muesum.hcraeser", + "muesum.hcsirotsih", + "muesum.hcuot", + "muesum.hgrubsttip", + "muesum.hsitirb", + "muesum.hsiwej", + "muesum.htlaeh", + "muesum.htron", + "muesum.htrowtrof", + "muesum.htuomnom", + "muesum.htuoy", + "muesum.iiawah", + "muesum.iknisleh", + "muesum.iks", + "muesum.ilad", + "muesum.iladrodavlas", + "muesum.isissa", + "muesum.itannicnic", + "muesum.kcnivleeg", + "muesum.kcolc", + "muesum.kcolc-dna-hctaw", + "muesum.kcolcdnahctaw", + "muesum.kfj", + "muesum.kinebis", + "muesum.klis", + "muesum.klofron", + "muesum.knarfenna", + "muesum.knat", + "muesum.koorbnarc", + "muesum.kramned", + "muesum.kramreiets", + "muesum.kroy", + "muesum.kroywen", + "muesum.lacidem", + "muesum.lacigoloeahcra", + "muesum.lacigolomeg", + "muesum.lacigolooz", + "muesum.lacinatob", + "muesum.lacirotsih", + "muesum.laertnom", + "muesum.lairomem", + "muesum.lanoitacude", + "muesum.lanoitan", + "muesum.laoc", + "muesum.larutluc", + "muesum.latrop", + "muesum.lautriv", + "muesum.lavan", + "muesum.lenurb", + "muesum.lesab", + "muesum.lessurb", + "muesum.leutriv", + "muesum.liartnogero", + "muesum.lisarb", + "muesum.llabesab", + "muesum.llahsnoegrus", + "muesum.llehs", + "muesum.llerdnevle", + "muesum.llib", + "muesum.llim", + "muesum.llimdniw", + "muesum.lobup", + "muesum.loohcs", + "muesum.lotsirb", + "muesum.madretsma", + "muesum.maets", + "muesum.mahnetlehc", + "muesum.mahrud", + "muesum.mct", + "muesum.melas", + "muesum.melasurej", + "muesum.mlif", + "muesum.mlohkcots", + "muesum.mlu", + "muesum.mraf", + "muesum.msilanruoj", + "muesum.muesumyrotsihlarutan", + "muesum.muiratenalp", + "muesum.muirauqa", + "muesum.muterobra", + "muesum.nac", + "muesum.nacirema", + "muesum.naciremaevitan", + "muesum.nagihcim", + "muesum.naidni", + "muesum.naitpyge", + "muesum.namfoelsi", + "muesum.nawehctaksas", + "muesum.nedalokohcs", + "muesum.nedews", + "muesum.nedrag", + "muesum.nedragcinatob", + "muesum.nedraglacinatob", + "muesum.nedragsnerdlihc", + "muesum.nedragsu", + "muesum.negahnepoc", + "muesum.nehcneum", + "muesum.nelaftsew", + "muesum.neppahcsnetewruutan", + "muesum.nerdlihc", + "muesum.nerednaalv", + "muesum.nerhu", + "muesum.nerhudnutamieh", + "muesum.nesseig", + "muesum.ngised", + "muesum.ngiseddnatra", + "muesum.ngiseddnutsnuk", + "muesum.nhab", + "muesum.nhabnesie", + "muesum.nhojts", + "muesum.nilreb", + "muesum.nitsua", + "muesum.nleok", + "muesum.nlocnil", + "muesum.nnob", + "muesum.nnurbneohcs", + "muesum.nodnol", + "muesum.nogero", + "muesum.noisivdnadnuos", + "muesum.noisivelet", + "muesum.noisnam", + "muesum.noitacinummoc", + "muesum.noitacude", + "muesum.noitacudetra", + "muesum.noitadnuof", + "muesum.noitaerc", + "muesum.noitaicossa", + "muesum.noitaiva", + "muesum.noitakinummokelet", + "muesum.noitanissassa", + "muesum.noitarbelectsevrah", + "muesum.noitaroproc", + "muesum.noitartsulli", + "muesum.noitasilivic", + "muesum.noitatnalp", + "muesum.noitats", + "muesum.noitavreserp", + "muesum.noitavresnoclatnemnorivne", + "muesum.noitazilivic", + "muesum.noitcelloc", + "muesum.noitcif-ecneics", + "muesum.noitibihxe", + "muesum.nori", + "muesum.nosdnah", + "muesum.nosimaj", + "muesum.nosreffej", + "muesum.nossral", + "muesum.noterbepac", + "muesum.notnilc", + "muesum.notsob", + "muesum.nreb", + "muesum.nredom", + "muesum.nretsew", + "muesum.nreuab", + "muesum.nrezul", + "muesum.nrobredap", + "muesum.nvahnebeok", + "muesum.nwot", + "muesum.oablib", + "muesum.ocedtra", + "muesum.ocixemwen", + "muesum.ocsicnarfnas", + "muesum.oelap", + "muesum.ogacihc", + "muesum.ogato", + "muesum.ogeidnas", + "muesum.oicadnuf", + "muesum.oidiserp", + "muesum.oiratno", + "muesum.ollecitnom", + "muesum.omitiram", + "muesum.onirot", + "muesum.orhtna", + "muesum.orienajedoir", + "muesum.pohskrow", + "muesum.qari", + "muesum.raw", + "muesum.rawdloc", + "muesum.rawlivic", + "muesum.rdd", + "muesum.rebma", + "muesum.rebyc", + "muesum.reirrac", + "muesum.rellimsiwel", + "muesum.renaksiznarf", + "muesum.repapswen", + "muesum.retaeht", + "muesum.retexe", + "muesum.retnec", + "muesum.retnececneics", + "muesum.retneclarutluc", + "muesum.retnecmuesum", + "muesum.retnectra", + "muesum.retsehcnam", + "muesum.retsehcor", + "muesum.retsneum", + "muesum.retupmoc", + "muesum.ria", + "muesum.rianepo", + "muesum.robal", + "muesum.ruasonid", + "muesum.ruobal", + "muesum.rutakirak", + "muesum.salg", + "muesum.sallad", + "muesum.salleh", + "muesum.saxet", + "muesum.sdik", + "muesum.sdipardnarg", + "muesum.secneics", + "muesum.secneicslarutan", + "muesum.sednal", + "muesum.sehcsideuj", + "muesum.sehcsirotsih", + "muesum.sehcsirotsihnizidem", + "muesum.sehcsirotsihrutan", + "muesum.sehcsiselhcs", + "muesum.seitinamuh", + "muesum.selaw", + "muesum.selegnasol", + "muesum.sellerutansecneics", + "muesum.sellexurb", + "muesum.selliasrev", + "muesum.sereem", + "muesum.sereugif", + "muesum.sertsac", + "muesum.sesuohcirotsih", + "muesum.seuen", + "muesum.seuqadac", + "muesum.seuqitna", + "muesum.seuqitnanacirema", + "muesum.seuqitnasu", + "muesum.seõçacinumoc", + "muesum.seõçacinumocelet-e-soierroc", + "muesum.sgnirpsmlap", + "muesum.shtab", + "muesum.silopanaidni", + "muesum.sirap", + "muesum.siuoltnias", + "muesum.sixa", + "muesum.slessurb", + "muesum.sllod", + "muesum.smraeriflanoitan", + "muesum.snablats", + "muesum.snal", + "muesum.snerdlihc", + "muesum.snoisnam", + "muesum.snoitacinummoc", + "muesum.snoitacinummocelet-dna-stsop", + "muesum.snäl", + "muesum.srednalf", + "muesum.srelttes", + "muesum.sremraf", + "muesum.srenim", + "muesum.sretnececneics", + "muesum.ssalg", + "muesum.sserp", + "muesum.stfarc", + "muesum.stfarcdnastra", + "muesum.stnalp", + "muesum.stolip", + "muesum.stra", + "muesum.straenif", + "muesum.straevitaroced", + "muesum.straevitarocedsu", + "muesum.strasu", + "muesum.straxuaeb", + "muesum.sub", + "muesum.submuloc", + "muesum.sucric", + "muesum.tagilltrop", + "muesum.tcejorp", + "muesum.tdats", + "muesum.teesum", + "muesum.tekramnaidni", + "muesum.tiorted", + "muesum.tnemelttes", + "muesum.tnemnorivne", + "muesum.tnempiuqemraf", + "muesum.tnevnoc", + "muesum.toped", + "muesum.tra", + "muesum.tradrib", + "muesum.traenif", + "muesum.tragttuts", + "muesum.trahsiwej", + "muesum.trakcor", + "muesum.tranacirema", + "muesum.tranootrac", + "muesum.tratamsa", + "muesum.trayraropmetnoc", + "muesum.tropaes", + "muesum.tropsnart", + "muesum.tropwen", + "muesum.trufknarf", + "muesum.tsacdaorb", + "muesum.tsaoctsae", + "muesum.tsewhtuos", + "muesum.tsilayol", + "muesum.tsnuk", + "muesum.tsrohnemled", + "muesum.tsruhlyram", + "muesum.tsurt", + "muesum.uabgreb", + "muesum.uaetalpodaroloc", + "muesum.urmyc", + "muesum.wocsom", + "muesum.wrn", + "muesum.xesse", + "muesum.xineohp", + "muesum.xnam", + "muesum.xtas", + "muesum.yabekaepasehc", + "muesum.yawetag", + "muesum.yawliar", + "muesum.ycamrahp", + "muesum.ydoc", + "muesum.yehsub", + "muesum.yelekreb", + "muesum.yellav", + "muesum.yellaveniwydnarb", + "muesum.yellort", + "muesum.yendys", + "muesum.yenom", + "muesum.yerrus", + "muesum.yesnreug", + "muesum.yesrejwen", + "muesum.ygoloeahcra", + "muesum.ygoloeg", + "muesum.ygolomotne", + "muesum.ygolonhcet", + "muesum.ygolonhte", + "muesum.ygolooz", + "muesum.ygoloporhtna", + "muesum.ygolopot", + "muesum.ygoloroh", + "muesum.yhpargotohp", + "muesum.yletalihp", + "muesum.ylimaf", + "muesum.ymedaca", + "muesum.ymonortsa", + "muesum.ynatob", + "muesum.ynyn", + "muesum.yps", + "muesum.yraropmetnoc", + "muesum.yratilim", + "muesum.yrediorbme", + "muesum.yrellag", + "muesum.yrellagtra", + "muesum.yrevocsid", + "muesum.yrlewej", + "muesum.yrnosameerf", + "muesum.yrotsih", + "muesum.yrotsihdnaecneics", + "muesum.yrotsihecneics", + "muesum.yrotsihgnivil", + "muesum.yrotsihgnivilsu", + "muesum.yrotsihlacol", + "muesum.yrotsihlarutan", + "muesum.yrotsihretupmoc", + "muesum.yrotsihsu", + "muesum.yrtsudnidnaecneics", + "muesum.yspelipe", + "muesum.yteicos", + "muesum.yteicoslacirotsih", + "muesum.ytinummoc", + "muesum.ytisrevinu", + "muesum.ytnuoc", + "muesum.zarg", + "muesum.ziewhcs", + "muesum.znil", + "muesum.zojadab", + "muesum.zurcatnas", + "muesum.моки", + "muesum.×ילשורי", + "na", + "na.gro", + "na.moc", + "na.ten", + "na.ude", + "nabrud", + "nacilbuper", + "naf", + "nam", + "naol", + "nasood", + "nassin", + "navarac", + "nbc", + "nc", + "nc.ah", + "nc.bh", + "nc.ca", + "nc.cs", + "nc.dg", + "nc.ds", + "nc.eh", + "nc.gro", + "nc.ha", + "nc.hq", + "nc.hs", + "nc.ih", + "nc.jb", + "nc.jf", + "nc.jt", + "nc.jx", + "nc.jz", + "nc.kh", + "nc.lh", + "nc.lim", + "nc.lj", + "nc.mn", + "nc.moc", + "nc.nh", + "nc.nl", + "nc.ns", + "nc.ny", + "nc.om", + "nc.qc", + "nc.sg", + "nc.sj", + "nc.swanozama.etupmoc", + "nc.swanozama.etupmoc.1-htron-nc", + "nc.ten", + "nc.ude", + "nc.vog", + "nc.wt", + "nc.xg", + "nc.xj", + "nc.xn", + "nc.xs", + "nc.zg", + "nc.zx", + "nc.å¸å…¬", + "nc.絡網", + "nc.络网", + "ncb", + "ndg", + "nedrag", + "neerg", + "nefuak", + "nehctik", + "neilibommi", + "neiw", + "ner", + "nerednaalv", + "nesier", + "ng", + "ng.ca", + "ng.gro", + "ng.moc", + "ng.ten", + "ng.ude", + "ng.vog", + "ngised", + "nh", + "nh.bog", + "nh.gro", + "nh.lim", + "nh.moc", + "nh.ten", + "nh.ude", + "ni", + "ni.ca", + "ni.cin", + "ni.dni", + "ni.gro", + "ni.lim", + "ni.mrif", + "ni.neg", + "ni.oc", + "ni.ser", + "ni.ten", + "ni.topsgolb", + "ni.ude", + "ni.vog", + "nigriv", + "nilreb", + "nip", + "nipul", + "niw", + "nix", + "nk", + "nk.gro", + "nk.ten", + "nk.ude", + "nk.vog", + "nleok", + "nlocnil", + "nm", + "nm.cyn", + "nm.gro", + "nm.ude", + "nm.vog", + "nodnol", + "noihsaf", + "noisiv", + "noisivorue", + "noitacude", + "noitadnuof", + "noitcua", + "noitcurtsnoc", + "nolas", + "nomrom", + "nonac", + "nopq", + "nosiail", + "nosiam", + "nospe", + "notron", + "np", + "np.gro", + "np.oc", + "np.ten", + "np.ude", + "np.vog", + "nrec", + "nreyab", + "nrop", + "ns", + "ns.gro", + "ns.moc", + "ns.osrep", + "ns.tra", + "ns.ude", + "ns.vinu", + "ns.vuog", + "nt", + "nt.dni", + "nt.esnefed", + "nt.gro", + "nt.ltni", + "nt.moc", + "nt.mocnim", + "nt.msiruot", + "nt.nerut", + "nt.nif", + "nt.ofni", + "nt.osrep", + "nt.sne", + "nt.snr", + "nt.tan", + "nt.ten", + "nt.tenirga", + "nt.tenude", + "nt.trnr", + "nt.unr", + "nt.vog", + "ntm", + "nustad", + "nuxamay", + "nv", + "nv.ca", + "nv.eman", + "nv.gro", + "nv.htlaeh", + "nv.moc", + "nv.ofni", + "nv.orp", + "nv.ten", + "nv.tni", + "nv.ude", + "nv.vog", + "nv.zib", + "nworc", + "nwot", + "nwotepac", + "oa", + "oa.bp", + "oa.de", + "oa.go", + "oa.oc", + "oa.ti", + "oa.vg", + "ob", + "ob.bog", + "ob.gro", + "ob.lim", + "ob.moc", + "ob.ten", + "ob.tni", + "ob.ude", + "ob.vog", + "ob.vt", + "obolg", + "oc", + "oc.bew", + "oc.cer", + "oc.gro", + "oc.lim", + "oc.moc", + "oc.mon", + "oc.mrif", + "oc.ofni", + "oc.stra", + "oc.ten", + "oc.tni", + "oc.ude", + "oc.vog", + "ocin", + "ocmara", + "ocsedarb", + "od", + "od.bew", + "od.bog", + "od.dls", + "od.gro", + "od.lim", + "od.moc", + "od.ten", + "od.tra", + "od.ude", + "od.vog", + "odagoba", + "oec", + "oediv", + "oedor", + "oem", + "of", + "ofni", + "ofni.egdelwonk-fo-lerrab", + "ofni.egdelwonk-fo-llerrab", + "ofni.eht-skorg", + "ofni.erom-rof-ereh", + "ofni.llatiswonk", + "ofni.pifles", + "ofni.pohbew", + "ofni.ruo-rof", + "ofni.siht-skorg", + "ofni.sndnyd", + "ogn", + "ognam", + "ognib", + "ohwsohw", + "oi", + "oi.buhtig", + "oi.din", + "oi.moc", + "oib", + "oidua", + "oir", + "oisyhp", + "oj", + "oj.eman", + "oj.gro", + "oj.hcs", + "oj.lim", + "oj.moc", + "oj.ten", + "oj.ude", + "oj.vog", + "om", + "om.gro", + "om.moc", + "om.ten", + "om.ude", + "om.vog", + "omg", + "omil", + "ommi", + "omorp", + "on", + "on.aa", + "on.aa.sg", + "on.addo", + "on.adiisevvad", + "on.adlov", + "on.adnarts", + "on.aduas", + "on.agav", + "on.agev", + "on.aglot", + "on.agrajnevvad", + "on.agrajnu", + "on.agrájnevvad", + "on.agrájnu", + "on.ah", + "on.ajddadab", + "on.ajsel", + "on.akel", + "on.akhojsarak", + "on.akhojšárák", + "on.akiivagnael", + "on.akiivagnag", + "on.akiivagÅ‹ael", + "on.akiivagŋág", + "on.akiivran", + "on.alf", + "on.allahrevo", + "on.aloms", + "on.alos", + "on.alsennev", + "on.alus", + "on.aløms", + "on.amuar", + "on.anar", + "on.anar-i-om", + "on.anarf", + "on.anart", + "on.anat", + "on.andouvsatvid", + "on.ankiv", + "on.anmos", + "on.anmøs", + "on.annod", + "on.annød", + "on.anra", + "on.ansen", + "on.antaouvatheig", + "on.antouvacchab", + "on.antouvaccháb", + "on.antouvachab", + "on.antouvacháb", + "on.antouvan", + "on.antouviag", + "on.antouviág", + "on.antouvsamo", + "on.antouvsattvid", + "on.antouván", + "on.anærf", + "on.anært", + "on.appol", + "on.aristu", + "on.arkua", + "on.arolf", + "on.artih", + "on.asans", + "on.asaons", + "on.asiar", + "on.asierdron", + "on.asierros", + "on.asierrøs", + "on.asiár", + "on.aslag", + "on.aslah", + "on.asmor", + "on.asmort", + "on.assir", + "on.asuf", + "on.asÃ¥ns", + "on.atkoulonka", + "on.atkouloÅ‹ká", + "on.atla", + "on.atsaeframmah", + "on.atsaefrámmáh", + "on.atsorf", + "on.atsro", + "on.atsrø", + "on.av", + "on.av.sg", + "on.avledatskork", + "on.avreiks", + "on.ayorf", + "on.ayørf", + "on.dareh", + "on.darehnnivk", + "on.darehsdork", + "on.darehsdørk", + "on.darehuas", + "on.datsebi", + "on.datsekkar", + "on.datsellyh", + "on.datsennan", + "on.datsgort", + "on.datsgørt", + "on.datskalf", + "on.datskirderf", + "on.datslevev", + "on.datsmirg", + "on.datsobegah", + "on.datsobegæh", + "on.datsrah", + "on.datsrejg", + "on.die", + "on.dierah", + "on.diesetivk", + "on.diesladman", + "on.dlofros", + "on.dlofrøs", + "on.dloftsev.ednas", + "on.dloftso.relav", + "on.dloftsø.relÃ¥v", + "on.dnal-erdnos", + "on.dnal-erdnøs", + "on.dnal-erdron", + "on.dnaladroh.so", + "on.dnaldron.ob", + "on.dnaldron.yoreh", + "on.dnaldron.yøreh", + "on.dnaldron.øb", + "on.dnalem", + "on.dnalemlejh", + "on.dnaleproj", + "on.dnaleprøj", + "on.dnalevi", + "on.dnalgyb", + "on.dnalnaks", + "on.dnalnÃ¥ks", + "on.dnaloh-goksrua", + "on.dnalorf", + "on.dnalro", + "on.dnalrua", + "on.dnalrø", + "on.dnaltros", + "on.dnaløh-goksrua", + "on.dnarts", + "on.dnartsedevt", + "on.dnartselab", + "on.dnartsemloh", + "on.dnasellil", + "on.dnasnaitsirk", + "on.dnasrof", + "on.dnul", + "on.dnulos", + "on.dnus", + "on.dnusdim", + "on.dnusdlejt", + "on.dnuseguah", + "on.dnusela", + "on.dnuselÃ¥", + "on.dnuskkoh", + "on.dnuslavk", + "on.dnusnaitsirk", + "on.dnusraf", + "on.dnusrege", + "on.dnusregie", + "on.dnustef", + "on.dnusyonnorb", + "on.dnusyønnørb", + "on.drablavs", + "on.drablavs.sg", + "on.drageppo", + "on.dragla", + "on.drojfa", + "on.drojfadniv", + "on.drojfak", + "on.drojfavk", + "on.drojfdie", + "on.drojfednas", + "on.drojfekkelf", + "on.drojfllins", + "on.drojfriel", + "on.drojfrots", + "on.drojfslab", + "on.drojfstab", + "on.drojfstÃ¥b", + "on.drojfsyt", + "on.drojfÃ¥", + "on.drojfÃ¥k", + "on.drojfævk", + "on.drojles", + "on.drots", + "on.drÃ¥geppo", + "on.drÃ¥glÃ¥", + "on.dureksub.sen", + "on.edar", + "on.edlom", + "on.edrof", + "on.edrøf", + "on.edÃ¥r", + "on.eggyr", + "on.egnats", + "on.eimeuvejsemaan", + "on.eimeuvejsemåån", + "on.eimeuvsekaal", + "on.eirjea", + "on.ejdef", + "on.ejdoks", + "on.ejles", + "on.ekeraom", + "on.ekerÃ¥om", + "on.ekhgnaark", + "on.ekhgnaÃ¥rk", + "on.ekiregnir", + "on.ekkot", + "on.ekkots", + "on.eksig", + "on.eksuaf", + "on.elbmab", + "on.elkyb", + "on.ellav", + "on.ellehtats", + "on.eloh", + "on.emit", + "on.emojt", + "on.emøjt", + "on.enarg", + "on.engos", + "on.engøs", + "on.enmeh", + "on.enreil", + "on.ente", + "on.enummok", + "on.enyrb", + "on.erdils-ertsev", + "on.erdils-ertsyo", + "on.erdils-ertsyø", + "on.erua", + "on.ervod", + "on.esaans", + "on.esaÃ¥ns", + "on.etrobraa", + "on.etspaav", + "on.eurg", + "on.fo", + "on.fo.sg", + "on.foh", + "on.fs", + "on.fs.sg", + "on.fv", + "on.fv.sg", + "on.gallor", + "on.gavegnal", + "on.gavelreb", + "on.gnav", + "on.gnavsnellu", + "on.gnorg", + "on.goksdie", + "on.goksmor", + "on.goksmør", + "on.goksnerol", + "on.goksnerøl", + "on.goksro", + "on.goksrø", + "on.greb", + "on.grebadnar", + "on.grebedyps", + "on.grebsdie", + "on.grebself", + "on.grebsgnok", + "on.grebsnot", + "on.grebsnøt", + "on.grobspras", + "on.guahatsla", + "on.gÃ¥vegnal", + "on.gÃ¥velreb", + "on.ha", + "on.ha.sg", + "on.iehsragev", + "on.iehsrÃ¥gev", + "on.ierf", + "on.ikhavlaraeb", + "on.ikhávlaraeb", + "on.iks", + "on.ilma", + "on.ilmÃ¥", + "on.impouvtalam", + "on.impouvtalám", + "on.ipphal", + "on.ipphál", + "on.irrounaddleid", + "on.issaneve", + "on.iššáneve", + "on.kabord", + "on.kabørd", + "on.kajks", + "on.kalleis", + "on.kiv", + "on.kivaklejps", + "on.kivlam", + "on.kivlevs", + "on.kivlu", + "on.kivmag", + "on.kivnel", + "on.kivojg", + "on.kivral", + "on.kivran", + "on.kivrepok", + "on.kivriel", + "on.kivryor", + "on.kivryør", + "on.kivsah", + "on.kivskel", + "on.kivsom", + "on.kivøjg", + "on.kkabene", + "on.kojsarak", + "on.kramdeh.relav", + "on.kramdeh.relÃ¥v", + "on.kramdeh.so", + "on.kramelet.ob", + "on.kramelet.øb", + "on.kramera", + "on.kÃ¥jks", + "on.la", + "on.ladanrus", + "on.laddnumurb", + "on.laddron", + "on.ladegnard", + "on.ladente", + "on.ladesmeh", + "on.ladessin", + "on.ladettin", + "on.ladgis", + "on.ladgnyl", + "on.ladkro", + "on.ladlem", + "on.ladllejfttah", + "on.ladllof", + "on.ladluag-ertdim", + "on.ladlus", + "on.ladnam", + "on.ladnera", + "on.ladngos", + "on.ladnib", + "on.ladninroh", + "on.ladnir", + "on.ladnkos", + "on.ladnnus", + "on.ladnoj", + "on.lado-dron", + "on.lado-ros", + "on.lado-røs", + "on.ladppo", + "on.ladra", + "on.ladral", + "on.ladranram", + "on.ladregne", + "on.ladrel", + "on.ladrev", + "on.ladris", + "on.ladrojts", + "on.ladrots", + "on.ladrua-dron", + "on.ladrua-ros", + "on.ladrua-røs", + "on.ladruh", + "on.ladrÃ¥", + "on.ladræl", + "on.ladrøjts", + "on.ladsejg", + "on.ladsenivk", + "on.ladseryf", + "on.ladskav", + "on.ladsmor-go-erom.ednas", + "on.ladsmor-go-erom.yoreh", + "on.ladsmor-go-erøm.ednas", + "on.ladsmor-go-erøm.yøreh", + "on.ladsuag", + "on.ladtlas", + "on.ladtrajh", + "on.ladtsuan", + "on.ladvla", + "on.ladvle-rots", + "on.ladvu-go-eron", + "on.ladyt", + "on.laksedlig", + "on.laresa", + "on.laresÃ¥", + "on.lbibeklof", + "on.lbibseklyf", + "on.les", + "on.lesdah", + "on.lh", + "on.lh.sg", + "on.lim", + "on.lisyrt", + "on.llejf", + "on.llovetsua", + "on.llovgnit", + "on.llovksa", + "on.llovsdie", + "on.ln", + "on.ln.sg", + "on.lo", + "on.lo.sg", + "on.loboh", + "on.log", + "on.loh", + "on.lr", + "on.lr.sg", + "on.lÃ¥", + "on.lÃ¥ksedlig", + "on.løboh", + "on.marah", + "on.mavk", + "on.mf", + "on.mf.sg", + "on.mh", + "on.mh.sg", + "on.miehdnort", + "on.miehrtsua", + "on.miehssej", + "on.mierkrejb", + "on.miksa", + "on.mol", + "on.mt", + "on.mt.sg", + "on.mudom", + "on.muesum", + "on.murab", + "on.murdrejg", + "on.murevle", + "on.muros", + "on.muruh", + "on.muræb", + "on.murøs", + "on.muttals", + "on.nagav", + "on.nagokssman", + "on.nagÃ¥v", + "on.najlis", + "on.naor", + "on.narg", + "on.narrev", + "on.neddosen", + "on.neddoton", + "on.nedlah", + "on.nedragyo", + "on.nedragyø", + "on.nedrojfsam", + "on.negiets", + "on.negnalas", + "on.negnallab", + "on.negnanavk", + "on.negnanævk", + "on.negnatarg", + "on.negnatddosen", + "on.negnaval", + "on.negnavessov", + "on.negnidol", + "on.negnidøl", + "on.negnilar", + "on.negnilær", + "on.negnyl", + "on.negreb", + "on.neiks", + "on.neksrot", + "on.nekyor", + "on.nekyør", + "on.neladgnos", + "on.neladner", + "on.neladnojm", + "on.neladnøjm", + "on.neladom", + "on.nelatloh", + "on.nelug", + "on.nelÃ¥tloh", + "on.nemmard", + "on.neojsom", + "on.neojssendnas", + "on.neppolg", + "on.neslahsladrojts", + "on.neslahsladrøjts", + "on.neso", + "on.netol", + "on.netot-ertsev", + "on.netot-ertso", + "on.netot-ertsø", + "on.netroh", + "on.netøl", + "on.nevlykkys", + "on.nevlynav", + "on.neyam-naj", + "on.neyam-naj.sg", + "on.neøjsom", + "on.neøjssendnas", + "on.ngorf", + "on.ngujb", + "on.nidnaort", + "on.nivnarg", + "on.nkob", + "on.nladendua", + "on.nmaherka", + "on.nmaherkÃ¥", + "on.nnit", + "on.nnurgsrop", + "on.norf-dron", + "on.norf-ros", + "on.norf-røs", + "on.nraieb", + "on.nrevats", + "on.nsfev", + "on.nuaks", + "on.nyrts", + "on.oc", + "on.odob", + "on.odrav", + "on.oievs", + "on.okssouf", + "on.olmob", + "on.olmøb", + "on.olousadna", + "on.olousechac", + "on.olousecháÄ", + "on.olso", + "on.olso.sg", + "on.omsdeks", + "on.oniekotuak", + "on.oregark", + "on.orolf", + "on.oryoso", + "on.oryøso", + "on.osdav", + "on.osmort", + "on.ped", + "on.ppakdron", + "on.ppelk", + "on.raddjab", + "on.raddjáb", + "on.radiab", + "on.rajtif", + "on.raluag", + "on.ramah", + "on.ravsyt", + "on.regnakiel", + "on.regnakro", + "on.regnamerb", + "on.regnanat", + "on.regnanmas", + "on.regnarav-ros", + "on.regnarav-røs", + "on.regnasrop", + "on.regnatalf", + "on.regnavats", + "on.regnavel", + "on.regnayoh", + "on.regnayøh", + "on.regnivsgnok", + "on.reil", + "on.rejkniets", + "on.rekanvej", + "on.rekarem", + "on.rekasgnir", + "on.rekasnellu", + "on.rekie-erden", + "on.rekie-ervo", + "on.rekie-ervø", + "on.rekram", + "on.reksa", + "on.rekÃ¥rem", + "on.relajf", + "on.relavh", + "on.rembah", + "on.rembáh", + "on.remmahellil", + "on.rennul", + "on.retsloj", + "on.retsløj", + "on.retsul", + "on.reyo", + "on.reyø", + "on.rimpah", + "on.rimpáh", + "on.rm", + "on.rm.sg", + "on.rosir", + "on.rt", + "on.rt.sg", + "on.rádiáb", + "on.rævsyt", + "on.røsir", + "on.sadnil", + "on.sendnas", + "on.senedga", + "on.senekrib", + "on.senekrik", + "on.seneksom", + "on.seneve", + "on.senmeh", + "on.senmejg", + "on.sennroh-go-ejve", + "on.sensa", + "on.sensednil", + "on.sensko", + "on.senskø", + "on.sensof", + "on.sensyt", + "on.sensÃ¥", + "on.sentsev", + "on.sgv", + "on.shf", + "on.sigaval", + "on.soror", + "on.sorør", + "on.sosman", + "on.ssofenoh", + "on.ssofenøh", + "on.ssom", + "on.ssov", + "on.suhlem", + "on.suhsreka.sen", + "on.sÃ¥", + "on.sÃ¥dnil", + "on.tabaol", + "on.tagaov", + "on.taggrav", + "on.tajjrav-attam", + "on.tajjráv-attám", + "on.talab", + "on.talas", + "on.talás", + "on.tasoum", + "on.tats", + "on.taveib", + "on.tavour", + "on.tednalyoh", + "on.tednalyøh", + "on.tef", + "on.tesnyt", + "on.tesrokomsdeks", + "on.tessen", + "on.tevtpiks", + "on.tinaks", + "on.tináks", + "on.tlohar", + "on.tlohÃ¥r", + "on.tn", + "on.tn.sg", + "on.toma", + "on.tomÃ¥", + "on.topsgolb", + "on.ts", + "on.ts.sg", + "on.tsefremmah", + "on.tsor", + "on.tsør", + "on.tterdi", + "on.tábaol", + "on.tággráv", + "on.táláb", + "on.tálás", + "on.tásoum", + "on.táveib", + "on.ub", + "on.ub.sg", + "on.ubalk", + "on.ubedna", + "on.ubegnir", + "on.ubenner", + "on.ubles", + "on.ubælk", + "on.udrab", + "on.udraeb", + "on.ugnasrop", + "on.ugvi", + "on.ugŋásrop", + "on.ujdaehala", + "on.ujdaehalá", + "on.ujjedub", + "on.ujvarekkhar", + "on.ujvárekkhár", + "on.uksiouf", + "on.undiaegadvoug", + "on.untaed", + "on.virp", + "on.vleslam", + "on.vleslÃ¥m", + "on.ybessen", + "on.ybnart", + "on.ybsebel", + "on.ybtsev", + "on.yodar", + "on.yodna", + "on.yodnas", + "on.yodor", + "on.yogavtsev", + "on.yokrajb", + "on.yoksa", + "on.yolem", + "on.yomrak", + "on.yonart", + "on.yonnif", + "on.yonnorb", + "on.yoramah", + "on.yoran", + "on.yorav", + "on.yoredni", + "on.yoretso", + "on.yoretton", + "on.yoreva", + "on.yorul", + "on.yoryd", + "on.yosam", + "on.yosenner", + "on.yosgav", + "on.yoslrak", + "on.yostivk", + "on.yovrejks", + "on.yødar", + "on.yødna", + "on.yødnas", + "on.yødør", + "on.yøgÃ¥vtsev", + "on.yøkrajb", + "on.yøksa", + "on.yølem", + "on.yømrak", + "on.yønart", + "on.yønnif", + "on.yønnørb", + "on.yøredni", + "on.yøretso", + "on.yørettøn", + "on.yøreva", + "on.yørul", + "on.yøryd", + "on.yøræn", + "on.yøræv", + "on.yøsenner", + "on.yøsgÃ¥v", + "on.yøstivk", + "on.yøsÃ¥m", + "on.yøvrejks", + "on.áslág", + "on.átlá", + "on.ávreiks", + "on.Ã¥gÃ¥v", + "on.Ã¥h", + "on.Ã¥jddÃ¥dÃ¥b", + "on.Ã¥lf", + "on.ødob", + "on.ødrav", + "on.øregark", + "on.ørolf", + "on.øsdav", + "on.øsmort", + "onisac", + "onu", + "oob", + "oof", + "oog", + "ooo", + "oottat", + "opas", + "or", + "or.cer", + "or.erots", + "or.gro", + "or.moc", + "or.mon", + "or.mrif", + "or.mt", + "or.ofni", + "or.stra", + "or.tn", + "or.topsgolb", + "or.www", + "orea", + "orea.aac", + "orea.acgd", + "orea.aidem", + "orea.bulc", + "orea.bulcorea", + "orea.ciffartria", + "orea.citaborea", + "orea.ecalptekram", + "orea.ecnallievrus-ria", + "orea.ecnalubma", + "orea.ecnanetniam", + "orea.ecnarusni", + "orea.ecnerefnoc", + "orea.egnahcxe", + "orea.emordorea", + "orea.enigne", + "orea.enilria", + "orea.enizagam", + "orea.erawtfos", + "orea.gnidart", + "orea.gnidilg", + "orea.gnidilgarap", + "orea.gnidilggnah", + "orea.gnildnahdnuorg", + "orea.gnilledom", + "orea.gninoollab", + "orea.gniretac", + "orea.gnisael", + "orea.gnitlusnoc", + "orea.gnituhcarap", + "orea.gnividyks", + "orea.hcraeser", + "orea.ixat", + "orea.lanruoj", + "orea.leuf", + "orea.licnuoc", + "orea.lortnoc", + "orea.lortnoc-ciffart-ria", + "orea.ngised", + "orea.noinu", + "orea.noitacifitrec", + "orea.noitaercer", + "orea.noitagitsevni-tnedicca", + "orea.noitagivan", + "orea.noitaicossa", + "orea.noitaicossa-regnessap", + "orea.noitaivalivic", + "orea.noitaredef", + "orea.noitcudorp", + "orea.noitneverp-tnedicca", + "orea.ograc", + "orea.pihsnoipmahc", + "orea.puorg", + "orea.puorggnikrow", + "orea.redart", + "orea.reenigne", + "orea.rekorb", + "orea.reniart", + "orea.retrahc", + "orea.rohtua", + "orea.rotacude", + "orea.scitsigol", + "orea.secivres", + "orea.ser", + "orea.skrow", + "orea.sserp", + "orea.sserpxe", + "orea.stnega", + "orea.tfarcria", + "orea.tfarcrotor", + "orea.thgierf", + "orea.thgilf", + "orea.thgilorcim", + "orea.tliubemoh", + "orea.tnatlusnoc", + "orea.tneduts", + "orea.tnemesuma", + "orea.tnemniatretne", + "orea.tnemnrevog", + "orea.tnempiuqe", + "orea.tolip", + "orea.tropria", + "orea.tsilanruoj", + "orea.tsitneics", + "orea.werc", + "orea.wohs", + "orea.ycnegreme", + "orea.ydobper", + "orea.ytefas", + "orerref", + "orez", + "orp", + "orp.aca", + "orp.apc", + "orp.dem", + "orp.gne", + "orp.rab", + "orp.ruj", + "orp.wal", + "os", + "os.gro", + "os.moc", + "os.ten", + "ot", + "ot.gro", + "ot.lim", + "ot.moc", + "ot.ten", + "ot.ude", + "ot.vog", + "otohp", + "otov", + "otoyk", + "ottol", + "otua", + "oxas", + "oykot", + "paehc", + "pas", + "pg", + "pg.gro", + "pg.ibom", + "pg.moc", + "pg.ossa", + "pg.ten", + "pg.ude", + "pir", + "piz", + "pj", + "pj.abihc", + "pj.abihc.abihciakoy", + "pj.abihc.adon", + "pj.abihc.amahim", + "pj.abihc.amayeragan", + "pj.abihc.amayetat", + "pj.abihc.arabom", + "pj.abihc.aragan", + "pj.abihc.arahihci", + "pj.abihc.aruagedos", + "pj.abihc.arukas", + "pj.abihc.aruustak", + "pj.abihc.asos", + "pj.abihc.asufomihs", + "pj.abihc.atamihcay", + "pj.abihc.atiran", + "pj.abihc.awagimanah", + "pj.abihc.awagimo", + "pj.abihc.awagomak", + "pj.abihc.awakihci", + "pj.abihc.awazustum", + "pj.abihc.awihsak", + "pj.abihc.ayagamak", + "pj.abihc.ayimonihci", + "pj.abihc.eakas", + "pj.abihc.enagot", + "pj.abihc.iazni", + "pj.abihc.iesohc", + "pj.abihc.ihasa", + "pj.abihc.ihsabanuf", + "pj.abihc.ihsohc", + "pj.abihc.ikato", + "pj.abihc.ikazok", + "pj.abihc.imusi", + "pj.abihc.iorihs", + "pj.abihc.irakihabihsokoy", + "pj.abihc.irodim", + "pj.abihc.irotak", + "pj.abihc.irukujuk", + "pj.abihc.iusihs", + "pj.abihc.nanohc", + "pj.abihc.nanoyk", + "pj.abihc.odiakustoy", + "pj.abihc.odustam", + "pj.abihc.ohsonhot", + "pj.abihc.okarihs", + "pj.abihc.okat", + "pj.abihc.okiba", + "pj.abihc.onihsaran", + "pj.abihc.osobimanim", + "pj.abihc.otasarihsimao", + "pj.abihc.otasimot", + "pj.abihc.ouhc", + "pj.abihc.oyihcay", + "pj.abihc.ukujno", + "pj.abihc.usayaru", + "pj.abihc.ustimik", + "pj.abihc.usttuf", + "pj.abihc.uzarasik", + "pj.agas", + "pj.agas.agas", + "pj.agas.amatamah", + "pj.agas.amayik", + "pj.agas.amihsak", + "pj.agas.arat", + "pj.agas.atagatik", + "pj.agas.atahatik", + "pj.agas.atira", + "pj.agas.atiraihsin", + "pj.agas.ekaira", + "pj.agas.enimimak", + "pj.agas.iakneg", + "pj.agas.igaruyk", + "pj.agas.igo", + "pj.agas.ihcamo", + "pj.agas.ihcuo", + "pj.agas.ihsiorihs", + "pj.agas.ikaznak", + "pj.agas.imodukuf", + "pj.agas.iragonihsoy", + "pj.agas.irami", + "pj.agas.nezih", + "pj.agas.ukat", + "pj.agas.ukohuok", + "pj.agas.usot", + "pj.agas.ustarak", + "pj.agihs", + "pj.agihs.akok", + "pj.agihs.amahagan", + "pj.agihs.amayirom", + "pj.agihs.amihsakat", + "pj.agihs.arabiam", + "pj.agihs.awagoton", + "pj.agihs.emiharot", + "pj.agihs.enokih", + "pj.agihs.houyr", + "pj.agihs.iazaihsin", + "pj.agihs.iesok", + "pj.agihs.ikustakat", + "pj.agihs.imoihsagih", + "pj.agihs.namihcahimo", + "pj.agihs.nanok", + "pj.agihs.ohsia", + "pj.agihs.omag", + "pj.agihs.otasoyot", + "pj.agihs.otok", + "pj.agihs.ottir", + "pj.agihs.usay", + "pj.agihs.ustasuk", + "pj.agihs.usto", + "pj.akaso", + "pj.akaso.adawihsik", + "pj.akaso.adeki", + "pj.akaso.akanoyot", + "pj.akaso.akasakaayahihc", + "pj.akaso.akasoihsagih", + "pj.akaso.akoadat", + "pj.akaso.akuziak", + "pj.akaso.amayas", + "pj.akaso.amayasakaso", + "pj.akaso.amodak", + "pj.akaso.arabustam", + "pj.akaso.arawihsak", + "pj.akaso.arediijuf", + "pj.akaso.atakarih", + "pj.akaso.atik", + "pj.akaso.atius", + "pj.akaso.awagayen", + "pj.akaso.awagodoyihsagih", + "pj.akaso.eson", + "pj.akaso.etawanojihs", + "pj.akaso.honim", + "pj.akaso.iakas", + "pj.akaso.ihcugirom", + "pj.akaso.ihsayabadnot", + "pj.akaso.ihsiakat", + "pj.akaso.ihsiat", + "pj.akaso.ihsin", + "pj.akaso.ihsoyimusihsagih", + "pj.akaso.ikarabi", + "pj.akaso.ikasim", + "pj.akaso.ikustakat", + "pj.akaso.imuzi", + "pj.akaso.irijat", + "pj.akaso.irotamuk", + "pj.akaso.nanak", + "pj.akaso.nannah", + "pj.akaso.nannes", + "pj.akaso.oay", + "pj.akaso.onaganihcawak", + "pj.akaso.onasimuzi", + "pj.akaso.onatak", + "pj.akaso.oneba", + "pj.akaso.onikibah", + "pj.akaso.onoyot", + "pj.akaso.otanim", + "pj.akaso.otiad", + "pj.akaso.otomamihs", + "pj.akaso.ouhc", + "pj.akaso.ustoimuzi", + "pj.akaso.usttes", + "pj.akoukuf", + "pj.akoukuf.adamay", + "pj.akoukuf.adeos", + "pj.akoukuf.agno", + "pj.akoukuf.agok", + "pj.akoukuf.agusak", + "pj.akoukuf.ahiku", + "pj.akoukuf.akawayim", + "pj.akoukuf.akuzii", + "pj.akoukuf.amakan", + "pj.akoukuf.amayasih", + "pj.akoukuf.amayim", + "pj.akoukuf.arawak", + "pj.akoukuf.atagon", + "pj.akoukuf.atakah", + "pj.akoukuf.atakanum", + "pj.akoukuf.atakat", + "pj.akoukuf.atumo", + "pj.akoukuf.awagakan", + "pj.akoukuf.awaganay", + "pj.akoukuf.awagat", + "pj.akoukuf.awagias", + "pj.akoukuf.awako", + "pj.akoukuf.awakorih", + "pj.akoukuf.ayihsa", + "pj.akoukuf.ayusak", + "pj.akoukuf.emay", + "pj.akoukuf.emuruk", + "pj.akoukuf.etaruk", + "pj.akoukuf.eus", + "pj.akoukuf.ianohs", + "pj.akoukuf.iaraihcat", + "pj.akoukuf.igoruk", + "pj.akoukuf.ihcukuf", + "pj.akoukuf.ihsagih", + "pj.akoukuf.ihsahukuy", + "pj.akoukuf.ihsin", + "pj.akoukuf.ikagako", + "pj.akoukuf.ikamuzim", + "pj.akoukuf.ikiust", + "pj.akoukuf.iko", + "pj.akoukuf.ikustani", + "pj.akoukuf.imanim", + "pj.akoukuf.imotihsoynihs", + "pj.akoukuf.imu", + "pj.akoukuf.irogo", + "pj.akoukuf.irugasas", + "pj.akoukuf.iusu", + "pj.akoukuf.nesiek", + "pj.akoukuf.nezub", + "pj.akoukuf.nezukihc", + "pj.akoukuf.ogukihc", + "pj.akoukuf.ohak", + "pj.akoukuf.ohot", + "pj.akoukuf.ohukihc", + "pj.akoukuf.ojono", + "pj.akoukuf.ojukihc", + "pj.akoukuf.okayim", + "pj.akoukuf.onihsukihc", + "pj.akoukuf.oto", + "pj.akoukuf.ouhc", + "pj.akoukuf.ufiazad", + "pj.akoukuf.ugnihs", + "pj.akoukuf.ustoyot", + "pj.akouzihs", + "pj.akouzihs.abmetog", + "pj.akouzihs.adamihs", + "pj.akouzihs.adeijuf", + "pj.akouzihs.adihsoy", + "pj.akouzihs.adomihs", + "pj.akouzihs.akouzihs", + "pj.akouzihs.amihsim", + "pj.akouzihs.arabiah", + "pj.akouzihs.arahonikam", + "pj.akouzihs.atawi", + "pj.akouzihs.awagekak", + "pj.akouzihs.awagukik", + "pj.akouzihs.awakijuf", + "pj.akouzihs.ayimonijuf", + "pj.akouzihs.iara", + "pj.akouzihs.iasok", + "pj.akouzihs.ihcamirom", + "pj.akouzihs.ijuf", + "pj.akouzihs.ikazeamo", + "pj.akouzihs.ikazustam", + "pj.akouzihs.imannak", + "pj.akouzihs.imata", + "pj.akouzihs.inukonuzi", + "pj.akouzihs.iorukuf", + "pj.akouzihs.nohenawak", + "pj.akouzihs.onosus", + "pj.akouzihs.oti", + "pj.akouzihs.ustamamah", + "pj.akouzihs.uzamun", + "pj.akouzihs.uzawak", + "pj.akouzihs.uzi", + "pj.akouzihs.uziay", + "pj.akouzihs.uziihsagih", + "pj.akouzihs.uziihsin", + "pj.akouzihs.uziimanim", + "pj.akouzihs.uzimihs", + "pj.amatias", + "pj.amatias.adihsoy", + "pj.amatias.adot", + "pj.amatias.adusah", + "pj.amatias.akadih", + "pj.amatias.akasa", + "pj.amatias.akoarihs", + "pj.amatias.akos", + "pj.amatias.amatias", + "pj.amatias.amayas", + "pj.amatias.amayorom", + "pj.amatias.amayotah", + "pj.amatias.amayustamihsagih", + "pj.amatias.amihsagurust", + "pj.amatias.amijawak", + "pj.amatias.amuri", + "pj.amatias.ani", + "pj.amatias.awageko", + "pj.amatias.awageman", + "pj.amatias.awagikot", + "pj.amatias.awago", + "pj.amatias.awakara", + "pj.amatias.awakihsoy", + "pj.amatias.awakimak", + "pj.amatias.awaru", + "pj.amatias.awazorokot", + "pj.amatias.ayagamuk", + "pj.amatias.ayagihsok", + "pj.amatias.ayagotah", + "pj.amatias.ayakuf", + "pj.amatias.ayimo", + "pj.amatias.aziin", + "pj.amatias.ebakusak", + "pj.amatias.eogawak", + "pj.amatias.esogo", + "pj.amatias.ettas", + "pj.amatias.ezokoy", + "pj.amatias.ibaraw", + "pj.amatias.ihcugawak", + "pj.amatias.ihsoyim", + "pj.amatias.ihsubustam", + "pj.amatias.iiroy", + "pj.amatias.ikato", + "pj.amatias.ikihs", + "pj.amatias.ikuk", + "pj.amatias.ikustawi", + "pj.amatias.imakoyr", + "pj.amatias.imihsoy", + "pj.amatias.imijuf", + "pj.amatias.imuziimak", + "pj.amatias.naznar", + "pj.amatias.odakas", + "pj.amatias.oihsay", + "pj.amatias.ojnoh", + "pj.amatias.onago", + "pj.amatias.onanim", + "pj.amatias.onimijuf", + "pj.amatias.onnah", + "pj.amatias.onoy", + "pj.amatias.orihsayim", + "pj.amatias.orotagan", + "pj.amatias.otasim", + "pj.amatias.otasimak", + "pj.amatias.otigus", + "pj.amatias.otomatik", + "pj.amatias.ozak", + "pj.amatias.ubihcihc", + "pj.amatias.ubihcihcihsagih", + "pj.amatias.usonuok", + "pj.amatias.uynah", + "pj.amayakaw", + "pj.amayakaw.adira", + "pj.amayakaw.adnotimak", + "pj.amayakaw.akadih", + "pj.amayakaw.amaharihs", + "pj.amayakaw.amahim", + "pj.amayakaw.amayakaw", + "pj.amayakaw.amayatik", + "pj.amayakaw.amayoduk", + "pj.amayakaw.aruustakihcan", + "pj.amayakaw.aruy", + "pj.amayakaw.asauy", + "pj.amayakaw.awagadira", + "pj.amayakaw.awagazok", + "pj.amayakaw.awagorih", + "pj.amayakaw.awakonik", + "pj.amayakaw.ayok", + "pj.amayakaw.azok", + "pj.amayakaw.ebanat", + "pj.amayakaw.edawi", + "pj.amayakaw.igarustak", + "pj.amayakaw.ijiat", + "pj.amayakaw.imani", + "pj.amayakaw.naniak", + "pj.amayakaw.obog", + "pj.amayakaw.onimik", + "pj.amayakaw.otasim", + "pj.amayakaw.otomihsah", + "pj.amayakaw.otomihsuk", + "pj.amayakaw.ugnihs", + "pj.amayako", + "pj.amayako.ajos", + "pj.amayako.akoasak", + "pj.amayako.amayako", + "pj.amayako.amayust", + "pj.amayako.amihsayah", + "pj.amayako.arabi", + "pj.amayako.arukawaihsin", + "pj.amayako.awiaka", + "pj.amayako.awinam", + "pj.amayako.egakay", + "pj.amayako.ekaw", + "pj.amayako.igan", + "pj.amayako.ihcukasa", + "pj.amayako.ihcuotes", + "pj.amayako.ihsahakat", + "pj.amayako.ikasim", + "pj.amayako.ikihsaruk", + "pj.amayako.imiin", + "pj.amayako.nanemuk", + "pj.amayako.nezib", + "pj.amayako.ohsotas", + "pj.amayako.ojnihs", + "pj.amayako.onamat", + "pj.amayako.onimagak", + "pj.amayako.oohs", + "pj.amayako.ouhcibik", + "pj.amayot", + "pj.amayot.adamay", + "pj.amayot.agot", + "pj.amayot.akoakat", + "pj.amayot.amayetat", + "pj.amayot.amayot", + "pj.amayot.anahoj", + "pj.amayot.ariat", + "pj.amayot.awakiinakan", + "pj.amayot.awakireman", + "pj.amayot.ebayo", + "pj.amayot.eboruk", + "pj.amayot.ihasa", + "pj.amayot.ihciimak", + "pj.amayot.ihsahanuf", + "pj.amayot.ikuzanu", + "pj.amayot.imani", + "pj.amayot.imanot", + "pj.amayot.imih", + "pj.amayot.nezuyn", + "pj.amayot.otnan", + "pj.amayot.uhcuf", + "pj.amayot.ustimukuf", + "pj.amayot.uzimi", + "pj.amayot.uzou", + "pj.amihsogak", + "pj.amihsogak.amayuok", + "pj.amihsogak.amihsogak", + "pj.amihsogak.asi", + "pj.amihsogak.ayonak", + "pj.amihsogak.ebanawak", + "pj.amihsogak.enatakan", + "pj.amihsogak.enatimanim", + "pj.amihsogak.enuka", + "pj.amihsogak.etomoonihsin", + "pj.amihsogak.iadnesamustas", + "pj.amihsogak.ikazarukam", + "pj.amihsogak.ikoih", + "pj.amihsogak.imama", + "pj.amihsogak.imuzi", + "pj.amihsogak.iusuy", + "pj.amihsogak.nesi", + "pj.amihsogak.oknik", + "pj.amihsogak.oos", + "pj.amihsogak.otomustam", + "pj.amihsogak.uzimurat", + "pj.amihsorih", + "pj.amihsorih.akan", + "pj.amihsorih.akas", + "pj.amihsorih.amayukuf", + "pj.amihsorih.amihsorihihsagih", + "pj.amihsorih.amijate", + "pj.amihsorih.amijimakikaso", + "pj.amihsorih.arabohs", + "pj.amihsorih.arahekat", + "pj.amihsorih.arahim", + "pj.amihsorih.ares", + "pj.amihsorih.atiak", + "pj.amihsorih.awiad", + "pj.amihsorih.ekato", + "pj.amihsorih.eruk", + "pj.amihsorih.ihciakustah", + "pj.amihsorih.ihcimono", + "pj.amihsorih.ihcinihs", + "pj.amihsorih.ihsinares", + "pj.amihsorih.ihsoyim", + "pj.amihsorih.imanimasa", + "pj.amihsorih.iuk", + "pj.amihsorih.negokikesnij", + "pj.amihsorih.ognoh", + "pj.amihsorih.onamuk", + "pj.amihsorih.uhcuf", + "pj.amihsukot", + "pj.amihsukot.abihci", + "pj.amihsukot.amihsukot", + "pj.amihsukot.amihsustamok", + "pj.amihsukot.amim", + "pj.amihsukot.awagakan", + "pj.amihsukot.egihsustam", + "pj.amihsukot.igum", + "pj.amihsukot.ihcoganas", + "pj.amihsukot.ihsoyim", + "pj.amihsukot.ikijaw", + "pj.amihsukot.imanim", + "pj.amihsukot.imuzia", + "pj.amihsukot.iukihsihs", + "pj.amihsukot.nana", + "pj.amihsukot.naniak", + "pj.amihsukot.onati", + "pj.amihsukot.oturan", + "pj.amihsukuf", + "pj.amihsukuf.abatuf", + "pj.amihsukuf.amato", + "pj.amihsukuf.amayenak", + "pj.amihsukuf.amayirok", + "pj.amihsukuf.amihsim", + "pj.amihsukuf.amihsukuf", + "pj.amihsukuf.amos", + "pj.amihsukuf.amuko", + "pj.amihsukuf.araboihsatik", + "pj.amihsukuf.aruganat", + "pj.amihsukuf.atakatik", + "pj.amihsukuf.atamawak", + "pj.amihsukuf.atarih", + "pj.amihsukuf.awagakus", + "pj.amihsukuf.awagemas", + "pj.amihsukuf.awaguy", + "pj.amihsukuf.awakamat", + "pj.amihsukuf.awakarihs", + "pj.amihsukuf.awakasa", + "pj.amihsukuf.awakihsi", + "pj.amihsukuf.awanah", + "pj.amihsukuf.awohs", + "pj.amihsukuf.egnabuzia", + "pj.amihsukuf.eiman", + "pj.amihsukuf.etad", + "pj.amihsukuf.etatii", + "pj.amihsukuf.iadnab", + "pj.amihsukuf.ienet", + "pj.amihsukuf.ihsagih", + "pj.amihsukuf.ihsiimagak", + "pj.amihsukuf.ikawi", + "pj.amihsukuf.ikazimuzi", + "pj.amihsukuf.ikubay", + "pj.amihsukuf.iminuk", + "pj.amihsukuf.irook", + "pj.amihsukuf.irustamay", + "pj.amihsukuf.nihsiat", + "pj.amihsukuf.ogetomo", + "pj.amihsukuf.ogihsin", + "pj.amihsukuf.ognan", + "pj.amihsukuf.ogomihs", + "pj.amihsukuf.ono", + "pj.amihsukuf.onoduruf", + "pj.amihsukuf.onorih", + "pj.amihsukuf.orihsawani", + "pj.amihsukuf.otamay", + "pj.amihsukuf.otasimuzia", + "pj.amihsukuf.urahim", + "pj.amihsukuf.ustamakawuzia", + "pj.amihsukuf.uziaihsin", + "pj.amihsukuf.uzianay", + "pj.amnug", + "pj.amnug.abawak", + "pj.amnug.adoyihc", + "pj.amnug.akanna", + "pj.amnug.akoihsoy", + "pj.amnug.akoijuf", + "pj.amnug.akoimot", + "pj.amnug.amayakat", + "pj.amnug.amustagaihsagih", + "pj.amnug.anihsatak", + "pj.amnug.annak", + "pj.amnug.arahonagan", + "pj.amnug.arnak", + "pj.amnug.aro", + "pj.amnug.arukati", + "pj.amnug.arumamat", + "pj.amnug.atamun", + "pj.amnug.atinomihs", + "pj.amnug.ato", + "pj.amnug.awakubihs", + "pj.amnug.awiem", + "pj.amnug.awohs", + "pj.amnug.ihsabeam", + "pj.amnug.ihsayabetat", + "pj.amnug.ikasakat", + "pj.amnug.ikasesi", + "pj.amnug.imakanim", + "pj.amnug.imuzio", + "pj.amnug.iogamust", + "pj.amnug.irodim", + "pj.amnug.ojonakan", + "pj.amnug.oneu", + "pj.amnug.onoyikust", + "pj.amnug.otnihs", + "pj.amnug.ukomnan", + "pj.amnug.ustasuk", + "pj.amnug.uyrik", + "pj.aran", + "pj.aran.abihsak", + "pj.aran.adakatotamay", + "pj.aran.adu", + "pj.aran.aduo", + "pj.aran.aguraki", + "pj.aran.amayatikimak", + "pj.aran.amayatikomihs", + "pj.aran.amayirokotamay", + "pj.aran.amoki", + "pj.aran.arahihsak", + "pj.aran.aran", + "pj.aran.awageson", + "pj.aran.awaknet", + "pj.aran.ekayim", + "pj.aran.eozamay", + "pj.aran.esog", + "pj.aran.eustim", + "pj.aran.iarukas", + "pj.aran.iawak", + "pj.aran.igarustak", + "pj.aran.ihciomihs", + "pj.aran.ihsinawak", + "pj.aran.ijo", + "pj.aran.ikamnak", + "pj.aran.ikatoruk", + "pj.aran.imakawak", + "pj.aran.inos", + "pj.aran.irnet", + "pj.aran.irotakat", + "pj.aran.irugeh", + "pj.aran.odna", + "pj.aran.odoyo", + "pj.aran.ognas", + "pj.aran.ojnihs", + "pj.aran.onihsoy", + "pj.aran.onihsoyihsagih", + "pj.aran.otomarawat", + "pj.aran.oyrok", + "pj.atagamay", + "pj.atagamay.adihsio", + "pj.atagamay.akatarihs", + "pj.atagamay.akourust", + "pj.atagamay.amayakan", + "pj.atagamay.amayarum", + "pj.atagamay.amayenak", + "pj.atagamay.amayonimak", + "pj.atagamay.arukho", + "pj.atagamay.atagamay", + "pj.atagamay.ataganuf", + "pj.atagamay.atahakat", + "pj.atagamay.atakas", + "pj.atagamay.awagekas", + "pj.atagamay.awagorumam", + "pj.atagamay.awakihsin", + "pj.atagamay.awakim", + "pj.atagamay.awazanabo", + "pj.atagamay.awazenoy", + "pj.atagamay.awazot", + "pj.atagamay.azuy", + "pj.atagamay.eagas", + "pj.atagamay.ebonamay", + "pj.atagamay.edii", + "pj.atagamay.enihsagih", + "pj.atagamay.eo", + "pj.atagamay.iagan", + "pj.atagamay.ianohs", + "pj.atagamay.ihasa", + "pj.atagamay.ihsinawak", + "pj.atagamay.inugo", + "pj.atagamay.odnet", + "pj.atagamay.ojnihs", + "pj.atagamay.oynan", + "pj.atagamay.ukohak", + "pj.atagiin", + "pj.atagiin.aga", + "pj.atagiin.akium", + "pj.atagiin.akoagan", + "pj.atagiin.amunou", + "pj.atagiin.amunouimanim", + "pj.atagiin.atabihs", + "pj.atagiin.atagiin", + "pj.atagiin.atioy", + "pj.atagiin.awagioti", + "pj.atagiin.awakikes", + "pj.atagiin.awazuy", + "pj.atagiin.awirak", + "pj.atagiin.ayijo", + "pj.atagiin.ekustim", + "pj.atagiin.emabust", + "pj.atagiin.ianiat", + "pj.atagiin.ihcamakot", + "pj.atagiin.ikazawihsak", + "pj.atagiin.ikazomuzi", + "pj.atagiin.imagat", + "pj.atagiin.imakarum", + "pj.atagiin.imo", + "pj.atagiin.nanust", + "pj.atagiin.nesog", + "pj.atagiin.odas", + "pj.atagiin.oihcot", + "pj.atagiin.ojnas", + "pj.atagiin.okihay", + "pj.atagiin.okoym", + "pj.atagiin.omak", + "pj.atagiin.onaga", + "pj.atagiin.ories", + "pj.atagiin.uories", + "pj.atagiin.usteoj", + "pj.atika", + "pj.atika.ago", + "pj.atika.akasok", + "pj.atika.akoimak", + "pj.atika.atago", + "pj.atika.atagorihcah", + "pj.atika.atika", + "pj.atika.atikaatik", + "pj.atika.awaki", + "pj.atika.awoyk", + "pj.atika.emojog", + "pj.atika.enatim", + "pj.atika.esuranihsagih", + "pj.atika.etado", + "pj.atika.etokoy", + "pj.atika.ihsoyirom", + "pj.atika.imagatak", + "pj.atika.inaokimak", + "pj.atika.nesiad", + "pj.atika.ohakin", + "pj.atika.ojnoh", + "pj.atika.ojnohiruy", + "pj.atika.onuzak", + "pj.atika.orihson", + "pj.atika.otasijuf", + "pj.atika.otasim", + "pj.atika.oyjnoh", + "pj.atika.ukobmes", + "pj.atika.uoppah", + "pj.atio", + "pj.atio.adakatognub", + "pj.atio.amasah", + "pj.atio.amihsemih", + "pj.atio.asu", + "pj.atio.atekat", + "pj.atio.atih", + "pj.atio.atio", + "pj.atio.eonokok", + "pj.atio.eustimak", + "pj.atio.ijih", + "pj.atio.ikasinuk", + "pj.atio.ikias", + "pj.atio.ikusu", + "pj.atio.imukust", + "pj.atio.onoognub", + "pj.atio.ufuy", + "pj.atio.ujuk", + "pj.atio.uppeb", + "pj.atio.usuk", + "pj.awagak", + "pj.awagak.amihsoan", + "pj.awagak.arihotok", + "pj.awagak.awagakihsagih", + "pj.awagak.awagaya", + "pj.awagak.emaguram", + "pj.awagak.ijnonak", + "pj.awagak.ijustnez", + "pj.awagak.ikunas", + "pj.awagak.imonihcu", + "pj.awagak.ohsonot", + "pj.awagak.onnam", + "pj.awagak.oyotim", + "pj.awagak.ustamakat", + "pj.awagak.ustodat", + "pj.awagak.uzatu", + "pj.awaganak", + "pj.awaganak.adustam", + "pj.awaganak.akusokoy", + "pj.awaganak.akustarih", + "pj.awaganak.amaz", + "pj.awaganak.anibe", + "pj.awaganak.aragihsaimanim", + "pj.awaganak.arahesi", + "pj.awaganak.arahimagas", + "pj.awaganak.arawado", + "pj.awaganak.arawaguy", + "pj.awaganak.aruim", + "pj.awaganak.arukamak", + "pj.awaganak.atikamay", + "pj.awaganak.awakia", + "pj.awaganak.awakoyik", + "pj.awaganak.awakumas", + "pj.awaganak.awasijuf", + "pj.awaganak.ayimonin", + "pj.awaganak.enokah", + "pj.awaganak.esaya", + "pj.awaganak.iakan", + "pj.awaganak.iesiak", + "pj.awaganak.igusta", + "pj.awaganak.ihsuz", + "pj.awaganak.ikasagihc", + "pj.awaganak.io", + "pj.awaganak.iukust", + "pj.awaganak.onadah", + "pj.awaganak.osio", + "pj.awaganak.otamay", + "pj.awakihsi", + "pj.awakihsi.adanihcu", + "pj.awakihsi.agak", + "pj.awakihsi.akihs", + "pj.awakihsi.amijaw", + "pj.awakihsi.atabust", + "pj.awakihsi.atikawak", + "pj.awakihsi.awazanak", + "pj.awakihsi.igurust", + "pj.awakihsi.ihcionon", + "pj.awakihsi.imon", + "pj.awakihsi.iukah", + "pj.awakihsi.nasukah", + "pj.awakihsi.oanan", + "pj.awakihsi.oton", + "pj.awakihsi.otonakan", + "pj.awakihsi.ukohak", + "pj.awakihsi.ustamok", + "pj.awakihsi.uzimana", + "pj.awakihsi.uzus", + "pj.awaniko", + "pj.awaniko.ahan", + "pj.awaniko.amarat", + "pj.awaniko.amijemuk", + "pj.awaniko.amuru", + "pj.awaniko.anedak", + "pj.awaniko.anezi", + "pj.awaniko.anno", + "pj.awaniko.arahihsin", + "pj.awaniko.ararih", + "pj.awaniko.awakihsi", + "pj.awaniko.awaniko", + "pj.awaniko.ayehi", + "pj.awaniko.azonig", + "pj.awaniko.eosaru", + "pj.awaniko.eseay", + "pj.awaniko.ihsagih", + "pj.awaniko.ijomihs", + "pj.awaniko.ikagihsi", + "pj.awaniko.ikanot", + "pj.awaniko.ikihsakot", + "pj.awaniko.imaginuk", + "pj.awaniko.imakihsug", + "pj.awaniko.imamaz", + "pj.awaniko.imigo", + "pj.awaniko.imotekat", + "pj.awaniko.inuga", + "pj.awaniko.inuganoy", + "pj.awaniko.namoti", + "pj.awaniko.natimoy", + "pj.awaniko.nawonig", + "pj.awaniko.nijikan", + "pj.awaniko.nik", + "pj.awaniko.ogan", + "pj.awaniko.ojnan", + "pj.awaniko.otiadatik", + "pj.awaniko.otiadimanim", + "pj.awaniko.ubotom", + "pj.awaniko.ukusugakan", + "pj.awaniko.ukusugakanatik", + "pj.awaniko.ukusugimot", + "pj.awaniko.urabanoy", + "pj.awaniko.urabeah", + "pj.ca", + "pj.da", + "pj.de", + "pj.eim", + "pj.eim.abot", + "pj.eim.akasustam", + "pj.eim.akuzus", + "pj.eim.amahim", + "pj.eim.amayemak", + "pj.eim.amayim", + "pj.eim.amihs", + "pj.eim.anawuk", + "pj.eim.awiem", + "pj.eim.awik", + "pj.eim.ebani", + "pj.eim.eogawak", + "pj.eim.esi", + "pj.eim.esiimanim", + "pj.eim.iarataw", + "pj.eim.igusim", + "pj.eim.ihasa", + "pj.eim.ihciakkoy", + "pj.eim.ikamat", + "pj.eim.ikasosik", + "pj.eim.ikat", + "pj.eim.ikiat", + "pj.eim.iraban", + "pj.eim.odat", + "pj.eim.ohik", + "pj.eim.onamuk", + "pj.eim.onihseru", + "pj.eim.onodu", + "pj.eim.onomok", + "pj.eim.ust", + "pj.emihe", + "pj.emihe.amahataway", + "pj.emihe.amahiin", + "pj.emihe.amayustam", + "pj.emihe.amijawu", + "pj.emihe.amijimak", + "pj.emihe.ataki", + "pj.emihe.atakiman", + "pj.emihe.ebot", + "pj.emihe.ianoh", + "pj.emihe.ikasam", + "pj.emihe.irabami", + "pj.emihe.nania", + "pj.emihe.negokamuk", + "pj.emihe.noot", + "pj.emihe.ojias", + "pj.emihe.okihcu", + "pj.emihe.onustam", + "pj.emihe.ouhcukokihs", + "pj.emihe.oyi", + "pj.emihe.oyies", + "pj.emihe.ukohik", + "pj.emihe.uzo", + "pj.en", + "pj.enamihs", + "pj.enamihs.adamah", + "pj.enamihs.adho", + "pj.enamihs.adusam", + "pj.enamihs.akustay", + "pj.enamihs.ama", + "pj.enamihs.amihsonihsin", + "pj.enamihs.amihsoniko", + "pj.enamihs.awakih", + "pj.enamihs.enamihs", + "pj.enamihs.eustam", + "pj.enamihs.igaka", + "pj.enamihs.igusay", + "pj.enamihs.ikonikak", + "pj.enamihs.imikih", + "pj.enamihs.nannu", + "pj.enamihs.omukay", + "pj.enamihs.omuzi", + "pj.enamihs.omuziihsagih", + "pj.enamihs.omuziuko", + "pj.enamihs.onawust", + "pj.enamihs.otasim", + "pj.enamihs.ustog", + "pj.enamihs.uyamat", + "pj.etawi", + "pj.etawi.abahay", + "pj.etawi.adamay", + "pj.etawi.adon", + "pj.etawi.akoirom", + "pj.etawi.atahonat", + "pj.etawi.atakatnezukir", + "pj.etawi.atimus", + "pj.etawi.awasijuf", + "pj.etawi.awasuzim", + "pj.etawi.awihs", + "pj.etawi.ehonihci", + "pj.etawi.ehonin", + "pj.etawi.ehonuk", + "pj.etawi.etawi", + "pj.etawi.iaduf", + "pj.etawi.iamurak", + "pj.etawi.iawak", + "pj.etawi.ihcusto", + "pj.etawi.ihsiamak", + "pj.etawi.ihsiukuzihs", + "pj.etawi.ijoboj", + "pj.etawi.ijuk", + "pj.etawi.ikamanah", + "pj.etawi.ikamuzuk", + "pj.etawi.ikasagenak", + "pj.etawi.ikesonihci", + "pj.etawi.imakatik", + "pj.etawi.imuziarih", + "pj.etawi.imuziawi", + "pj.etawi.okayim", + "pj.etawi.onorih", + "pj.etawi.onot", + "pj.etawi.otanufo", + "pj.etawi.uhso", + "pj.gl", + "pj.igayim", + "pj.igayim.adukak", + "pj.igayim.amagoihs", + "pj.igayim.amakihs", + "pj.igayim.amihsustam", + "pj.igayim.amihsustamihsagih", + "pj.igayim.amunawi", + "pj.igayim.amunnesek", + "pj.igayim.arawago", + "pj.igayim.ariho", + "pj.igayim.atabihs", + "pj.igayim.atarum", + "pj.igayim.awagano", + "pj.igayim.awakuruf", + "pj.igayim.awiat", + "pj.igayim.ayimot", + "pj.igayim.ayukaw", + "pj.igayim.emot", + "pj.igayim.enimes", + "pj.igayim.ihsiorihs", + "pj.igayim.ikamonihsi", + "pj.igayim.ikasawak", + "pj.igayim.ikaso", + "pj.igayim.imak", + "pj.igayim.irataw", + "pj.igayim.iromuram", + "pj.igayim.irotan", + "pj.igayim.oaz", + "pj.igayim.ojagat", + "pj.igayim.otasim", + "pj.igayim.otomamay", + "pj.igayim.ufir", + "pj.igayim.ukirnasimanim", + "pj.igayim.ukuhsakihcihs", + "pj.igihcot", + "pj.igihcot.agah", + "pj.igihcot.agakihsa", + "pj.igihcot.agust", + "pj.igihcot.akom", + "pj.igihcot.amayo", + "pj.igihcot.amayusarak", + "pj.igihcot.amunak", + "pj.igihcot.araboihsusan", + "pj.igihcot.arawatho", + "pj.igihcot.ariho", + "pj.igihcot.arukas", + "pj.igihcot.atakihsin", + "pj.igihcot.atiay", + "pj.igihcot.awakonimak", + "pj.igihcot.awazenakat", + "pj.igihcot.ayimonustu", + "pj.igihcot.ayoihs", + "pj.igihcot.eiiju", + "pj.igihcot.ekustomihs", + "pj.igihcot.enufawi", + "pj.igihcot.iakihci", + "pj.igihcot.igetom", + "pj.igihcot.igihcot", + "pj.igihcot.igon", + "pj.igihcot.okihsam", + "pj.igihcot.okkin", + "pj.igihcot.onas", + "pj.igihcot.osioruk", + "pj.igihcot.otab", + "pj.igihcot.ubim", + "pj.igihcot.usan", + "pj.ihcia", + "pj.ihcia.adnah", + "pj.ihcia.ama", + "pj.ihcia.amahakat", + "pj.ihcia.amahim", + "pj.ihcia.amayuni", + "pj.ihcia.amihsibot", + "pj.ihcia.amihsust", + "pj.ihcia.arahat", + "pj.ihcia.aratihs", + "pj.ihcia.arik", + "pj.ihcia.aruihsagih", + "pj.ihcia.arukawi", + "pj.ihcia.atihc", + "pj.ihcia.atok", + "pj.ihcia.atoyot", + "pj.ihcia.awakoyot", + "pj.ihcia.awazani", + "pj.ihcia.ayimonihci", + "pj.ihcia.ayirak", + "pj.ihcia.einak", + "pj.ihcia.ekaoyot", + "pj.ihcia.ekusa", + "pj.ihcia.emanokot", + "pj.ihcia.enoyot", + "pj.ihcia.iagusak", + "pj.ihcia.iakot", + "pj.ihcia.iasia", + "pj.ihcia.ieot", + "pj.ihcia.ihasairawo", + "pj.ihcia.ihcugo", + "pj.ihcia.ihsahoyot", + "pj.ihcia.ihsoyim", + "pj.ihcia.ikamok", + "pj.ihcia.ikazako", + "pj.ihcia.ikihssi", + "pj.ihcia.imotay", + "pj.ihcia.irogamag", + "pj.ihcia.nanikeh", + "pj.ihcia.nanok", + "pj.ihcia.nihssin", + "pj.ihcia.ogot", + "pj.ihcia.oihsin", + "pj.ihcia.ojna", + "pj.ihcia.orihsnihs", + "pj.ihcia.osuf", + "pj.ihcia.otes", + "pj.ihcia.ubo", + "pj.ihcia.uraho", + "pj.ihcia.usoyik", + "pj.ihcia.ustakihs", + "pj.ihcia.uyrihc", + "pj.ihcia.uzah", + "pj.ihcok", + "pj.ihcok.adusay", + "pj.ihcok.akadih", + "pj.ihcok.amayotom", + "pj.ihcok.arahim", + "pj.ihcok.arahusuy", + "pj.ihcok.arumakan", + "pj.ihcok.asot", + "pj.ihcok.asotihsin", + "pj.ihcok.awagatik", + "pj.ihcok.awagodoyin", + "pj.ihcok.awakas", + "pj.ihcok.awako", + "pj.ihcok.iesieg", + "pj.ihcok.ihco", + "pj.ihcok.ihcok", + "pj.ihcok.ijamu", + "pj.ihcok.ika", + "pj.ihcok.ikasus", + "pj.ihcok.ikusto", + "pj.ihcok.imagak", + "pj.ihcok.imak", + "pj.ihcok.irahan", + "pj.ihcok.omukus", + "pj.ihcok.oni", + "pj.ihcok.onust", + "pj.ihcok.onustihsagih", + "pj.ihcok.otorum", + "pj.ihcok.oyot", + "pj.ihcok.oyoto", + "pj.ihcok.ukoknan", + "pj.ihcok.uzimihsasot", + "pj.ihcugamay", + "pj.ihcugamay.amayukot", + "pj.ihcugamay.amihso", + "pj.ihcugamay.atoyot", + "pj.ihcugamay.ebu", + "pj.ihcugamay.esubat", + "pj.ihcugamay.igah", + "pj.ihcugamay.ikesonomihs", + "pj.ihcugamay.inukawi", + "pj.ihcugamay.irakih", + "pj.ihcugamay.nanuhs", + "pj.ihcugamay.otagan", + "pj.ihcugamay.uba", + "pj.ihcugamay.ufoh", + "pj.ihcugamay.uotim", + "pj.ihcugamay.ustamaduk", + "pj.ihcugamay.uuy", + "pj.ihsanamay", + "pj.ihsanamay.adihsoyijuf", + "pj.ihsanamay.amayabat", + "pj.ihsanamay.arahoneu", + "pj.ihsanamay.arustakihsin", + "pj.ihsanamay.awakayah", + "pj.ihsanamay.awakijuf", + "pj.ihsanamay.awasuran", + "pj.ihsanamay.awohs", + "pj.ihsanamay.egusok", + "pj.ihsanamay.iak", + "pj.ihsanamay.ihcimakan", + "pj.ihsanamay.ihsanamay", + "pj.ihsanamay.ihsod", + "pj.ihsanamay.ikasarin", + "pj.ihsanamay.ikufeuf", + "pj.ihsanamay.ikusto", + "pj.ihsanamay.okakanamay", + "pj.ihsanamay.okihcugawakijuf", + "pj.ihsanamay.onihso", + "pj.ihsanamay.otasimawakihci", + "pj.ihsanamay.otukoh", + "pj.ihsanamay.ouhc", + "pj.ihsanamay.spla-imanim", + "pj.ihsanamay.ubnan", + "pj.ihsanamay.ubonim", + "pj.ihsanamay.ufok", + "pj.ihsanamay.uhsok", + "pj.ihsanamay.urust", + "pj.ikarabi", + "pj.ikarabi.abukust", + "pj.ikarabi.agok", + "pj.ikarabi.akan", + "pj.ikarabi.akanihcatih", + "pj.ikarabi.amasak", + "pj.ikarabi.amatimo", + "pj.ikarabi.amawi", + "pj.ikarabi.amihsak", + "pj.ikarabi.amustomihs", + "pj.ikarabi.ani", + "pj.ikarabi.arahihcu", + "pj.ikarabi.araway", + "pj.ikarabi.aruagimusak", + "pj.ikarabi.aruihcust", + "pj.ikarabi.atagamay", + "pj.ikarabi.atageman", + "pj.ikarabi.atoihcatih", + "pj.ikarabi.awagarukas", + "pj.ikarabi.awago", + "pj.ikarabi.awos", + "pj.ikarabi.ayimoihcatih", + "pj.ikarabi.ayirom", + "pj.ikarabi.ebomot", + "pj.ikarabi.edirot", + "pj.ikarabi.enot", + "pj.ikarabi.etadomihs", + "pj.ikarabi.iakas", + "pj.ikarabi.iakot", + "pj.ikarabi.iarao", + "pj.ikarabi.iesukihc", + "pj.ikarabi.igahakat", + "pj.ikarabi.ihasa", + "pj.ikarabi.ihcatih", + "pj.ikarabi.ikarabi", + "pj.ikarabi.ikasaguyr", + "pj.ikarabi.ikihsani", + "pj.ikarabi.ikuy", + "pj.ikarabi.ima", + "pj.ikarabi.irukustamat", + "pj.ikarabi.odnab", + "pj.ikarabi.ogiad", + "pj.ikarabi.ohim", + "pj.ikarabi.okati", + "pj.ikarabi.orihsijuf", + "pj.ikarabi.osoj", + "pj.ikarabi.otasorihs", + "pj.ikarabi.otim", + "pj.ikarabi.oyihcay", + "pj.ikarabi.ufius", + "pj.ikarabi.ukihsu", + "pj.ikarabi.usimak", + "pj.ikasagan", + "pj.ikasagan.amabo", + "pj.ikasagan.amihsust", + "pj.ikasagan.anatawak", + "pj.ikasagan.arabamihs", + "pj.ikasagan.arumo", + "pj.ikasagan.aruustam", + "pj.ikasagan.awijihc", + "pj.ikasagan.ayahasi", + "pj.ikasagan.iakias", + "pj.ikasagan.ihies", + "pj.ikasagan.ikasagan", + "pj.ikasagan.iki", + "pj.ikasagan.imasah", + "pj.ikasagan.neznu", + "pj.ikasagan.obesas", + "pj.ikasagan.odarih", + "pj.ikasagan.oteso", + "pj.ikasagan.otog", + "pj.ikasagan.otogimaknihs", + "pj.ikasagan.ustigot", + "pj.ikasagan.ustonihcuk", + "pj.ikasagan.ustuf", + "pj.ikazayim", + "pj.ikazayim.abiihs", + "pj.ikazayim.aguyh", + "pj.ikazayim.akoebon", + "pj.ikazayim.akustorom", + "pj.ikazayim.amihsuk", + "pj.ikazayim.aremihsin", + "pj.ikazayim.aruatik", + "pj.ikazayim.atakatik", + "pj.ikazayim.atamim", + "pj.ikazayim.awagatik", + "pj.ikazayim.awagodak", + "pj.ikazayim.aya", + "pj.ikazayim.ebanakat", + "pj.ikazayim.esakog", + "pj.ikazayim.ihsayabok", + "pj.ikazayim.ikazakat", + "pj.ikazayim.ikazayim", + "pj.ikazayim.imanimawak", + "pj.ikazayim.imotinuk", + "pj.ikazayim.imotnihs", + "pj.ikazayim.nanihcin", + "pj.ikazayim.ojik", + "pj.ikazayim.ojonokayim", + "pj.ikazayim.onibe", + "pj.ikazayim.onust", + "pj.ikazayim.otias", + "pj.ikazayim.urahakat", + "pj.iromoa", + "pj.iromoa.adawot", + "pj.iromoa.aturust", + "pj.iromoa.awasim", + "pj.iromoa.ehonihcah", + "pj.iromoa.ehonihcihs", + "pj.iromoa.ehonnas", + "pj.iromoa.ehonog", + "pj.iromoa.ehonukor", + "pj.iromoa.esario", + "pj.iromoa.ianarih", + "pj.iromoa.iganayati", + "pj.iromoa.ihsioruk", + "pj.iromoa.ijehon", + "pj.iromoa.ikasorih", + "pj.iromoa.imakihsah", + "pj.iromoa.inawo", + "pj.iromoa.iramodakan", + "pj.iromoa.iromoa", + "pj.iromoa.ognihs", + "pj.iromoa.okkat", + "pj.iromoa.uragust", + "pj.iromoa.ustum", + "pj.irottot", + "pj.irottot.arahawak", + "pj.irottot.aruotok", + "pj.irottot.asakaw", + "pj.irottot.asasim", + "pj.irottot.egok", + "pj.irottot.irottot", + "pj.irottot.nanihcin", + "pj.irottot.oganoy", + "pj.irottot.onih", + "pj.irottot.otanimiakas", + "pj.irottot.ubnan", + "pj.irottot.uzay", + "pj.irottot.uzihc", + "pj.iukuf", + "pj.iukuf.adeki", + "pj.iukuf.agurust", + "pj.iukuf.amabo", + "pj.iukuf.amahakat", + "pj.iukuf.amahim", + "pj.iukuf.amayustak", + "pj.iukuf.asakaw", + "pj.iukuf.eabas", + "pj.iukuf.iakas", + "pj.iukuf.iho", + "pj.iukuf.ijiehie", + "pj.iukuf.iukuf", + "pj.iukuf.nezihce", + "pj.iukuf.nezihceimanim", + "pj.iukuf.ono", + "pj.oc", + "pj.odiakkoh", + "pj.odiakkoh.adeki", + "pj.odiakkoh.agakihset", + "pj.odiakkoh.ahcebihs", + "pj.odiakkoh.akadih", + "pj.odiakkoh.akufib", + "pj.odiakkoh.akunarihs", + "pj.odiakkoh.amayiruk", + "pj.odiakkoh.amhot", + "pj.odiakkoh.amihsorihatik", + "pj.odiakkoh.amihsukuf", + "pj.odiakkoh.amoras", + "pj.odiakkoh.amusta", + "pj.odiakkoh.ariba", + "pj.odiakkoh.aribaka", + "pj.odiakkoh.aribo", + "pj.odiakkoh.ariburuf", + "pj.odiakkoh.arozo", + "pj.odiakkoh.arugakihsagih", + "pj.odiakkoh.aruoyot", + "pj.odiakkoh.asakim", + "pj.odiakkoh.atagikust", + "pj.odiakkoh.atamun", + "pj.odiakkoh.awagakan", + "pj.odiakkoh.awagakuf", + "pj.odiakkoh.awaganus", + "pj.odiakkoh.awaganusimak", + "pj.odiakkoh.awakaru", + "pj.odiakkoh.awakihasa", + "pj.odiakkoh.awakihsagih", + "pj.odiakkoh.awakikat", + "pj.odiakkoh.awakimak", + "pj.odiakkoh.awakomihs", + "pj.odiakkoh.awakum", + "pj.odiakkoh.awazimawi", + "pj.odiakkoh.awine", + "pj.odiakkoh.awoyk", + "pj.odiakkoh.ayot", + "pj.odiakkoh.eamustam", + "pj.odiakkoh.eanan", + "pj.odiakkoh.ebakihs", + "pj.odiakkoh.ebayak", + "pj.odiakkoh.ebonoroh", + "pj.odiakkoh.eboto", + "pj.odiakkoh.eian", + "pj.odiakkoh.ekihsam", + "pj.odiakkoh.ekufoto", + "pj.odiakkoh.enakami", + "pj.odiakkoh.eppoko", + "pj.odiakkoh.eppokoihsin", + "pj.odiakkoh.esotihc", + "pj.odiakkoh.etad", + "pj.odiakkoh.etadokah", + "pj.odiakkoh.euonikat", + "pj.odiakkoh.iabib", + "pj.odiakkoh.iamokamot", + "pj.odiakkoh.ianakkaw", + "pj.odiakkoh.ianakoroh", + "pj.odiakkoh.ianawi", + "pj.odiakkoh.ianeomak", + "pj.odiakkoh.ianihsatu", + "pj.odiakkoh.ianokik", + "pj.odiakkoh.ianustamoruk", + "pj.odiakkoh.ianustasakan", + "pj.odiakkoh.ieib", + "pj.odiakkoh.ihcioy", + "pj.odiakkoh.ihcubmek", + "pj.odiakkoh.ihcuirihs", + "pj.odiakkoh.ihsase", + "pj.odiakkoh.ihsekka", + "pj.odiakkoh.ihsoknar", + "pj.odiakkoh.ihsuesom", + "pj.odiakkoh.ijufirihsir", + "pj.odiakkoh.ikamamihs", + "pj.odiakkoh.ikiat", + "pj.odiakkoh.ikin", + "pj.odiakkoh.imatik", + "pj.odiakkoh.imotoyot", + "pj.odiakkoh.ioakihs", + "pj.odiakkoh.ioarihs", + "pj.odiakkoh.irahs", + "pj.odiakkoh.irakihsi", + "pj.odiakkoh.iramot", + "pj.odiakkoh.irihsaba", + "pj.odiakkoh.irihsir", + "pj.odiakkoh.irotarib", + "pj.odiakkoh.nahctuk", + "pj.odiakkoh.narorum", + "pj.odiakkoh.nase", + "pj.odiakkoh.natokahs", + "pj.odiakkoh.nuber", + "pj.odiakkoh.okayot", + "pj.odiakkoh.omire", + "pj.odiakkoh.omukay", + "pj.odiakkoh.onaruf", + "pj.odiakkoh.onarufimak", + "pj.odiakkoh.onarufimanim", + "pj.odiakkoh.oorih", + "pj.odiakkoh.orihibo", + "pj.odiakkoh.orihsuk", + "pj.odiakkoh.orobah", + "pj.odiakkoh.orohib", + "pj.odiakkoh.orohihsimak", + "pj.odiakkoh.orohsa", + "pj.odiakkoh.oropnan", + "pj.odiakkoh.oroyan", + "pj.odiakkoh.orumen", + "pj.odiakkoh.otasoyik", + "pj.odiakkoh.oteko", + "pj.odiakkoh.otukoh", + "pj.odiakkoh.ubassa", + "pj.odiakkoh.ukotnihs", + "pj.odiakkoh.umassaw", + "pj.odiakkoh.umuo", + "pj.odiakkoh.uppakiin", + "pj.odiakkoh.uppenioto", + "pj.odiakkoh.uppennuk", + "pj.odiakkoh.uppip", + "pj.odiakkoh.urato", + "pj.odiakkoh.usakat", + "pj.odiakkoh.ustebe", + "pj.odiakkoh.ustebia", + "pj.odiakkoh.ustebihs", + "pj.odiakkoh.ustebihsa", + "pj.odiakkoh.ustebirobon", + "pj.odiakkoh.ustebme", + "pj.odiakkoh.ustebmom", + "pj.odiakkoh.ustebmotakan", + "pj.odiakkoh.ustebnoh", + "pj.odiakkoh.ustebnotamah", + "pj.odiakkoh.ustebomik", + "pj.odiakkoh.ustebos", + "pj.odiakkoh.ustebot", + "pj.odiakkoh.ustebukir", + "pj.odiakkoh.ustebuppihc", + "pj.odiakkoh.ustebust", + "pj.odiakkoh.ustonihsnihs", + "pj.odiakkoh.ustufuras", + "pj.odiakkoh.usuaru", + "pj.odiakkoh.uyru", + "pj.odiakkoh.uyrukoh", + "pj.odiakkoh.uzimihs", + "pj.odiakkoh.uzimihsok", + "pj.og", + "pj.ogoyh", + "pj.ogoyh.abmat", + "pj.ogoyh.adnas", + "pj.ogoyh.agusak", + "pj.ogoyh.akat", + "pj.ogoyh.akooyot", + "pj.ogoyh.akoy", + "pj.ogoyh.akuzarakat", + "pj.ogoyh.amayasas", + "pj.ogoyh.amirah", + "pj.ogoyh.awagani", + "pj.ogoyh.awagokak", + "pj.ogoyh.awakihci", + "pj.ogoyh.awakimak", + "pj.ogoyh.awakoy", + "pj.ogoyh.ayihsa", + "pj.ogoyh.ayimonihsin", + "pj.ogoyh.iasak", + "pj.ogoyh.ihsaka", + "pj.ogoyh.ihsiat", + "pj.ogoyh.ihsinawak", + "pj.ogoyh.ijawa", + "pj.ogoyh.ijawaimanim", + "pj.ogoyh.ijemih", + "pj.ogoyh.ikagoa", + "pj.ogoyh.ikasagama", + "pj.ogoyh.ikasukuf", + "pj.ogoyh.ikawihsin", + "pj.ogoyh.ikihsog", + "pj.ogoyh.ikim", + "pj.ogoyh.imati", + "pj.ogoyh.ioia", + "pj.ogoyh.irogimak", + "pj.ogoyh.nannas", + "pj.ogoyh.nesnonihs", + "pj.ogoyh.ogasa", + "pj.ogoyh.ogasakat", + "pj.ogoyh.oka", + "pj.ogoyh.onikat", + "pj.ogoyh.ono", + "pj.ogoyh.onustat", + "pj.ogoyh.orihsay", + "pj.ogoyh.osihs", + "pj.ogoyh.otomus", + "pj.ogoyh.oyas", + "pj.ogoyh.ubay", + "pj.ogoyh.ugnihs", + "pj.onagan", + "pj.onagan.abukah", + "pj.onagan.adaw", + "pj.onagan.adayim", + "pj.onagan.adeki", + "pj.onagan.adeu", + "pj.onagan.adii", + "pj.onagan.akasay", + "pj.onagan.akasuki", + "pj.onagan.akazus", + "pj.onagan.akihsoo", + "pj.onagan.akousay", + "pj.onagan.amayakat", + "pj.onagan.amayii", + "pj.onagan.amihsukufosik", + "pj.onagan.amijii", + "pj.onagan.amukihc", + "pj.onagan.ani", + "pj.onagan.anihsetat", + "pj.onagan.anuzii", + "pj.onagan.arah", + "pj.onagan.arugot", + "pj.onagan.asaim", + "pj.onagan.atagamay", + "pj.onagan.atoyim", + "pj.onagan.awagakan", + "pj.onagan.awagan", + "pj.onagan.awago", + "pj.onagan.awakustam", + "pj.onagan.awaziurak", + "pj.onagan.awonim", + "pj.onagan.awonimimanim", + "pj.onagan.awukoo", + "pj.onagan.awus", + "pj.onagan.awusomihs", + "pj.onagan.ayako", + "pj.onagan.ayarih", + "pj.onagan.eakas", + "pj.onagan.enagamok", + "pj.onagan.esubo", + "pj.onagan.igakat", + "pj.onagan.ihasa", + "pj.onagan.ihca", + "pj.onagan.ihcamo", + "pj.onagan.ihcamonanihs", + "pj.onagan.ihcuonamay", + "pj.onagan.ihsukagot", + "pj.onagan.ikakas", + "pj.onagan.ikamimanim", + "pj.onagan.ikato", + "pj.onagan.ikiaatik", + "pj.onagan.ikiaimanim", + "pj.onagan.ikoa", + "pj.onagan.ikuzihcom", + "pj.onagan.imakawak", + "pj.onagan.imijuf", + "pj.onagan.imo", + "pj.onagan.imot", + "pj.onagan.irato", + "pj.onagan.irijoihs", + "pj.onagan.iromakat", + "pj.onagan.nana", + "pj.onagan.nesnoawazon", + "pj.onagan.ohukas", + "pj.onagan.onagan", + "pj.onagan.onakan", + "pj.onagan.onihc", + "pj.onagan.onimuza", + "pj.onagan.onustat", + "pj.onagan.oromok", + "pj.onagan.osigan", + "pj.onagan.osik", + "pj.onagan.otomustam", + "pj.onagan.ukas", + "pj.onagan.ukohukihc", + "pj.onagan.ustamega", + "pj.otomamuk", + "pj.otomamuk.agamay", + "pj.otomamuk.amihsak", + "pj.otomamuk.arahihsin", + "pj.otomamuk.asok", + "pj.otomamuk.asukama", + "pj.otomamuk.asukamaimak", + "pj.otomamuk.atamanim", + "pj.otomamuk.enufim", + "pj.otomamuk.ihcukik", + "pj.otomamuk.ihsoyotih", + "pj.otomamuk.ikihsam", + "pj.otomamuk.iku", + "pj.otomamuk.inugo", + "pj.otomamuk.inugoimanim", + "pj.otomamuk.iromakat", + "pj.otomamuk.oara", + "pj.otomamuk.orihsustay", + "pj.otomamuk.osa", + "pj.otomamuk.otamay", + "pj.otomamuk.otomamuk", + "pj.otomamuk.otomus", + "pj.otomamuk.otu", + "pj.otomamuk.otukoyg", + "pj.otomamuk.oyohc", + "pj.otomamuk.usagan", + "pj.otomamuk.uzo", + "pj.otoyk", + "pj.otoyk.abmatoyk", + "pj.otoyk.akies", + "pj.otoyk.akoemak", + "pj.otoyk.akuzaw", + "pj.otoyk.amayihcukuf", + "pj.otoyk.amayihsagih", + "pj.otoyk.amayimuk", + "pj.otoyk.anihsamay", + "pj.otoyk.arawatiju", + "pj.otoyk.ataway", + "pj.otoyk.atik", + "pj.otoyk.ebanat", + "pj.otoyk.ebanatoyk", + "pj.otoyk.ebaya", + "pj.otoyk.edi", + "pj.otoyk.eni", + "pj.otoyk.iju", + "pj.otoyk.ikazamayo", + "pj.otoyk.imanim", + "pj.otoyk.natnan", + "pj.otoyk.ognatoyk", + "pj.otoyk.okum", + "pj.otoyk.omak", + "pj.otoyk.orihsamayimanim", + "pj.otoyk.oygakan", + "pj.otoyk.oykakoagan", + "pj.otoyk.oykas", + "pj.otoyk.oyoj", + "pj.otoyk.uruziam", + "pj.otoyk.uzayim", + "pj.otoyk.uzik", + "pj.oykot", + "pj.oykot.adihcam", + "pj.oykot.adimus", + "pj.oykot.adoyihc", + "pj.oykot.akatim", + "pj.oykot.akihsustak", + "pj.oykot.amat", + "pj.oykot.amatuko", + "pj.oykot.amayarumihsagih", + "pj.oykot.amayarumihsasum", + "pj.oykot.amihsagoa", + "pj.oykot.amihsika", + "pj.oykot.amihso", + "pj.oykot.amihsot", + "pj.oykot.amihsuzuok", + "pj.oykot.amiren", + "pj.oykot.arahonih", + "pj.oykot.arawasago", + "pj.oykot.ariadok", + "pj.oykot.arumah", + "pj.oykot.assuf", + "pj.oykot.atik", + "pj.oykot.ato", + "pj.oykot.awaganihs", + "pj.oykot.awagode", + "pj.oykot.awakara", + "pj.oykot.awakihcat", + "pj.oykot.ayagates", + "pj.oykot.ayubihs", + "pj.oykot.eamok", + "pj.oykot.edonih", + "pj.oykot.emo", + "pj.oykot.emurukihsagih", + "pj.oykot.esoyik", + "pj.oykot.ienagok", + "pj.oykot.igani", + "pj.oykot.ihcada", + "pj.oykot.ihcatinuk", + "pj.oykot.ihsabati", + "pj.oykot.ijnubukok", + "pj.oykot.ijoihcah", + "pj.oykot.imanigus", + "pj.oykot.ohuzim", + "pj.oykot.ojihcah", + "pj.oykot.onakan", + "pj.oykot.onih", + "pj.oykot.onihsasum", + "pj.oykot.onurika", + "pj.oykot.orugem", + "pj.oykot.otamayihsagih", + "pj.oykot.otanim", + "pj.oykot.otiat", + "pj.oykot.otok", + "pj.oykot.ouhc", + "pj.oykot.oyknub", + "pj.oykot.ufohc", + "pj.oykot.uhcuf", + "pj.oykot.ukujnihs", + "pj.rg", + "pj.ro", + "pj.topsgolb", + "pj.ufig", + "pj.ufig.adeki", + "pj.ufig.adih", + "pj.ufig.akimot", + "pj.ufig.amayakat", + "pj.ufig.amihsah", + "pj.ufig.ane", + "pj.ufig.arahagikes", + "pj.ufig.arahagimakak", + "pj.ufig.arahasak", + "pj.ufig.atagamay", + "pj.ufig.atagatik", + "pj.ufig.awagibi", + "pj.ufig.awagustakan", + "pj.ufig.awakarihs", + "pj.ufig.awakarihsihsagih", + "pj.ufig.ekatim", + "pj.ufig.euawak", + "pj.ufig.igohakas", + "pj.ufig.ihcapna", + "pj.ufig.ihcuonaw", + "pj.ufig.ikago", + "pj.ufig.ikes", + "pj.ufig.ikot", + "pj.ufig.imanuzim", + "pj.ufig.imijat", + "pj.ufig.inak", + "pj.ufig.iurat", + "pj.ufig.nanig", + "pj.ufig.odog", + "pj.ufig.ojug", + "pj.ufig.omakonim", + "pj.ufig.onim", + "pj.ufig.oroy", + "pj.ufig.osihcih", + "pj.ufig.ufig", + "pj.ufig.usotom", + "pj.ufig.ustamasak", + "pj.ufig.ustoay", + "pj.井ç¦", + "pj.京æ±", + "pj.分大", + "pj.å–é³¥", + "pj.å£å±±", + "pj.城宮", + "pj.城茨", + "pj.媛愛", + "pj.山富", + "pj.山岡", + "pj.岡ç¦", + "pj.岡é™", + "pj.島広", + "pj.å³¶å¾³", + "pj.å³¶ç¦", + "pj.å´Žå®®", + "pj.å´Žé•·", + "pj.å·çŸ³", + "pj.å·é¦™", + "pj.庫兵", + "pj.形山", + "pj.手岩", + "pj.木栃", + "pj.本熊", + "pj.根島", + "pj.梨山", + "pj.森é’", + "pj.潟新", + "pj.玉埼", + "pj.ç”°ç§‹", + "pj.知愛", + "pj.知高", + "pj.縄沖", + "pj.良奈", + "pj.葉åƒ", + "pj.è³€ä½", + "pj.賀滋", + "pj.都京", + "pj.é‡ä¸‰", + "pj.野長", + "pj.阜å²", + "pj.阪大", + "pj.馬群", + "pj.山歌和", + "pj.å³¶å…鹿", + "pj.å·å¥ˆç¥ž", + "pj.铿µ·åŒ—", + "pk", + "pk.art", + "pk.gro", + "pk.moc", + "pk.per", + "pk.ude", + "pk.vog", + "pleh", + "pm", + "pmac", + "pnd", + "pog", + "pohpih", + "pooc", + "pot", + "prahs", + "pt", + "puorg", + "puorgcts", + "pvsr", + "qa", + "qg", + "qi", + "qi.gro", + "qi.lim", + "qi.moc", + "qi.ten", + "qi.ude", + "qi.vog", + "qm", + "qse", + "ra", + "ra.bog", + "ra.gro", + "ra.lim", + "ra.moc", + "ra.moc.topsgolb", + "ra.rut", + "ra.ten", + "ra.tni", + "ra.ude", + "ra.vog", + "rab", + "ralos", + "ratat", + "ratsivom", + "ratsuen", + "raugaj", + "rb", + "rb.21g", + "rb.b", + "rb.bmi", + "rb.cer", + "rb.csp", + "rb.cte", + "rb.dem", + "rb.dmb", + "rb.dnf", + "rb.dni", + "rb.drt", + "rb.fgg", + "rb.fni", + "rb.gel", + "rb.gls", + "rb.glz", + "rb.gnc", + "rb.gne", + "rb.golb", + "rb.golf", + "rb.golv", + "rb.gpp", + "rb.gro", + "rb.ikiw", + "rb.isp", + "rb.ite", + "rb.ixat", + "rb.lel", + "rb.lim", + "rb.lsq", + "rb.ma", + "rb.mda", + "rb.mf", + "rb.mic", + "rb.moc", + "rb.moc.topsgolb", + "rb.nce", + "rb.oce", + "rb.odo", + "rb.oet", + "rb.oib", + "rb.oidar", + "rb.orp", + "rb.ota", + "rb.pm", + "rb.pme", + "rb.pmt", + "rb.pooc", + "rb.pse", + "rb.qra", + "rb.raf", + "rb.rga", + "rb.roj", + "rb.rtn", + "rb.rut", + "rb.suj", + "rb.sum", + "rb.tam", + "rb.ten", + "rb.tev", + "rb.tnc", + "rb.tof", + "rb.ton", + "rb.tra", + "rb.tsf", + "rb.ude", + "rb.vda", + "rb.vog", + "rb.vrs", + "rb.vt", + "rc", + "rc.as", + "rc.ca", + "rc.de", + "rc.if", + "rc.oc", + "rc.og", + "rc.ro", + "rednik", + "reeb", + "reenigne", + "reerac", + "rehcor", + "reitrac", + "rekop", + "rekorb", + "renes", + "retarebsnegömrev", + "retlaw", + "retnec", + "retupmoc", + "revordnal", + "reywal", + "rf", + "rf.aterg", + "rf.cidessa", + "rf.drp", + "rf.ecitsuj-reissiuh", + "rf.erianiretev", + "rf.esserp", + "rf.icc", + "rf.irgabmahc", + "rf.moc", + "rf.mon", + "rf.mt", + "rf.neicamrahp", + "rf.nicedem", + "rf.ossa", + "rf.selbatpmoc-strepxe", + "rf.seriaton", + "rf.setsitned-sneigrurihc", + "rf.seuova", + "rf.tacova", + "rf.topsgolb", + "rf.trepxe-ertemoeg", + "rf.trop", + "rf.troporea", + "rf.vuog", + "rg", + "rg.gro", + "rg.moc", + "rg.ten", + "rg.topsgolb", + "rg.ude", + "rg.vog", + "rh", + "rh.eman", + "rh.moc", + "rh.morf", + "rh.zi", + "rhur", + "ri", + "ri.ca", + "ri.di", + "ri.gro", + "ri.hcs", + "ri.oc", + "ri.ten", + "ri.vog", + "ri.ناريا", + "ri.ناریا", + "riaper", + "rk", + "rk.ca", + "rk.cs", + "rk.en", + "rk.ep", + "rk.er", + "rk.gk", + "rk.iggnoeyg", + "rk.kubgnoeyg", + "rk.kubgnuhc", + "rk.kubnoej", + "rk.lim", + "rk.luoes", + "rk.mangnoeyg", + "rk.mangnuhc", + "rk.mannoej", + "rk.naslu", + "rk.nasub", + "rk.noehcni", + "rk.noejead", + "rk.nowgnag", + "rk.oc", + "rk.og", + "rk.ro", + "rk.se", + "rk.sh", + "rk.sm", + "rk.topsgolb", + "rk.ugead", + "rk.ujej", + "rk.ujgnawg", + "rl", + "rl.gro", + "rl.moc", + "rl.ten", + "rl.ude", + "rl.vog", + "rm", + "rm.topsgolb", + "rm.vog", + "rn", + "rn.gro", + "rn.moc", + "rn.ofni", + "rn.ten", + "rn.ude", + "rn.vog", + "rn.zib", + "rocs", + "rohtua", + "rotca", + "rotlaer", + "rp", + "rp.alsi", + "rp.ca", + "rp.eman", + "rp.forp", + "rp.gro", + "rp.moc", + "rp.ofni", + "rp.orp", + "rp.ten", + "rp.tse", + "rp.ude", + "rp.vog", + "rp.zib", + "rs", + "rt", + "rt.21k", + "rt.bew", + "rt.cn", + "rt.cn.vog", + "rt.eman", + "rt.gro", + "rt.leb", + "rt.let", + "rt.lim", + "rt.lop", + "rt.moc", + "rt.moc.topsgolb", + "rt.neg", + "rt.ofni", + "rt.pek", + "rt.rd", + "rt.sbb", + "rt.ten", + "rt.ude", + "rt.va", + "rt.vog", + "rt.vt", + "rt.zib", + "rubad", + "sa", + "sa.vog", + "sabirappnb", + "sagev", + "salliv", + "samtsirhc", + "sb", + "sb.gro", + "sb.moc", + "sb.ten", + "sb.ude", + "sb.vog", + "sboj", + "sbs", + "sbu", + "scihparg", + "scip", + "scitylana", + "scod", + "sda", + "sdl", + "sdniwriaf", + "sdnomaid", + "sdrac", + "se", + "se.bog", + "se.gro", + "se.moc", + "se.moc.topsgolb", + "se.mon", + "se.ude", + "secivres", + "sedoc", + "seilppus", + "seirtsudni", + "seitreporp", + "sejaiv", + "selaw", + "selcycrotom", + "selgnis", + "semoh", + "semreh", + "seohs", + "sepicer", + "serit", + "serutcip", + "serutnev", + "sesirpretne", + "sesiurc", + "sesruoc", + "setaicossa", + "sevig", + "sg", + "sgnidloh", + "si", + "si.ekacpuc", + "si.gro", + "si.moc", + "si.ten", + "si.tni", + "si.ude", + "si.vog", + "sinnet", + "sirap", + "sitarg", + "skcor", + "skrow", + "sl", + "sl.gro", + "sl.oc", + "slaed", + "slatner", + "slessurb", + "sloot", + "sm", + "sm.gro", + "sm.moc", + "sm.ten", + "sm.ude", + "sm.vog", + "smb", + "smetsys", + "smialc", + "snaf", + "snagorf", + "snaol", + "sniagrab", + "sniamod", + "snoitacav", + "snoitcudorp", + "snoitulos", + "sodnoc", + "sogeuj", + "sotohp", + "sotua", + "sp", + "sp.ces", + "sp.gro", + "sp.moc", + "sp.olp", + "sp.ten", + "sp.ude", + "sp.vog", + "spihsralohcs", + "spilihp", + "spit", + "sr", + "sr.ca", + "sr.gro", + "sr.ni", + "sr.oc", + "sr.ude", + "sr.vog", + "srac", + "srap", + "sratiug", + "src", + "sredliub", + "sreerac", + "srentrap", + "srewolf", + "srotcartnoc", + "srpj", + "ssalg", + "ssenisub", + "ssentif", + "sserp", + "ssiws", + "staeytic", + "staob", + "stekram", + "stfig", + "sthcay", + "sthgilf", + "stnatnuocca", + "stnemtrapa", + "stnemtsevni", + "stneve", + "strap", + "su", + "su.ac", + "su.ac.21k", + "su.ac.bil", + "su.ac.cc", + "su.ag", + "su.ag.21k", + "su.ag.bil", + "su.ag.cc", + "su.ai", + "su.ai.21k", + "su.ai.bil", + "su.ai.cc", + "su.al", + "su.al.21k", + "su.al.bil", + "su.al.cc", + "su.am", + "su.am.21k", + "su.am.21k.hcorap", + "su.am.21k.rthc", + "su.am.21k.tvp", + "su.am.bil", + "su.am.cc", + "su.ap", + "su.ap.21k", + "su.ap.bil", + "su.ap.cc", + "su.asi", + "su.av", + "su.av.21k", + "su.av.bil", + "su.av.cc", + "su.aw", + "su.aw.21k", + "su.aw.bil", + "su.aw.cc", + "su.cd", + "su.cd.21k", + "su.cd.bil", + "su.cd.cc", + "su.cn", + "su.cn.21k", + "su.cn.bil", + "su.cn.cc", + "su.cs", + "su.cs.21k", + "su.cs.bil", + "su.cs.cc", + "su.def", + "su.di", + "su.di.21k", + "su.di.bil", + "su.di.cc", + "su.dm", + "su.dm.21k", + "su.dm.bil", + "su.dm.cc", + "su.dn", + "su.dn.bil", + "su.dn.cc", + "su.ds", + "su.ds.bil", + "su.ds.cc", + "su.ed", + "su.ed.21k", + "su.ed.bil", + "su.ed.cc", + "su.elas-4-dnal", + "su.elas-4-ffuts", + "su.em", + "su.em.21k", + "su.em.bil", + "su.em.cc", + "su.en", + "su.en.21k", + "su.en.bil", + "su.en.cc", + "su.hn", + "su.hn.21k", + "su.hn.bil", + "su.hn.cc", + "su.ho", + "su.ho.21k", + "su.ho.bil", + "su.ho.cc", + "su.ih", + "su.ih.bil", + "su.ih.cc", + "su.im", + "su.im.21k", + "su.im.bil", + "su.im.cc", + "su.ind", + "su.ir", + "su.ir.21k", + "su.ir.bil", + "su.ir.cc", + "su.iv", + "su.iv.21k", + "su.iv.bil", + "su.iv.cc", + "su.iw", + "su.iw.21k", + "su.iw.bil", + "su.iw.cc", + "su.jn", + "su.jn.21k", + "su.jn.bil", + "su.jn.cc", + "su.ka", + "su.ka.21k", + "su.ka.bil", + "su.ka.cc", + "su.ko", + "su.ko.21k", + "su.ko.bil", + "su.ko.cc", + "su.la", + "su.la.21k", + "su.la.bil", + "su.la.cc", + "su.lf", + "su.lf.21k", + "su.lf.bil", + "su.lf.cc", + "su.li", + "su.li.21k", + "su.li.bil", + "su.li.cc", + "su.mn", + "su.mn.21k", + "su.mn.bil", + "su.mn.cc", + "su.ni", + "su.ni.21k", + "su.ni.bil", + "su.ni.cc", + "su.nm", + "su.nm.21k", + "su.nm.bil", + "su.nm.cc", + "su.nsn", + "su.nt", + "su.nt.21k", + "su.nt.bil", + "su.nt.cc", + "su.oc", + "su.oc.21k", + "su.oc.bil", + "su.oc.cc", + "su.om", + "su.om.21k", + "su.om.bil", + "su.om.cc", + "su.ra", + "su.ra.21k", + "su.ra.bil", + "su.ra.cc", + "su.ro", + "su.ro.21k", + "su.ro.bil", + "su.ro.cc", + "su.rp", + "su.rp.21k", + "su.rp.bil", + "su.rp.cc", + "su.sa", + "su.sa.21k", + "su.sa.bil", + "su.sa.cc", + "su.sdik", + "su.sk", + "su.sk.21k", + "su.sk.bil", + "su.sk.cc", + "su.sm", + "su.sm.21k", + "su.sm.bil", + "su.sm.cc", + "su.tc", + "su.tc.21k", + "su.tc.bil", + "su.tc.cc", + "su.tm", + "su.tm.21k", + "su.tm.bil", + "su.tm.cc", + "su.tu", + "su.tu.21k", + "su.tu.bil", + "su.tu.cc", + "su.tv", + "su.tv.21k", + "su.tv.bil", + "su.tv.cc", + "su.ug", + "su.ug.21k", + "su.ug.bil", + "su.ug.cc", + "su.vn", + "su.vn.21k", + "su.vn.bil", + "su.vn.cc", + "su.vw", + "su.vw.cc", + "su.xt", + "su.xt.21k", + "su.xt.bil", + "su.xt.cc", + "su.yb-si", + "su.yk", + "su.yk.21k", + "su.yk.bil", + "su.yk.cc", + "su.yn", + "su.yn.21k", + "su.yn.bil", + "su.yn.cc", + "su.yw", + "su.yw.21k", + "su.yw.bil", + "su.yw.cc", + "su.za", + "su.za.21k", + "su.za.bil", + "su.za.cc", + "suah", + "suahuab", + "sue", + "suxen", + "sw", + "sw.gro", + "sw.moc", + "sw.sndnyd", + "sw.stepym", + "sw.ten", + "sw.ude", + "sw.vog", + "sweiver", + "swen", + "swodniw", + "syalcrab", + "syot", + "ta", + "ta.ca", + "ta.oc", + "ta.oc.topsgolb", + "ta.ofni", + "ta.ro", + "ta.vg", + "ta.virp", + "ta.zib", + "tac", + "tae", + "taes", + "tahc", + "tal", + "tarcomed", + "tb", + "tb.gro", + "tb.moc", + "tb.ten", + "tb.ude", + "tb.vog", + "tbgl", + "tcerid", + "tdimhcs", + "te", + "te.eman", + "te.gro", + "te.moc", + "te.ofni", + "te.ude", + "te.vog", + "te.zib", + "teem", + "tegaip", + "teid", + "tekcirc", + "tekram", + "ten", + "ten.al-morf", + "ten.az", + "ten.bg", + "ten.dnab-eht-ni", + "ten.eht-no-eciffo", + "ten.elibom-eruza", + "ten.emohtanyd", + "ten.enozdop", + "ten.erehurht", + "ten.es", + "ten.etis-repparcs", + "ten.fehc-a-si", + "ten.keeg-a-si", + "ten.keeg-asi", + "ten.ku", + "ten.ni", + "ten.oc-morf", + "ten.ojodsnd", + "ten.piemoh", + "ten.pifles", + "ten.pj", + "ten.pmac-dnab-ta", + "ten.po-oidar-mah", + "ten.pohbew", + "ten.ppaduolc", + "ten.ptfemoh", + "ten.ptfevres", + "ten.sailanyd", + "ten.sailasnd", + "ten.sbbevres", + "ten.sesuohsyub", + "ten.setisbeweruza", + "ten.sndgolb", + "ten.ssa-skcik", + "ten.tenretnifodne", + "ten.ti-ekorb", + "ten.ti-seod", + "ten.ti-slles", + "ten.ti-steg", + "ten.tnorfduolc", + "ten.tsixetnod", + "ten.uh", + "ten.xinuemoh", + "ten.xunilemoh", + "ten.yltsaf.dorp.a", + "ten.yltsaf.dorp.labolg", + "ten.yltsaf.lss.a", + "ten.yltsaf.lss.b", + "ten.yltsaf.lss.labolg", + "ten.yn-morf", + "ten.za-morf", + "tenii", + "tetcip", + "tev", + "tfig", + "tfosorcim", + "tg", + "tg.bog", + "tg.dni", + "tg.gro", + "tg.lim", + "tg.moc", + "tg.ten", + "tg.ude", + "th", + "th.dem", + "th.gro", + "th.ler", + "th.lop", + "th.moc", + "th.mrif", + "th.ofni", + "th.orp", + "th.osrep", + "th.ossa", + "th.pohs", + "th.pooc", + "th.ten", + "th.tluda", + "th.tra", + "th.ude", + "th.vuog", + "ti", + "ti.aat", + "ti.ab", + "ti.ac", + "ti.accul", + "ti.adv", + "ti.aiblo-oipmet", + "ti.aiblooipmet", + "ti.aicserb", + "ti.aidrabmol", + "ti.aiggof", + "ti.aigurep", + "ti.ailgup", + "ti.ailicis", + "ti.ailime-oigger", + "ti.ailimeoigger", + "ti.ailuig-aizenev-iluirf", + "ti.ailuig-aizeneviluirf", + "ti.ailuig-ev-iluirf", + "ti.ailuig-eviluirf", + "ti.ailuig-v-iluirf", + "ti.ailuig-viluirf", + "ti.ailuigaizenev-iluirf", + "ti.ailuigaizeneviluirf", + "ti.ailuigev-iluirf", + "ti.ailuigeviluirf", + "ti.ailuigv-iluirf", + "ti.ailuigviluirf", + "ti.ainabrev", + "ti.ainacul", + "ti.ainapmac", + "ti.ainatac", + "ti.ainidras", + "ti.ainobrac-saiselgi", + "ti.ainobracsaiselgi", + "ti.ainresi", + "ti.aiotsip", + "ti.airbalac", + "ti.airbalac-oigger", + "ti.airbalacoigger", + "ti.airbmu", + "ti.airdna-attelrab-inart", + "ti.airdna-inart-attelrab", + "ti.airdnaattelrabinart", + "ti.airdnainartattelrab", + "ti.airdnassela", + "ti.airepmi", + "ti.airugil", + "ti.aitnelav-obiv", + "ti.aitnelavobiv", + "ti.aivap", + "ti.aizenev", + "ti.aizeps-al", + "ti.aizepsal", + "ti.aizirog", + "ti.aliuqa", + "ti.aliuqal", + "ti.alleib", + "ti.amor", + "ti.amrap", + "ti.an", + "ti.anacsot", + "ti.anedom", + "ti.aneis", + "ti.anesec-ilrof", + "ti.anesecilrof", + "ti.angamor-ailime", + "ti.angamorailime", + "ti.angedras", + "ti.angolob", + "ti.anissem", + "ti.anital", + "ti.anne", + "ti.annevar", + "ti.anocna", + "ti.anomerc", + "ti.anorev", + "ti.anovas", + "ti.aoneg", + "ti.ap", + "ti.ar", + "ti.aracsep", + "ti.ararrac-assam", + "ti.ararracassam", + "ti.ararref", + "ti.aravon", + "ti.aretam", + "ti.artsailgo", + "ti.artsailgo-lled", + "ti.artsailgolled", + "ti.as", + "ti.asip", + "ti.assam-ararrac", + "ti.assamararrac", + "ti.asucaris", + "ti.asugar", + "ti.at", + "ti.atacilisab", + "ti.atarecam", + "ti.atresac", + "ti.atsoa", + "ti.atsoa-d-ellav", + "ti.atsoa-d-lav", + "ti.atsoa-dellav", + "ti.atsoa-dlav", + "ti.atsoa-ellav", + "ti.atsoad-ellav", + "ti.atsoad-lav", + "ti.atsoadellav", + "ti.atsoadlav", + "ti.atsoaellav", + "ti.attelrab-airdna-inart", + "ti.attelrab-inart-airdna", + "ti.attelrabairdnainart", + "ti.attelrabinartairdna", + "ti.attessinatlac", + "ti.audap", + "ti.av", + "ti.avodap", + "ti.avoneg", + "ti.avotnam", + "ti.aznairb-alled-e-aznom", + "ti.aznairb-aznom", + "ti.aznairballedeaznom", + "ti.aznairbaznom", + "ti.aznairbeaznom", + "ti.aznecaip", + "ti.azneciv", + "ti.aznesoc", + "ti.aznetop", + "ti.aznom", + "ti.bc", + "ti.bm", + "ti.bmu", + "ti.bv", + "ti.cf", + "ti.cis", + "ti.cl", + "ti.cm", + "ti.cp", + "ti.cr", + "ti.cv", + "ti.dp", + "ti.du", + "ti.ec", + "ti.eccel", + "ti.ecinev", + "ti.ecnerolf", + "ti.ef", + "ti.eg", + "ti.egida-a-onitnert", + "ti.egida-aonitnert", + "ti.egida-otla", + "ti.egida-otla-onitnert", + "ti.egida-otlaonitnert", + "ti.egidaa-onitnert", + "ti.egidaaonitnert", + "ti.egidaotla", + "ti.egidaotla-onitnert", + "ti.egidaotlaonitnert", + "ti.ehcram", + "ti.el", + "ti.em", + "ti.emor", + "ti.enidu", + "ti.enonedrop", + "ti.enonisorf", + "ti.enotorc", + "ti.ep", + "ti.er", + "ti.eserav", + "ti.esilom", + "ti.et", + "ti.etnomeip", + "ti.etseirt", + "ti.etsoa", + "ti.etsoa-eellav", + "ti.etsoaeellav", + "ti.ev", + "ti.eznerif", + "ti.ga", + "ti.gb", + "ti.gf", + "ti.gil", + "ti.go", + "ti.gp", + "ti.gr", + "ti.gup", + "ti.gvf", + "ti.hc", + "ti.ib", + "ti.ic", + "ti.idol", + "ti.if", + "ti.il", + "ti.illecrev", + "ti.ilopan", + "ti.ilrof-anesec", + "ti.ilrofanesec", + "ti.im", + "ti.inapart", + "ti.inart-attelrab-airdna", + "ti.inartattelrabairdna", + "ti.inimir", + "ti.inret", + "ti.ip", + "ti.ir", + "ti.irab", + "ti.irailgac", + "ti.irassas", + "ti.is", + "ti.isidnirb", + "ti.iteihc", + "ti.iteir", + "ti.itsa", + "ti.iv", + "ti.la", + "ti.lac", + "ti.lb", + "ti.lc", + "ti.lom", + "ti.lorit-deus-onitnert", + "ti.lorit-deusonitnert", + "ti.lorit-dus-onitnert", + "ti.lorit-dusonitnert", + "ti.lorit-s-onitnert", + "ti.lorit-sonitnert", + "ti.loritdeus", + "ti.loritdeus-onitnert", + "ti.loritdeusonitnert", + "ti.loritdus-onitnert", + "ti.loritdusonitnert", + "ti.lorits-onitnert", + "ti.loritsonitnert", + "ti.mac", + "ti.mf", + "ti.mi", + "ti.mol", + "ti.mr", + "ti.na", + "ti.nalim", + "ti.naslab", + "ti.nb", + "ti.nc", + "ti.ne", + "ti.nev", + "ti.nezob", + "ti.nirut", + "ti.nm", + "ti.nmp", + "ti.np", + "ti.nr", + "ti.nt", + "ti.oa", + "ti.oav", + "ti.ob", + "ti.obretiv", + "ti.oc", + "ti.occel", + "ti.oenuc", + "ti.og", + "ti.ogivor", + "ti.oidem-onadipmac", + "ti.oidemonadipmac", + "ti.oipmet-aiblo", + "ti.oipmetaiblo", + "ti.oirdnos", + "ti.oizal", + "ti.ol", + "ti.om", + "ti.omagreb", + "ti.omaret", + "ti.omoc", + "ti.omref", + "ti.omrelap", + "ti.on", + "ti.onadipmac-oidem", + "ti.onadipmacoidem", + "ti.onalim", + "ti.onatsiro", + "ti.onazlob", + "ti.onecip-ilocsa", + "ti.onecipilocsa", + "ti.onibru-orasep", + "ti.onibruorasep", + "ti.onilleva", + "ti.onirot", + "ti.onitnert", + "ti.onrelas", + "ti.onrovil", + "ti.onulleb", + "ti.op", + "ti.or", + "ti.orasep-onibru", + "ti.oraseponibru", + "ti.oraznatac", + "ti.oroun", + "ti.os", + "ti.osivert", + "ti.ossabopmac", + "ti.ot", + "ti.otarp", + "ti.otenev", + "ti.otessorg", + "ti.otnarat", + "ti.otnegirga", + "ti.otnert", + "ti.otneveneb", + "ti.ozzera", + "ti.ozzurba", + "ti.pa", + "ti.ps", + "ti.pt", + "ti.qa", + "ti.ra", + "ti.ram", + "ti.ras", + "ti.rb", + "ti.rba", + "ti.rc", + "ti.rf", + "ti.rg", + "ti.rk", + "ti.rme", + "ti.ro", + "ti.rp", + "ti.rs", + "ti.rt", + "ti.rv", + "ti.sab", + "ti.saiselgi-ainobrac", + "ti.saiselgiainobrac", + "ti.sb", + "ti.sc", + "ti.selpan", + "ti.si", + "ti.sm", + "ti.sot", + "ti.ss", + "ti.st", + "ti.sv", + "ti.ta", + "ti.tb", + "ti.tc", + "ti.tl", + "ti.tm", + "ti.tnomdeip", + "ti.to", + "ti.topsgolb", + "ti.tp", + "ti.tv", + "ti.ude", + "ti.ul", + "ti.un", + "ti.up", + "ti.va", + "ti.vog", + "ti.vp", + "ti.vs", + "ti.vt", + "ti.vv", + "ti.ydrabmol", + "ti.yellav-atsoa", + "ti.yellavatsoa", + "ti.ylicis", + "ti.ynacsut", + "ti.zal", + "ti.zb", + "ti.zc", + "ti.zp", + "tiderc", + "tier", + "tif", + "tl", + "tl.vog", + "tluda", + "tm", + "tm.gro", + "tm.moc", + "tm.ten", + "tm.ude", + "tnamorockivdnas", + "tnaruatser", + "tnatnuocca", + "tneg", + "tnemeganam", + "tnempiuqe", + "tner", + "tni", + "tni.ue", + "tniopdlog", + "tnirpatsiv", + "tnuocsid", + "tob", + "tocs", + "tog", + "toj", + "tp", + "tp.emon", + "tp.gro", + "tp.lbup", + "tp.moc", + "tp.ten", + "tp.tni", + "tp.topsgolb", + "tp.ude", + "tp.vog", + "trepxe", + "troper", + "troppus", + "ts", + "ts.adaxiabme", + "ts.emotoas", + "ts.epicnirp", + "ts.erots", + "ts.gro", + "ts.lim", + "ts.moc", + "ts.oc", + "ts.odalusnoc", + "ts.ten", + "ts.ude", + "ts.vog", + "tsaf", + "tseb", + "tsepadub", + "tser", + "tsi", + "tsirolf", + "tsitned", + "tsoh", + "tsop", + "tsurt", + "tt", + "tt.eman", + "tt.gro", + "tt.ibom", + "tt.levart", + "tt.moc", + "tt.muesum", + "tt.oc", + "tt.ofni", + "tt.orea", + "tt.orp", + "tt.pooc", + "tt.sboj", + "tt.ten", + "tt.tni", + "tt.ude", + "tt.vog", + "tt.zib", + "ttn", + "ttobba", + "ttoirram", + "tuognah", + "ty", + "tztej", + "ua", + "ua.as", + "ua.aw", + "ua.civ", + "ua.di", + "ua.dlq", + "ua.fnoc", + "ua.gro", + "ua.moc", + "ua.moc.topsgolb", + "ua.nsa", + "ua.ofni", + "ua.sat", + "ua.tca", + "ua.ten", + "ua.tn", + "ua.ude", + "ua.ude.as", + "ua.ude.aw", + "ua.ude.civ", + "ua.ude.dlq", + "ua.ude.sat", + "ua.ude.tca", + "ua.ude.tn", + "ua.ude.wsn", + "ua.vog", + "ua.vog.as", + "ua.vog.aw", + "ua.vog.civ", + "ua.vog.dlq", + "ua.vog.sat", + "ua.wsn", + "ua.zo", + "uati", + "uc", + "uc.fni", + "uc.gro", + "uc.moc", + "uc.ten", + "uc.ude", + "uc.vog", + "ude", + "ue", + "uh", + "uh.0002", + "uh.acitore", + "uh.aidem", + "uh.akitore", + "uh.edszot", + "uh.gro", + "uh.ilus", + "uh.letoh", + "uh.malker", + "uh.mlif", + "uh.mt", + "uh.murof", + "uh.naltagni", + "uh.oc", + "uh.oediv", + "uh.ofni", + "uh.olevynok", + "uh.onisac", + "uh.pohs", + "uh.rarga", + "uh.sakal", + "uh.sazatu", + "uh.semag", + "uh.swen", + "uh.tlob", + "uh.topsgolb", + "uh.trops", + "uh.virp", + "uh.xes", + "uh.xezs", + "uh.ytic", + "uh.zsagoj", + "uhos", + "uhsut", + "ul", + "um", + "um.ca", + "um.gro", + "um.moc", + "um.oc", + "um.ro", + "um.ten", + "um.vog", + "un", + "un.eniesrem", + "un.enim", + "un.tenkcahs", + "unem", + "uoggnaw", + "ur", + "ur.adgolov", + "ur.agulak", + "ur.aihsavuhc", + "ur.aikymlak", + "ur.ailerak", + "ur.airikhsab", + "ur.aissakahk", + "ur.aitayrub", + "ur.aitrumdu", + "ur.aitukay", + "ur.aivodrom", + "ur.akdohkan", + "ur.aknidud", + "ur.aktahcmak", + "ur.aktayv", + "ur.aktingam", + "ur.aktokuhc", + "ur.alo-rakhsoj", + "ur.alut", + "ur.amortsok", + "ur.analap", + "ur.aramas", + "ur.atihc", + "ur.avut", + "ur.ayegyda", + "ur.aznep", + "ur.bps", + "ur.ca", + "ur.dargoglov", + "ur.dargz", + "ur.dnr", + "ur.dorogleb", + "ur.edu-nalu", + "ur.eniram", + "ur.gbc", + "ur.gineok", + "ur.gro", + "ur.grub-e", + "ur.grubnero", + "ur.grubniretakey", + "ur.hzenorov", + "ur.ianatsuk", + "ur.iatla", + "ur.imok", + "ur.iram", + "ur.kihclan", + "ur.kotsovidalv", + "ur.kslaru-k", + "ur.kslegnahkra", + "ur.ksliron", + "ur.ksm", + "ur.ksmo", + "ur.ksmot", + "ur.ksn", + "ur.ksnamrum", + "ur.ksnayrb", + "ur.ksneloms", + "ur.ksnibaylehc", + "ur.ksnilahkas-onhzuy", + "ur.ksnodv", + "ur.ksrayonsark", + "ur.ksribisovon", + "ur.ksribmis", + "ur.ksrogitayp", + "ur.ksruk", + "ur.ksruma", + "ur.kst", + "ur.kstepil", + "ur.kstukri", + "ur.ksvehzi", + "ur.ksvorabahk", + "ur.ksvostbur", + "ur.lakiab", + "ur.lamaj", + "ur.lamay", + "ur.le-iram", + "ur.lehc", + "ur.lim", + "ur.lokso", + "ur.loporvats", + "ur.loyro", + "ur.lvalsoray", + "ur.mdu", + "ur.moc", + "ur.mot", + "ur.mrep", + "ur.nabuk", + "ur.nadagam", + "ur.nagruk", + "ur.nahkartsa", + "ur.narzys", + "ur.natsegad", + "ur.natsratat", + "ur.nazak", + "ur.nazayr", + "ur.nemuyt", + "ur.nilahkas", + "ur.nrv", + "ur.nystirast", + "ur.ovonavi", + "ur.ovoremek", + "ur.pp", + "ur.raj", + "ur.revt", + "ur.rhck", + "ur.rib", + "ur.rimidalv", + "ur.ruma", + "ur.sitym", + "ur.smk", + "ur.ssabzuk", + "ur.ten", + "ur.tni", + "ur.topsgolb", + "ur.tsaeraf", + "ur.tset", + "ur.tugrus", + "ur.ude", + "ur.vhk", + "ur.vobmat", + "ur.vog", + "ur.von", + "ur.vonn", + "ur.vorik", + "ur.votaras", + "ur.vts", + "ur.wmc", + "ur.ynzorg", + "ur.zakvakidalv", + "ur.zkn", + "ur.zns", + "ur.ztp", + "urmyc", + "urug", + "us", + "uv", + "uv.gro", + "uv.moc", + "uv.ten", + "uv.ude", + "uykuyr", + "vb", + "vc", + "vc.topsgolb", + "ved", + "vih", + "vl", + "vl.di", + "vl.fnoc", + "vl.gro", + "vl.lim", + "vl.moc", + "vl.nsa", + "vl.ten", + "vl.ude", + "vl.vog", + "vm", + "vm.eman", + "vm.gro", + "vm.lim", + "vm.moc", + "vm.muesum", + "vm.ofni", + "vm.orea", + "vm.orp", + "vm.pooc", + "vm.ten", + "vm.tni", + "vm.ude", + "vm.vog", + "vm.zib", + "vog", + "vom", + "vs", + "vs.bog", + "vs.der", + "vs.gro", + "vs.moc", + "vs.ude", + "vt", + "vt.bew-eht-no", + "vt.naht-esrow", + "vt.naht-retteb", + "vt.sndnyd", + "wa", + "wa.moc", + "wb", + "wb.gro", + "wb.oc", + "wc", + "wc.gro", + "wc.moc", + "wc.ten", + "wc.ude", + "weiver", + "wen", + "wes", + "wg", + "wm", + "wm.ca", + "wm.gro", + "wm.moc", + "wm.muesum", + "wm.oc", + "wm.pooc", + "wm.ten", + "wm.tni", + "wm.ude", + "wm.vog", + "wm.zib", + "wmb", + "wocsom", + "woh", + "wp", + "wp.de", + "wp.en", + "wp.oc", + "wp.og", + "wp.ro", + "wp.ualeb", + "wr", + "wr.ca", + "wr.lim", + "wr.moc", + "wr.oc", + "wr.ten", + "wr.tni", + "wr.ude", + "wr.vog", + "wr.vuog", + "wrn", + "wt", + "wt.bulc", + "wt.emag", + "wt.gro", + "wt.lim", + "wt.moc", + "wt.ten", + "wt.topsgolb", + "wt.ude", + "wt.vdi", + "wt.vog", + "wt.zibe", + "wt.業商", + "wt.織組", + "wt.路網", + "xa", + "xat", + "xc", + "xc.hta", + "xc.vog", + "xedan", + "xednay", + "xerof", + "xes", + "xm", + "xm.bog", + "xm.gro", + "xm.moc", + "xm.ten", + "xm.topsgolb", + "xm.ude", + "xmg", + "xobx", + "xorex", + "xs", + "xs.vog", + "xxx", + "yad", + "yadiloh", + "yadirfkcalb", + "yadot", + "yarot", + "yb", + "yb.fo", + "yb.lim", + "yb.moc", + "yb.vog", + "ycamrahp", + "ycnega", + "yduts", + "yeltneb", + "yendys", + "yenom", + "yenrotta", + "yg", + "yg.moc", + "yg.oc", + "yg.ten", + "ygolonhcet", + "ygrene", + "yhpargotohp", + "yk", + "yk.gro", + "yk.moc", + "yk.ten", + "yk.ude", + "yk.vog", + "yks", + "yl", + "yl.clp", + "yl.dem", + "yl.di", + "yl.gro", + "yl.hcs", + "yl.moc", + "yl.ten", + "yl.ude", + "yl.vog", + "ylf", + "ylibom", + "ylppus", + "ym", + "ym.eman", + "ym.gro", + "ym.lim", + "ym.moc", + "ym.ten", + "ym.ude", + "ym.vog", + "ymedaca", + "ymra", + "ynapmoc", + "yoj", + "yos", + "yp", + "yp.gro", + "yp.lim", + "yp.moc", + "yp.pooc", + "yp.ten", + "yp.ude", + "yp.vog", + "yregrus", + "yrellag", + "yreviled", + "yrotcerid", + "yrtnuoc", + "yruxul", + "ys", + "ys.gro", + "ys.lim", + "ys.moc", + "ys.ten", + "ys.ude", + "ys.vog", + "ytic", + "ytinummoc", + "ytisrevinu", + "ytrap", + "ytreporp", + "yu", + "yu.bug", + "yu.gro", + "yu.lim", + "yu.moc", + "yu.ten", + "yu.ude", + "yub", + "yvan", + "yxes", + "za", + "za.eman", + "za.gro", + "za.lim", + "za.moc", + "za.ofni", + "za.orp", + "za.pp", + "za.ten", + "za.tni", + "za.ude", + "za.vog", + "za.zib", + "zb", + "zb.az", + "zb.gro", + "zb.moc", + "zb.ten", + "zb.ude", + "zb.vog", + "zc", + "zc.topsgolb", + "zd", + "zd.gro", + "zd.lop", + "zd.moc", + "zd.ossa", + "zd.ten", + "zd.tra", + "zd.ude", + "zd.vog", + "zib", + "zib.eht-rof", + "zib.emos-rof", + "zib.erom-rof", + "zib.pifles", + "zib.pohbew", + "zib.retteb-rof", + "zib.sndnyd", + "zibg", + "zk", + "zk.gro", + "zk.lim", + "zk.moc", + "zk.ten", + "zk.ude", + "zk.vog", + "zn", + "zn.ca", + "zn.gro", + "zn.htlaeh", + "zn.irc", + "zn.iroam", + "zn.iroÄm", + "zn.iwi", + "zn.iwik", + "zn.keeg", + "zn.lim", + "zn.loohcs", + "zn.neg", + "zn.oc", + "zn.oc.topsgolb", + "zn.ten", + "zn.tnemailrap", + "zn.tvog", + "znaniflla", + "zrawhcs", + "zs", + "zs.ca", + "zs.gro", + "zs.oc", + "zt", + "zt.ca", + "zt.cs", + "zt.em", + "zt.en", + "zt.ibom", + "zt.letoh", + "zt.lim", + "zt.oc", + "zt.ofni", + "zt.og", + "zt.ro", + "zt.vt", + "zu", + "zu.gro", + "zu.moc", + "zu.oc", + "zu.ten", + "zurwon", + "zyx", + "zzub", + "фр", + "ეგ", + "业ä¼", + "东广", + "ä¹å¨±", + "信中", + "务政", + "动移", + "å¦å…«", + "厅é¤", + "å¸å…¬", + "å–„æ…ˆ", + "团集", + "国中", + "國中", + "å€ç½‘", + "城商", + "å°šæ—¶", + "山佛", + "店商", + "店网", + "府政", + "康å¥", + "æ¯ä¿¡", + "æˆæ¸¸", + "机手", + "构机", + "标商", + "歌谷", + "港香", + "æ¹¾å°", + "ç£å°", + "ç£è‡º", + "界世", + "益公", + "线在", + "络网", + "闻新", + "국한", + "성삼", + "брÑ", + "гро", + "зақ", + "ном", + "рку", + "Ñур", + "رصم", + "رطق", + "ยทไ", + "ãªã‚“ã¿", + "你爱我", + "å¡åŠ æ–°", + "浦利飞", + "网文中", + "锡马淡", + "брÑ.до", + "брÑ.ка", + "брÑ.рп", + "брÑ.гро", + "брÑ.рбо", + "брÑ.рпу", + "итед", + "тйаÑ", + "ةكبش", + "سنوت", + "عقوم", + "كتيب", + "نامع", + "तराभ", + "তরাভ", + "ਤਰਾਭ", + "તરાભ", + "à·à¶šà¶‚à¶½", + "トンイãƒ", + "ルグーグ", + "构机织组", + "ايروس", + "ةيروس", + "تراھب", + "رازاب", + "ناريا", + "ناریا", + "نميلا", + "هارمه", + "नठगंस", + "ালংাব", + "à±à°¤à°°à°¾à°­", + "авкÑом", + "нйално", + "ايسيلم", + "برغملا", + "تاراما", + "ندرالا", + "نيطسلÙ", + "وكمارا", + "ைகà¯à®™à®²à®‡", + "رئازجلا", + "يليابوم", + "ாயிதà¯à®¨à®‡", + "ةيدوعسلا", + "ةیدوعسلا", + "هيدوعسلا", + "ۃیدوعسلا", + "à¯à®°à¯‚பà¯à®ªà®•à¯à®™à®¿à®š", +} + +var count = len(list) + diff --git a/vendor/github.com/jpillora/go-tld/parse_test.go b/vendor/github.com/jpillora/go-tld/parse_test.go new file mode 100644 index 00000000..4cea0e98 --- /dev/null +++ b/vendor/github.com/jpillora/go-tld/parse_test.go @@ -0,0 +1,34 @@ +package tld + +import "testing" + +func run(input, sub, dom, tld string, t *testing.T) { + + u, err := Parse(input) + + if err != nil { + t.Errorf("errored '%s'", err) + } else if u.TLD != tld { + t.Errorf("should have TLD '%s', got '%s'", tld, u.TLD) + } else if u.Domain != dom { + t.Errorf("should have Domain '%s', got '%s'", dom, u.Domain) + } else if u.Subdomain != sub { + t.Errorf("should have Subdomain '%s', got '%s'", sub, u.Subdomain) + } +} + +func Test0(t *testing.T) { + run("http://foo.com", "", "foo", "com", t) +} + +func Test1(t *testing.T) { + run("http://zip.zop.foo.com", "zip.zop", "foo", "com", t) +} + +func Test2(t *testing.T) { + run("http://au.com.au", "", "au", "com.au", t) +} + +func Test3(t *testing.T) { + run("http://im.from.england.co.uk:1900", "im.from", "england", "co.uk", t) +} diff --git a/vendor/github.com/malfunkt/iprange/.travis.yml b/vendor/github.com/malfunkt/iprange/.travis.yml new file mode 100644 index 00000000..7e091f8d --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/.travis.yml @@ -0,0 +1,8 @@ +language: go + +go: + - "1.8" + - "1.9" + +script: + - "go test -v" diff --git a/vendor/github.com/malfunkt/iprange/LICENSE b/vendor/github.com/malfunkt/iprange/LICENSE new file mode 100644 index 00000000..c2e7714b --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 José Nieto , Arturo Vergara + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/malfunkt/iprange/README.md b/vendor/github.com/malfunkt/iprange/README.md new file mode 100644 index 00000000..9a2e5fe2 --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/README.md @@ -0,0 +1,42 @@ +# iprange + +[![GoDoc](https://godoc.org/github.com/malfunkt/iprange?status.svg)](https://godoc.org/github.com/malfunkt/iprange) +[![license](https://img.shields.io/github/license/mashape/apistatus.svg)]() +[![Build Status](https://travis-ci.org/malfunkt/iprange.svg?branch=master)](https://travis-ci.org/malfunkt/iprange) + +`iprange` is a library you can use to parse IPv4 addresses from a string in the `nmap` format. + +It takes a string, and returns a list of `Min`-`Max` pairs, which can then be expanded and normalized automatically by the package. + +## Supported Formats + +`iprange` supports the following formats: + +* `10.0.0.1` +* `10.0.0.0/24` +* `10.0.0.*` +* `10.0.0.1-10` +* `10.0.0.1, 10.0.0.5-10, 192.168.1.*, 192.168.10.0/24` + +## Usage + +```go +package main + +import ( + "log" + + "github.com/malfunkt/iprange" +) + +func main() { + list, err := iprange.ParseList("10.0.0.1, 10.0.0.5-10, 192.168.1.*, 192.168.10.0/24") + if err != nil { + log.Printf("error: %s", err) + } + log.Printf("%+v", list) + + rng := list.Expand() + log.Printf("%s", rng) +} +``` diff --git a/vendor/github.com/malfunkt/iprange/funcs.go b/vendor/github.com/malfunkt/iprange/funcs.go new file mode 100644 index 00000000..b6fc61a4 --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/funcs.go @@ -0,0 +1,92 @@ +package iprange + +import ( + "encoding/binary" + "net" + "sort" +) + +func streamRange(lower, upper net.IP) chan net.IP { + ipchan := make(chan net.IP, 1) + + rangeMask := net.IP([]byte{ + upper[0] - lower[0], + upper[1] - lower[1], + upper[2] - lower[2], + upper[3] - lower[3], + }) + + go func() { + defer close(ipchan) + + lower32 := binary.BigEndian.Uint32([]byte(lower)) + upper32 := binary.BigEndian.Uint32([]byte(upper)) + diff := upper32 - lower32 + + if diff < 0 { + panic("Lower address is actually higher than upper address.") + } + + mask := net.IP([]byte{0, 0, 0, 0}) + + for { + ipchan <- net.IP([]byte{ + lower[0] + mask[0], + lower[1] + mask[1], + lower[2] + mask[2], + lower[3] + mask[3], + }) + + if mask.Equal(rangeMask) { + break + } + + for i := 3; i >= 0; i-- { + if rangeMask[i] > 0 { + if mask[i] < rangeMask[i] { + mask[i] = mask[i] + 1 + break + } else { + mask[i] = mask[i] % rangeMask[i] + if i < 1 { + break + } + } + } + } + } + + }() + + return ipchan +} + +// Expand expands an address with a mask taken from a stream +func (r *AddressRange) Expand() []net.IP { + ips := []net.IP{} + for ip := range streamRange(r.Min, r.Max) { + ips = append(ips, ip) + } + return ips +} + +// Expand expands and normalizes a set of parsed target specifications +func (l AddressRangeList) Expand() []net.IP { + var res []net.IP + for i := range l { + res = append(res, l[i].Expand()...) + } + return normalize(res) +} + +func normalize(src []net.IP) []net.IP { + sort.Sort(asc(src)) + dst := make([]net.IP, 1, len(src)) + dst[0] = src[0] + for i := range src { + if !dst[len(dst)-1].Equal(src[i]) { + dst = append(dst, src[i]) + } + } + return dst +} diff --git a/vendor/github.com/malfunkt/iprange/ip.y b/vendor/github.com/malfunkt/iprange/ip.y new file mode 100644 index 00000000..2f586d6e --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/ip.y @@ -0,0 +1,109 @@ +%{ + +package iprange + +import ( + "encoding/binary" + "net" + + "github.com/pkg/errors" +) + +type AddressRangeList []AddressRange + +type AddressRange struct { + Min net.IP + Max net.IP +} + +type octetRange struct { + min byte + max byte +} + +%} + +%union { + num byte + octRange octetRange + addrRange AddressRange + result AddressRangeList +} + +%token num +%type address target +%type term octet_range +%type result + +%% + +result: target + { + $$ = append($$, $1) + iplex.(*ipLex).output = $$ + } + | result comma target + { + $$ = append($1, $3) + iplex.(*ipLex).output = $$ + } + +comma: ',' | ',' ' ' + +target: address '/' num + { + mask := net.CIDRMask(int($3), 32) + min := $1.Min.Mask(mask) + maxInt := binary.BigEndian.Uint32([]byte(min)) + + 0xffffffff - + binary.BigEndian.Uint32([]byte(mask)) + maxBytes := make([]byte, 4) + binary.BigEndian.PutUint32(maxBytes, maxInt) + maxBytes = maxBytes[len(maxBytes)-4:] + max := net.IP(maxBytes) + $$ = AddressRange { + Min: min.To4(), + Max: max.To4(), + } + } + | address + { + $$ = $1 + } + +address: term '.' term '.' term '.' term + { + $$ = AddressRange { + Min: net.IPv4($1.min, $3.min, $5.min, $7.min).To4(), + Max: net.IPv4($1.max, $3.max, $5.max, $7.max).To4(), + } + } + +term: num { $$ = octetRange { $1, $1 } } + | '*' { $$ = octetRange { 0, 255 } } + | octet_range { $$ = $1 } + +octet_range: num '-' num { $$ = octetRange { $1, $3 } } + +%% + +// ParseList takes a list of target specifications and returns a list of ranges, +// even if the list contains a single element. +func ParseList(in string) (AddressRangeList, error) { + lex := &ipLex{line: []byte(in)} + errCode := ipParse(lex) + if errCode != 0 || lex.err != nil { + return nil, errors.Wrap(lex.err, "could not parse target") + } + return lex.output, nil +} + +// Parse takes a single target specification and returns a range. It effectively calls ParseList +// and returns the first result +func Parse(in string) (*AddressRange, error) { + l, err := ParseList(in) + if err != nil { + return nil, err + } + return &l[0], nil +} diff --git a/vendor/github.com/malfunkt/iprange/iprange.go b/vendor/github.com/malfunkt/iprange/iprange.go new file mode 100644 index 00000000..fc8c1491 --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/iprange.go @@ -0,0 +1,4 @@ +//go:generate -command yacc go tool yacc +//go:generate yacc -p "ip" ip.y + +package iprange diff --git a/vendor/github.com/malfunkt/iprange/iprange_test.go b/vendor/github.com/malfunkt/iprange/iprange_test.go new file mode 100644 index 00000000..8c2d6e7d --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/iprange_test.go @@ -0,0 +1,172 @@ +package iprange + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleAddress(t *testing.T) { + ipRange, err := Parse("192.168.1.1") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 1).To4(), ipRange.Min) + assert.Equal(t, ipRange.Min, ipRange.Max) +} + +func TestCIDRAddress(t *testing.T) { + { + ipRange, err := Parse("192.168.1.1/24") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 1, 255).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("192.168.2.1/24") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 2, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 2, 255).To4(), ipRange.Max) + + out := ipRange.Expand() + assert.Equal(t, int(0xffffffff-0xffffff00), len(out)-1) + for i := 0; i < 256; i++ { + assert.Equal(t, net.IP([]byte{192, 168, 2, byte(i)}), out[i]) + } + } + + { + ipRange, err := Parse("10.1.2.3/16") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(10, 1, 0, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(10, 1, 255, 255).To4(), ipRange.Max) + + out := ipRange.Expand() + assert.Equal(t, int(0xffffffff-0xffff0000), len(out)-1) + for i := 0; i < 65536; i++ { + assert.Equal(t, net.IP([]byte{10, 1, byte(i / 256), byte(i % 256)}), out[i]) + } + } + + { + ipRange, err := Parse("10.1.2.3/32") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(10, 1, 2, 3).To4(), ipRange.Min) + assert.Equal(t, ipRange.Min, ipRange.Max) + } +} + +func TestWildcardAddress(t *testing.T) { + ipRange, err := Parse("192.168.1.*") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 1, 255).To4(), ipRange.Max) +} + +func TestRangeAddress(t *testing.T) { + { + ipRange, err := Parse("192.168.1.10-20") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 1, 10).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 1, 20).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("192.168.10-20.1") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 10, 1).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 20, 1).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("0-255.1.1.1") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(0, 1, 1, 1).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(255, 1, 1, 1).To4(), ipRange.Max) + } + + { + ipRange, err := Parse("1-2.3-4.5-6.7-8") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(1, 3, 5, 7).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(2, 4, 6, 8).To4(), ipRange.Max) + + out := ipRange.Expand() + + assert.Equal(t, 16, len(out)) + assert.Equal(t, out, []net.IP{ + net.IP([]byte{1, 3, 5, 7}), + net.IP([]byte{1, 3, 5, 8}), + net.IP([]byte{1, 3, 6, 7}), + net.IP([]byte{1, 3, 6, 8}), + net.IP([]byte{1, 4, 5, 7}), + net.IP([]byte{1, 4, 5, 8}), + net.IP([]byte{1, 4, 6, 7}), + net.IP([]byte{1, 4, 6, 8}), + net.IP([]byte{2, 3, 5, 7}), + net.IP([]byte{2, 3, 5, 8}), + net.IP([]byte{2, 3, 6, 7}), + net.IP([]byte{2, 3, 6, 8}), + net.IP([]byte{2, 4, 5, 7}), + net.IP([]byte{2, 4, 5, 8}), + net.IP([]byte{2, 4, 6, 7}), + net.IP([]byte{2, 4, 6, 8}), + }) + } +} + +func TestMixedAddress(t *testing.T) { + ipRange, err := Parse("192.168.10-20.*/25") + assert.Nil(t, err) + + assert.Equal(t, net.IPv4(192, 168, 10, 0).To4(), ipRange.Min) + assert.Equal(t, net.IPv4(192, 168, 10, 127).To4(), ipRange.Max) +} + +func TestList(t *testing.T) { + rangeList, err := ParseList("192.168.1.1, 192.168.1.1/24, 192.168.1.*, 192.168.1.10-20") + assert.Nil(t, err) + assert.Len(t, rangeList, 4) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 1}), rangeList[0].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 1}), rangeList[0].Max) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 0}), rangeList[1].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 255}), rangeList[1].Max) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 0}), rangeList[2].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 255}), rangeList[2].Max) + + assert.Equal(t, net.IP([]byte{192, 168, 1, 10}), rangeList[3].Min) + assert.Equal(t, net.IP([]byte{192, 168, 1, 20}), rangeList[3].Max) +} + +func TestBadAddress(t *testing.T) { + ipRange, err := Parse("192.168.10") + assert.Nil(t, ipRange) + assert.Error(t, err) +} + +func TestBadList(t *testing.T) { + rangeList, err := ParseList("192.168.1,, 192.168.1.1/24, 192.168.1.*, 192.168.1.10-20") + assert.Error(t, err) + assert.Nil(t, rangeList) +} + +func TestListExpansion(t *testing.T) { + rangeList, err := ParseList("192.168.1.10, 192.168.1.1-20, 192.168.1.10/29") + assert.Nil(t, err) + + expanded := rangeList.Expand() + assert.Len(t, expanded, 20) +} diff --git a/vendor/github.com/malfunkt/iprange/lex.go b/vendor/github.com/malfunkt/iprange/lex.go new file mode 100644 index 00000000..818626d5 --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/lex.go @@ -0,0 +1,84 @@ +package iprange + +import ( + "bytes" + "errors" + "log" + "strconv" + "unicode/utf8" +) + +const eof = 0 + +type ipLex struct { + line []byte + peek rune + output AddressRangeList + err error +} + +func (ip *ipLex) Lex(yylval *ipSymType) int { + for { + c := ip.next() + switch c { + case eof: + return eof + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return ip.byte(c, yylval) + default: + return int(c) + } + } +} + +func (ip *ipLex) byte(c rune, yylval *ipSymType) int { + add := func(b *bytes.Buffer, c rune) { + if _, err := b.WriteRune(c); err != nil { + log.Fatalf("WriteRune: %s", err) + } + } + var b bytes.Buffer + add(&b, c) +L: + for { + c = ip.next() + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + add(&b, c) + default: + break L + } + } + if c != eof { + ip.peek = c + } + octet, err := strconv.ParseUint(b.String(), 10, 32) + if err != nil { + log.Printf("badly formatted octet") + return eof + } + yylval.num = byte(octet) + return num +} + +func (ip *ipLex) next() rune { + if ip.peek != eof { + r := ip.peek + ip.peek = eof + return r + } + if len(ip.line) == 0 { + return eof + } + c, size := utf8.DecodeRune(ip.line) + ip.line = ip.line[size:] + if c == utf8.RuneError && size == 1 { + log.Print("invalid utf8") + return ip.next() + } + return c +} + +func (ip *ipLex) Error(s string) { + ip.err = errors.New(s) +} diff --git a/vendor/github.com/malfunkt/iprange/sortip.go b/vendor/github.com/malfunkt/iprange/sortip.go new file mode 100644 index 00000000..f9757554 --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/sortip.go @@ -0,0 +1,27 @@ +package iprange + +import ( + "math/big" + "net" +) + +// Asc implements sorting in ascending order for IP addresses +type asc []net.IP + +func (a asc) Len() int { + return len(a) +} + +func (a asc) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a asc) Less(i, j int) bool { + bigi := big.NewInt(0).SetBytes(a[i]) + bigj := big.NewInt(0).SetBytes(a[j]) + + if bigi.Cmp(bigj) == -1 { + return true + } + return false +} diff --git a/vendor/github.com/malfunkt/iprange/y.go b/vendor/github.com/malfunkt/iprange/y.go new file mode 100644 index 00000000..8c4dc7d5 --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/y.go @@ -0,0 +1,557 @@ +//line ip.y:2 +package iprange + +import __yyfmt__ "fmt" + +//line ip.y:3 +import ( + "encoding/binary" + "net" + + "github.com/pkg/errors" +) + +type AddressRangeList []AddressRange + +type AddressRange struct { + Min net.IP + Max net.IP +} + +type octetRange struct { + min byte + max byte +} + +//line ip.y:26 +type ipSymType struct { + yys int + num byte + octRange octetRange + addrRange AddressRange + result AddressRangeList +} + +const num = 57346 + +var ipToknames = [...]string{ + "$end", + "error", + "$unk", + "num", + "','", + "' '", + "'/'", + "'.'", + "'*'", + "'-'", +} +var ipStatenames = [...]string{} + +const ipEofCode = 1 +const ipErrCode = 2 +const ipInitialStackSize = 16 + +//line ip.y:88 + +// ParseList takes a list of target specifications and returns a list of ranges, +// even if the list contains a single element. +func ParseList(in string) (AddressRangeList, error) { + lex := &ipLex{line: []byte(in)} + errCode := ipParse(lex) + if errCode != 0 || lex.err != nil { + return nil, errors.Wrap(lex.err, "could not parse target") + } + return lex.output, nil +} + +// Parse takes a single target specification and returns a range. It effectively calls ParseList +// and returns the first result +func Parse(in string) (*AddressRange, error) { + l, err := ParseList(in) + if err != nil { + return nil, err + } + return &l[0], nil +} + +//line yacctab:1 +var ipExca = [...]int{ + -1, 1, + 1, -1, + -2, 0, +} + +const ipNprod = 12 +const ipPrivate = 57344 + +var ipTokenNames []string +var ipStates []string + +const ipLast = 22 + +var ipAct = [...]int{ + + 4, 5, 12, 20, 2, 10, 6, 18, 11, 14, + 9, 17, 16, 13, 15, 8, 1, 7, 3, 19, + 0, 21, +} +var ipPact = [...]int{ + + -3, 5, -1000, -2, 0, -8, -1000, -1000, -3, 3, + 10, -3, 7, -1000, -1000, -1000, -1, -1000, -3, -5, + -3, -1000, +} +var ipPgo = [...]int{ + + 0, 18, 4, 0, 17, 16, 15, +} +var ipR1 = [...]int{ + + 0, 5, 5, 6, 6, 2, 2, 1, 3, 3, + 3, 4, +} +var ipR2 = [...]int{ + + 0, 1, 3, 1, 2, 3, 1, 7, 1, 1, + 1, 3, +} +var ipChk = [...]int{ + + -1000, -5, -2, -1, -3, 4, 9, -4, -6, 5, + 7, 8, 10, -2, 6, 4, -3, 4, 8, -3, + 8, -3, +} +var ipDef = [...]int{ + + 0, -2, 1, 6, 0, 8, 9, 10, 0, 3, + 0, 0, 0, 2, 4, 5, 0, 11, 0, 0, + 0, 7, +} +var ipTok1 = [...]int{ + + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 6, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 9, 3, 5, 10, 8, 7, +} +var ipTok2 = [...]int{ + + 2, 3, 4, +} +var ipTok3 = [...]int{ + 0, +} + +var ipErrorMessages = [...]struct { + state int + token int + msg string +}{} + +//line yaccpar:1 + +/* parser for yacc output */ + +var ( + ipDebug = 0 + ipErrorVerbose = false +) + +type ipLexer interface { + Lex(lval *ipSymType) int + Error(s string) +} + +type ipParser interface { + Parse(ipLexer) int + Lookahead() int +} + +type ipParserImpl struct { + lval ipSymType + stack [ipInitialStackSize]ipSymType + char int +} + +func (p *ipParserImpl) Lookahead() int { + return p.char +} + +func ipNewParser() ipParser { + return &ipParserImpl{} +} + +const ipFlag = -1000 + +func ipTokname(c int) string { + if c >= 1 && c-1 < len(ipToknames) { + if ipToknames[c-1] != "" { + return ipToknames[c-1] + } + } + return __yyfmt__.Sprintf("tok-%v", c) +} + +func ipStatname(s int) string { + if s >= 0 && s < len(ipStatenames) { + if ipStatenames[s] != "" { + return ipStatenames[s] + } + } + return __yyfmt__.Sprintf("state-%v", s) +} + +func ipErrorMessage(state, lookAhead int) string { + const TOKSTART = 4 + + if !ipErrorVerbose { + return "syntax error" + } + + for _, e := range ipErrorMessages { + if e.state == state && e.token == lookAhead { + return "syntax error: " + e.msg + } + } + + res := "syntax error: unexpected " + ipTokname(lookAhead) + + // To match Bison, suggest at most four expected tokens. + expected := make([]int, 0, 4) + + // Look for shiftable tokens. + base := ipPact[state] + for tok := TOKSTART; tok-1 < len(ipToknames); tok++ { + if n := base + tok; n >= 0 && n < ipLast && ipChk[ipAct[n]] == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if ipDef[state] == -2 { + i := 0 + for ipExca[i] != -1 || ipExca[i+1] != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; ipExca[i] >= 0; i += 2 { + tok := ipExca[i] + if tok < TOKSTART || ipExca[i+1] == 0 { + continue + } + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + + // If the default action is to accept or reduce, give up. + if ipExca[i+1] != 0 { + return res + } + } + + for i, tok := range expected { + if i == 0 { + res += ", expecting " + } else { + res += " or " + } + res += ipTokname(tok) + } + return res +} + +func iplex1(lex ipLexer, lval *ipSymType) (char, token int) { + token = 0 + char = lex.Lex(lval) + if char <= 0 { + token = ipTok1[0] + goto out + } + if char < len(ipTok1) { + token = ipTok1[char] + goto out + } + if char >= ipPrivate { + if char < ipPrivate+len(ipTok2) { + token = ipTok2[char-ipPrivate] + goto out + } + } + for i := 0; i < len(ipTok3); i += 2 { + token = ipTok3[i+0] + if token == char { + token = ipTok3[i+1] + goto out + } + } + +out: + if token == 0 { + token = ipTok2[1] /* unknown char */ + } + if ipDebug >= 3 { + __yyfmt__.Printf("lex %s(%d)\n", ipTokname(token), uint(char)) + } + return char, token +} + +func ipParse(iplex ipLexer) int { + return ipNewParser().Parse(iplex) +} + +func (iprcvr *ipParserImpl) Parse(iplex ipLexer) int { + var ipn int + var ipVAL ipSymType + var ipDollar []ipSymType + _ = ipDollar // silence set and not used + ipS := iprcvr.stack[:] + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + ipstate := 0 + iprcvr.char = -1 + iptoken := -1 // iprcvr.char translated into internal numbering + defer func() { + // Make sure we report no lookahead when not parsing. + ipstate = -1 + iprcvr.char = -1 + iptoken = -1 + }() + ipp := -1 + goto ipstack + +ret0: + return 0 + +ret1: + return 1 + +ipstack: + /* put a state and value onto the stack */ + if ipDebug >= 4 { + __yyfmt__.Printf("char %v in %v\n", ipTokname(iptoken), ipStatname(ipstate)) + } + + ipp++ + if ipp >= len(ipS) { + nyys := make([]ipSymType, len(ipS)*2) + copy(nyys, ipS) + ipS = nyys + } + ipS[ipp] = ipVAL + ipS[ipp].yys = ipstate + +ipnewstate: + ipn = ipPact[ipstate] + if ipn <= ipFlag { + goto ipdefault /* simple state */ + } + if iprcvr.char < 0 { + iprcvr.char, iptoken = iplex1(iplex, &iprcvr.lval) + } + ipn += iptoken + if ipn < 0 || ipn >= ipLast { + goto ipdefault + } + ipn = ipAct[ipn] + if ipChk[ipn] == iptoken { /* valid shift */ + iprcvr.char = -1 + iptoken = -1 + ipVAL = iprcvr.lval + ipstate = ipn + if Errflag > 0 { + Errflag-- + } + goto ipstack + } + +ipdefault: + /* default state action */ + ipn = ipDef[ipstate] + if ipn == -2 { + if iprcvr.char < 0 { + iprcvr.char, iptoken = iplex1(iplex, &iprcvr.lval) + } + + /* look through exception table */ + xi := 0 + for { + if ipExca[xi+0] == -1 && ipExca[xi+1] == ipstate { + break + } + xi += 2 + } + for xi += 2; ; xi += 2 { + ipn = ipExca[xi+0] + if ipn < 0 || ipn == iptoken { + break + } + } + ipn = ipExca[xi+1] + if ipn < 0 { + goto ret0 + } + } + if ipn == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + iplex.Error(ipErrorMessage(ipstate, iptoken)) + Nerrs++ + if ipDebug >= 1 { + __yyfmt__.Printf("%s", ipStatname(ipstate)) + __yyfmt__.Printf(" saw %s\n", ipTokname(iptoken)) + } + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for ipp >= 0 { + ipn = ipPact[ipS[ipp].yys] + ipErrCode + if ipn >= 0 && ipn < ipLast { + ipstate = ipAct[ipn] /* simulate a shift of "error" */ + if ipChk[ipstate] == ipErrCode { + goto ipstack + } + } + + /* the current p has no shift on "error", pop stack */ + if ipDebug >= 2 { + __yyfmt__.Printf("error recovery pops state %d\n", ipS[ipp].yys) + } + ipp-- + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if ipDebug >= 2 { + __yyfmt__.Printf("error recovery discards %s\n", ipTokname(iptoken)) + } + if iptoken == ipEofCode { + goto ret1 + } + iprcvr.char = -1 + iptoken = -1 + goto ipnewstate /* try again in the same state */ + } + } + + /* reduction by production ipn */ + if ipDebug >= 2 { + __yyfmt__.Printf("reduce %v in:\n\t%v\n", ipn, ipStatname(ipstate)) + } + + ipnt := ipn + ippt := ipp + _ = ippt // guard against "declared and not used" + + ipp -= ipR2[ipn] + // ipp is now the index of $0. Perform the default action. Iff the + // reduced production is ε, $1 is possibly out of range. + if ipp+1 >= len(ipS) { + nyys := make([]ipSymType, len(ipS)*2) + copy(nyys, ipS) + ipS = nyys + } + ipVAL = ipS[ipp+1] + + /* consult goto table to find next state */ + ipn = ipR1[ipn] + ipg := ipPgo[ipn] + ipj := ipg + ipS[ipp].yys + 1 + + if ipj >= ipLast { + ipstate = ipAct[ipg] + } else { + ipstate = ipAct[ipj] + if ipChk[ipstate] != -ipn { + ipstate = ipAct[ipg] + } + } + // dummy call; replaced with literal code + switch ipnt { + + case 1: + ipDollar = ipS[ippt-1 : ippt+1] + //line ip.y:41 + { + ipVAL.result = append(ipVAL.result, ipDollar[1].addrRange) + iplex.(*ipLex).output = ipVAL.result + } + case 2: + ipDollar = ipS[ippt-3 : ippt+1] + //line ip.y:46 + { + ipVAL.result = append(ipDollar[1].result, ipDollar[3].addrRange) + iplex.(*ipLex).output = ipVAL.result + } + case 5: + ipDollar = ipS[ippt-3 : ippt+1] + //line ip.y:54 + { + mask := net.CIDRMask(int(ipDollar[3].num), 32) + min := ipDollar[1].addrRange.Min.Mask(mask) + maxInt := binary.BigEndian.Uint32([]byte(min)) + + 0xffffffff - + binary.BigEndian.Uint32([]byte(mask)) + maxBytes := make([]byte, 4) + binary.BigEndian.PutUint32(maxBytes, maxInt) + maxBytes = maxBytes[len(maxBytes)-4:] + max := net.IP(maxBytes) + ipVAL.addrRange = AddressRange{ + Min: min.To4(), + Max: max.To4(), + } + } + case 6: + ipDollar = ipS[ippt-1 : ippt+1] + //line ip.y:70 + { + ipVAL.addrRange = ipDollar[1].addrRange + } + case 7: + ipDollar = ipS[ippt-7 : ippt+1] + //line ip.y:75 + { + ipVAL.addrRange = AddressRange{ + Min: net.IPv4(ipDollar[1].octRange.min, ipDollar[3].octRange.min, ipDollar[5].octRange.min, ipDollar[7].octRange.min).To4(), + Max: net.IPv4(ipDollar[1].octRange.max, ipDollar[3].octRange.max, ipDollar[5].octRange.max, ipDollar[7].octRange.max).To4(), + } + } + case 8: + ipDollar = ipS[ippt-1 : ippt+1] + //line ip.y:82 + { + ipVAL.octRange = octetRange{ipDollar[1].num, ipDollar[1].num} + } + case 9: + ipDollar = ipS[ippt-1 : ippt+1] + //line ip.y:83 + { + ipVAL.octRange = octetRange{0, 255} + } + case 10: + ipDollar = ipS[ippt-1 : ippt+1] + //line ip.y:84 + { + ipVAL.octRange = ipDollar[1].octRange + } + case 11: + ipDollar = ipS[ippt-3 : ippt+1] + //line ip.y:86 + { + ipVAL.octRange = octetRange{ipDollar[1].num, ipDollar[3].num} + } + } + goto ipstack /* stack new state and value */ +} diff --git a/vendor/github.com/malfunkt/iprange/y.output b/vendor/github.com/malfunkt/iprange/y.output new file mode 100644 index 00000000..42877a3c --- /dev/null +++ b/vendor/github.com/malfunkt/iprange/y.output @@ -0,0 +1,185 @@ + +state 0 + $accept: .result $end + + num shift 5 + '*' shift 6 + . error + + address goto 3 + target goto 2 + term goto 4 + octet_range goto 7 + result goto 1 + +state 1 + $accept: result.$end + result: result.comma target + + $end accept + ',' shift 9 + . error + + comma goto 8 + +state 2 + result: target. (1) + + . reduce 1 (src line 40) + + +state 3 + target: address.'/' num + target: address. (6) + + '/' shift 10 + . reduce 6 (src line 69) + + +state 4 + address: term.'.' term '.' term '.' term + + '.' shift 11 + . error + + +state 5 + term: num. (8) + octet_range: num.'-' num + + '-' shift 12 + . reduce 8 (src line 82) + + +state 6 + term: '*'. (9) + + . reduce 9 (src line 83) + + +state 7 + term: octet_range. (10) + + . reduce 10 (src line 84) + + +state 8 + result: result comma.target + + num shift 5 + '*' shift 6 + . error + + address goto 3 + target goto 13 + term goto 4 + octet_range goto 7 + +state 9 + comma: ','. (3) + comma: ','.' ' + + ' ' shift 14 + . reduce 3 (src line 51) + + +state 10 + target: address '/'.num + + num shift 15 + . error + + +state 11 + address: term '.'.term '.' term '.' term + + num shift 5 + '*' shift 6 + . error + + term goto 16 + octet_range goto 7 + +state 12 + octet_range: num '-'.num + + num shift 17 + . error + + +state 13 + result: result comma target. (2) + + . reduce 2 (src line 45) + + +state 14 + comma: ',' ' '. (4) + + . reduce 4 (src line 51) + + +state 15 + target: address '/' num. (5) + + . reduce 5 (src line 53) + + +state 16 + address: term '.' term.'.' term '.' term + + '.' shift 18 + . error + + +state 17 + octet_range: num '-' num. (11) + + . reduce 11 (src line 86) + + +state 18 + address: term '.' term '.'.term '.' term + + num shift 5 + '*' shift 6 + . error + + term goto 19 + octet_range goto 7 + +state 19 + address: term '.' term '.' term.'.' term + + '.' shift 20 + . error + + +state 20 + address: term '.' term '.' term '.'.term + + num shift 5 + '*' shift 6 + . error + + term goto 21 + octet_range goto 7 + +state 21 + address: term '.' term '.' term '.' term. (7) + + . reduce 7 (src line 74) + + +10 terminals, 7 nonterminals +12 grammar rules, 22/2000 states +0 shift/reduce, 0 reduce/reduce conflicts reported +56 working sets used +memory: parser 15/30000 +5 extra closures +19 shift entries, 1 exceptions +10 goto entries +6 entries saved by goto default +Optimizer space used: output 22/30000 +22 table entries, 1 zero +maximum spread: 10, maximum offset: 20 diff --git a/vendor/github.com/mattn/go-colorable/.travis.yml b/vendor/github.com/mattn/go-colorable/.travis.yml new file mode 100644 index 00000000..42768b8b --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip + +sudo: false + +script: + - go test -v diff --git a/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/mattn/go-colorable/LICENSE new file mode 100644 index 00000000..91b5cef3 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/mattn/go-colorable/README.md new file mode 100644 index 00000000..e84226a7 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/README.md @@ -0,0 +1,43 @@ +# go-colorable + +Colorable writer for windows. + +For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) +This package is possible to handle escape sequence for ansi color on windows. + +## Too Bad! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) + + +## So Good! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) + +## Usage + +```go +logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) +logrus.SetOutput(colorable.NewColorableStdout()) + +logrus.Info("succeeded") +logrus.Warn("not correct") +logrus.Error("something error") +logrus.Fatal("panic") +``` + +You can compile above code on non-windows OSs. + +## Installation + +``` +$ go get github.com/mattn/go-colorable +``` + +# License + +MIT + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-colorable/_example/escape-seq/main.go b/vendor/github.com/mattn/go-colorable/_example/escape-seq/main.go new file mode 100644 index 00000000..8cbcb909 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/_example/escape-seq/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "bufio" + "fmt" + + "github.com/mattn/go-colorable" +) + +func main() { + stdOut := bufio.NewWriter(colorable.NewColorableStdout()) + + fmt.Fprint(stdOut, "\x1B[3GMove to 3rd Column\n") + fmt.Fprint(stdOut, "\x1B[1;2HMove to 2nd Column on 1st Line\n") + stdOut.Flush() +} diff --git a/vendor/github.com/mattn/go-colorable/_example/logrus/main.go b/vendor/github.com/mattn/go-colorable/_example/logrus/main.go new file mode 100644 index 00000000..0da0906c --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/_example/logrus/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/Sirupsen/logrus" + "github.com/mattn/go-colorable" +) + +func main() { + logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) + logrus.SetOutput(colorable.NewColorableStdout()) + + logrus.Info("succeeded") + logrus.Warn("not correct") + logrus.Error("something error") + logrus.Fatal("panic") +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go new file mode 100644 index 00000000..a7fe19a8 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -0,0 +1,27 @@ +// +build !windows + +package colorable + +import ( + "io" + "os" +) + +// NewColorable return new instance of Writer which handle escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr return new instance of Writer which handle escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_test.go b/vendor/github.com/mattn/go-colorable/colorable_test.go new file mode 100644 index 00000000..49a4ffa9 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_test.go @@ -0,0 +1,30 @@ +package colorable + +import ( + "bytes" + "testing" +) + +// checkEncoding checks that colorable is output encoding agnostic as long as +// the encoding is a superset of ASCII. This implies that one byte not part of +// an ANSI sequence must give exactly one byte in output +func checkEncoding(t *testing.T, data []byte) { + // Send non-UTF8 data to colorable + b := bytes.NewBuffer(make([]byte, 0, 10)) + if b.Len() != 0 { + t.FailNow() + } + // TODO move colorable wrapping outside the test + c := NewNonColorable(b) + c.Write(data) + if b.Len() != len(data) { + t.Fatalf("%d bytes expected, got %d", len(data), b.Len()) + } +} + +func TestEncoding(t *testing.T) { + checkEncoding(t, []byte{}) // Empty + checkEncoding(t, []byte(`abc`)) // "abc" + checkEncoding(t, []byte(`é`)) // "é" in UTF-8 + checkEncoding(t, []byte{233}) // 'é' in Latin-1 +} diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go new file mode 100644 index 00000000..628ad904 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -0,0 +1,820 @@ +package colorable + +import ( + "bytes" + "io" + "math" + "os" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") +) + +type Writer struct { + out io.Writer + handle syscall.Handle + lastbuf bytes.Buffer + oldattr word + oldpos coord +} + +// NewColorable return new instance of Writer which handle escape sequence from File. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + if isatty.IsTerminal(file.Fd()) { + var csbi consoleScreenBufferInfo + handle := syscall.Handle(file.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + } else { + return file + } +} + +// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. +func NewColorableStdout() io.Writer { + return NewColorable(os.Stdout) +} + +// NewColorableStderr return new instance of Writer which handle escape sequence for stderr. +func NewColorableStderr() io.Writer { + return NewColorable(os.Stderr) +} + +var color256 = map[int]int{ + 0: 0x000000, + 1: 0x800000, + 2: 0x008000, + 3: 0x808000, + 4: 0x000080, + 5: 0x800080, + 6: 0x008080, + 7: 0xc0c0c0, + 8: 0x808080, + 9: 0xff0000, + 10: 0x00ff00, + 11: 0xffff00, + 12: 0x0000ff, + 13: 0xff00ff, + 14: 0x00ffff, + 15: 0xffffff, + 16: 0x000000, + 17: 0x00005f, + 18: 0x000087, + 19: 0x0000af, + 20: 0x0000d7, + 21: 0x0000ff, + 22: 0x005f00, + 23: 0x005f5f, + 24: 0x005f87, + 25: 0x005faf, + 26: 0x005fd7, + 27: 0x005fff, + 28: 0x008700, + 29: 0x00875f, + 30: 0x008787, + 31: 0x0087af, + 32: 0x0087d7, + 33: 0x0087ff, + 34: 0x00af00, + 35: 0x00af5f, + 36: 0x00af87, + 37: 0x00afaf, + 38: 0x00afd7, + 39: 0x00afff, + 40: 0x00d700, + 41: 0x00d75f, + 42: 0x00d787, + 43: 0x00d7af, + 44: 0x00d7d7, + 45: 0x00d7ff, + 46: 0x00ff00, + 47: 0x00ff5f, + 48: 0x00ff87, + 49: 0x00ffaf, + 50: 0x00ffd7, + 51: 0x00ffff, + 52: 0x5f0000, + 53: 0x5f005f, + 54: 0x5f0087, + 55: 0x5f00af, + 56: 0x5f00d7, + 57: 0x5f00ff, + 58: 0x5f5f00, + 59: 0x5f5f5f, + 60: 0x5f5f87, + 61: 0x5f5faf, + 62: 0x5f5fd7, + 63: 0x5f5fff, + 64: 0x5f8700, + 65: 0x5f875f, + 66: 0x5f8787, + 67: 0x5f87af, + 68: 0x5f87d7, + 69: 0x5f87ff, + 70: 0x5faf00, + 71: 0x5faf5f, + 72: 0x5faf87, + 73: 0x5fafaf, + 74: 0x5fafd7, + 75: 0x5fafff, + 76: 0x5fd700, + 77: 0x5fd75f, + 78: 0x5fd787, + 79: 0x5fd7af, + 80: 0x5fd7d7, + 81: 0x5fd7ff, + 82: 0x5fff00, + 83: 0x5fff5f, + 84: 0x5fff87, + 85: 0x5fffaf, + 86: 0x5fffd7, + 87: 0x5fffff, + 88: 0x870000, + 89: 0x87005f, + 90: 0x870087, + 91: 0x8700af, + 92: 0x8700d7, + 93: 0x8700ff, + 94: 0x875f00, + 95: 0x875f5f, + 96: 0x875f87, + 97: 0x875faf, + 98: 0x875fd7, + 99: 0x875fff, + 100: 0x878700, + 101: 0x87875f, + 102: 0x878787, + 103: 0x8787af, + 104: 0x8787d7, + 105: 0x8787ff, + 106: 0x87af00, + 107: 0x87af5f, + 108: 0x87af87, + 109: 0x87afaf, + 110: 0x87afd7, + 111: 0x87afff, + 112: 0x87d700, + 113: 0x87d75f, + 114: 0x87d787, + 115: 0x87d7af, + 116: 0x87d7d7, + 117: 0x87d7ff, + 118: 0x87ff00, + 119: 0x87ff5f, + 120: 0x87ff87, + 121: 0x87ffaf, + 122: 0x87ffd7, + 123: 0x87ffff, + 124: 0xaf0000, + 125: 0xaf005f, + 126: 0xaf0087, + 127: 0xaf00af, + 128: 0xaf00d7, + 129: 0xaf00ff, + 130: 0xaf5f00, + 131: 0xaf5f5f, + 132: 0xaf5f87, + 133: 0xaf5faf, + 134: 0xaf5fd7, + 135: 0xaf5fff, + 136: 0xaf8700, + 137: 0xaf875f, + 138: 0xaf8787, + 139: 0xaf87af, + 140: 0xaf87d7, + 141: 0xaf87ff, + 142: 0xafaf00, + 143: 0xafaf5f, + 144: 0xafaf87, + 145: 0xafafaf, + 146: 0xafafd7, + 147: 0xafafff, + 148: 0xafd700, + 149: 0xafd75f, + 150: 0xafd787, + 151: 0xafd7af, + 152: 0xafd7d7, + 153: 0xafd7ff, + 154: 0xafff00, + 155: 0xafff5f, + 156: 0xafff87, + 157: 0xafffaf, + 158: 0xafffd7, + 159: 0xafffff, + 160: 0xd70000, + 161: 0xd7005f, + 162: 0xd70087, + 163: 0xd700af, + 164: 0xd700d7, + 165: 0xd700ff, + 166: 0xd75f00, + 167: 0xd75f5f, + 168: 0xd75f87, + 169: 0xd75faf, + 170: 0xd75fd7, + 171: 0xd75fff, + 172: 0xd78700, + 173: 0xd7875f, + 174: 0xd78787, + 175: 0xd787af, + 176: 0xd787d7, + 177: 0xd787ff, + 178: 0xd7af00, + 179: 0xd7af5f, + 180: 0xd7af87, + 181: 0xd7afaf, + 182: 0xd7afd7, + 183: 0xd7afff, + 184: 0xd7d700, + 185: 0xd7d75f, + 186: 0xd7d787, + 187: 0xd7d7af, + 188: 0xd7d7d7, + 189: 0xd7d7ff, + 190: 0xd7ff00, + 191: 0xd7ff5f, + 192: 0xd7ff87, + 193: 0xd7ffaf, + 194: 0xd7ffd7, + 195: 0xd7ffff, + 196: 0xff0000, + 197: 0xff005f, + 198: 0xff0087, + 199: 0xff00af, + 200: 0xff00d7, + 201: 0xff00ff, + 202: 0xff5f00, + 203: 0xff5f5f, + 204: 0xff5f87, + 205: 0xff5faf, + 206: 0xff5fd7, + 207: 0xff5fff, + 208: 0xff8700, + 209: 0xff875f, + 210: 0xff8787, + 211: 0xff87af, + 212: 0xff87d7, + 213: 0xff87ff, + 214: 0xffaf00, + 215: 0xffaf5f, + 216: 0xffaf87, + 217: 0xffafaf, + 218: 0xffafd7, + 219: 0xffafff, + 220: 0xffd700, + 221: 0xffd75f, + 222: 0xffd787, + 223: 0xffd7af, + 224: 0xffd7d7, + 225: 0xffd7ff, + 226: 0xffff00, + 227: 0xffff5f, + 228: 0xffff87, + 229: 0xffffaf, + 230: 0xffffd7, + 231: 0xffffff, + 232: 0x080808, + 233: 0x121212, + 234: 0x1c1c1c, + 235: 0x262626, + 236: 0x303030, + 237: 0x3a3a3a, + 238: 0x444444, + 239: 0x4e4e4e, + 240: 0x585858, + 241: 0x626262, + 242: 0x6c6c6c, + 243: 0x767676, + 244: 0x808080, + 245: 0x8a8a8a, + 246: 0x949494, + 247: 0x9e9e9e, + 248: 0xa8a8a8, + 249: 0xb2b2b2, + 250: 0xbcbcbc, + 251: 0xc6c6c6, + 252: 0xd0d0d0, + 253: 0xdadada, + 254: 0xe4e4e4, + 255: 0xeeeeee, +} + +// Write write data on console +func (w *Writer) Write(data []byte) (n int, err error) { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + er := bytes.NewReader(data) + var bw [1]byte +loop: + for { + r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + if r1 == 0 { + break loop + } + + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + break loop + } + if c2 != 0x5b { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + continue + } + + var buf bytes.Buffer + var m byte + for { + c, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + w.lastbuf.Write(buf.Bytes()) + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + m = c + break + } + buf.Write([]byte(string(c))) + } + + var csbi consoleScreenBufferInfo + switch m { + case 'A': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'B': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'C': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'D': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + if n, err = strconv.Atoi(buf.String()); err == nil { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + } + case 'E': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'F': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'G': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = short(n - 1) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'H': + token := strings.Split(buf.String(), ";") + if len(token) != 2 { + continue + } + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + n2, err := strconv.Atoi(token[1]) + if err != nil { + continue + } + csbi.cursorPosition.x = short(n2 - 1) + csbi.cursorPosition.y = short(n1 - 1) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'J': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'K': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'm': + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + attr := csbi.attributes + cs := buf.String() + if cs == "" { + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) + continue + } + token := strings.Split(cs, ";") + for i := 0; i < len(token); i++ { + ns := token[i] + if n, err = strconv.Atoi(ns); err == nil { + switch { + case n == 0 || n == 100: + attr = w.oldattr + case 1 <= n && n <= 5: + attr |= foregroundIntensity + case n == 7: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 22 == n || n == 25 || n == 25: + attr |= foregroundIntensity + case n == 27: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 30 <= n && n <= 37: + attr &= backgroundMask + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case n == 38: // set foreground color. + if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256foreAttr == nil { + n256setup() + } + attr &= backgroundMask + attr |= n256foreAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & backgroundMask) + } + case n == 39: // reset foreground color. + attr &= backgroundMask + attr |= w.oldattr & foregroundMask + case 40 <= n && n <= 47: + attr &= foregroundMask + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case n == 48: // set background color. + if i < len(token)-2 && token[i+1] == "5" { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256backAttr == nil { + n256setup() + } + attr &= foregroundMask + attr |= n256backAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & foregroundMask) + } + case n == 49: // reset foreground color. + attr &= foregroundMask + attr |= w.oldattr & backgroundMask + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) + } + } + case 'h': + cs := buf.String() + if cs == "?25" { + var ci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + } + case 'l': + cs := buf.String() + if cs == "?25" { + var ci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + } + case 's': + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + case 'u': + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + } + } + return len(data) - w.lastbuf.Len(), nil +} + +type consoleColor struct { + rgb int + red bool + green bool + blue bool + intensity bool +} + +func (c consoleColor) foregroundAttr() (attr word) { + if c.red { + attr |= foregroundRed + } + if c.green { + attr |= foregroundGreen + } + if c.blue { + attr |= foregroundBlue + } + if c.intensity { + attr |= foregroundIntensity + } + return +} + +func (c consoleColor) backgroundAttr() (attr word) { + if c.red { + attr |= backgroundRed + } + if c.green { + attr |= backgroundGreen + } + if c.blue { + attr |= backgroundBlue + } + if c.intensity { + attr |= backgroundIntensity + } + return +} + +var color16 = []consoleColor{ + consoleColor{0x000000, false, false, false, false}, + consoleColor{0x000080, false, false, true, false}, + consoleColor{0x008000, false, true, false, false}, + consoleColor{0x008080, false, true, true, false}, + consoleColor{0x800000, true, false, false, false}, + consoleColor{0x800080, true, false, true, false}, + consoleColor{0x808000, true, true, false, false}, + consoleColor{0xc0c0c0, true, true, true, false}, + consoleColor{0x808080, false, false, false, true}, + consoleColor{0x0000ff, false, false, true, true}, + consoleColor{0x00ff00, false, true, false, true}, + consoleColor{0x00ffff, false, true, true, true}, + consoleColor{0xff0000, true, false, false, true}, + consoleColor{0xff00ff, true, false, true, true}, + consoleColor{0xffff00, true, true, false, true}, + consoleColor{0xffffff, true, true, true, true}, +} + +type hsv struct { + h, s, v float32 +} + +func (a hsv) dist(b hsv) float32 { + dh := a.h - b.h + switch { + case dh > 0.5: + dh = 1 - dh + case dh < -0.5: + dh = -1 - dh + } + ds := a.s - b.s + dv := a.v - b.v + return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) +} + +func toHSV(rgb int) hsv { + r, g, b := float32((rgb&0xFF0000)>>16)/256.0, + float32((rgb&0x00FF00)>>8)/256.0, + float32(rgb&0x0000FF)/256.0 + min, max := minmax3f(r, g, b) + h := max - min + if h > 0 { + if max == r { + h = (g - b) / h + if h < 0 { + h += 6 + } + } else if max == g { + h = 2 + (b-r)/h + } else { + h = 4 + (r-g)/h + } + } + h /= 6.0 + s := max - min + if max != 0 { + s /= max + } + v := max + return hsv{h: h, s: s, v: v} +} + +type hsvTable []hsv + +func toHSVTable(rgbTable []consoleColor) hsvTable { + t := make(hsvTable, len(rgbTable)) + for i, c := range rgbTable { + t[i] = toHSV(c.rgb) + } + return t +} + +func (t hsvTable) find(rgb int) consoleColor { + hsv := toHSV(rgb) + n := 7 + l := float32(5.0) + for i, p := range t { + d := hsv.dist(p) + if d < l { + l, n = d, i + } + } + return color16[n] +} + +func minmax3f(a, b, c float32) (min, max float32) { + if a < b { + if b < c { + return a, c + } else if a < c { + return a, b + } else { + return c, b + } + } else { + if a < c { + return b, c + } else if b < c { + return b, a + } else { + return c, a + } + } +} + +var n256foreAttr []word +var n256backAttr []word + +func n256setup() { + n256foreAttr = make([]word, 256) + n256backAttr = make([]word, 256) + t := toHSVTable(color16) + for i, rgb := range color256 { + c := t.find(rgb) + n256foreAttr[i] = c.foregroundAttr() + n256backAttr[i] = c.backgroundAttr() + } +} diff --git a/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/mattn/go-colorable/noncolorable.go new file mode 100644 index 00000000..ca588c78 --- /dev/null +++ b/vendor/github.com/mattn/go-colorable/noncolorable.go @@ -0,0 +1,61 @@ +package colorable + +import ( + "bytes" + "io" +) + +// NonColorable hold writer but remove escape sequence. +type NonColorable struct { + out io.Writer + lastbuf bytes.Buffer +} + +// NewNonColorable return new instance of Writer which remove escape sequence from Writer. +func NewNonColorable(w io.Writer) io.Writer { + return &NonColorable{out: w} +} + +// Write write data on console +func (w *NonColorable) Write(data []byte) (n int, err error) { + er := bytes.NewReader(data) + var bw [1]byte +loop: + for { + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + break loop + } + if c2 != 0x5b { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + continue + } + + var buf bytes.Buffer + for { + c, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + w.lastbuf.Write(buf.Bytes()) + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + break + } + buf.Write([]byte(string(c))) + } + } + return len(data) - w.lastbuf.Len(), nil +} diff --git a/vendor/github.com/mattn/go-isatty/.travis.yml b/vendor/github.com/mattn/go-isatty/.travis.yml new file mode 100644 index 00000000..b9f8b239 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/.travis.yml @@ -0,0 +1,9 @@ +language: go +go: + - tip + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5 diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 00000000..65dc692b --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO + +MIT License (Expat) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md new file mode 100644 index 00000000..1e69004b --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/README.md @@ -0,0 +1,50 @@ +# go-isatty + +[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) +[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty) +[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) + +isatty for golang + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + fmt.Println("Is Cygwin/MSYS2 Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} +``` + +## Installation + +``` +$ go get github.com/mattn/go-isatty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) + +## Thanks + +* k-takata: base idea for IsCygwinTerminal + + https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go new file mode 100644 index 00000000..17d4f90e --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/doc.go @@ -0,0 +1,2 @@ +// Package isatty implements interface to isatty +package isatty diff --git a/vendor/github.com/mattn/go-isatty/example_test.go b/vendor/github.com/mattn/go-isatty/example_test.go new file mode 100644 index 00000000..fa8f7e74 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/example_test.go @@ -0,0 +1,18 @@ +package isatty_test + +import ( + "fmt" + "os" + + "github.com/mattn/go-isatty" +) + +func Example() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + fmt.Println("Is Cygwin/MSYS2 Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_appengine.go b/vendor/github.com/mattn/go-isatty/isatty_appengine.go new file mode 100644 index 00000000..9584a988 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_appengine.go @@ -0,0 +1,15 @@ +// +build appengine + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on on appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go new file mode 100644 index 00000000..42f2514d --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -0,0 +1,18 @@ +// +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TIOCGETA + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_linux.go b/vendor/github.com/mattn/go-isatty/isatty_linux.go new file mode 100644 index 00000000..7384cf99 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_linux.go @@ -0,0 +1,18 @@ +// +build linux +// +build !appengine,!ppc64,!ppc64le + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TCGETS + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go b/vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go new file mode 100644 index 00000000..44e5d213 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go @@ -0,0 +1,19 @@ +// +build linux +// +build ppc64 ppc64le + +package isatty + +import ( + "unsafe" + + syscall "golang.org/x/sys/unix" +) + +const ioctlReadTermios = syscall.TCGETS + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go new file mode 100644 index 00000000..ff4de3d9 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -0,0 +1,10 @@ +// +build !windows +// +build !appengine + +package isatty + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others_test.go b/vendor/github.com/mattn/go-isatty/isatty_others_test.go new file mode 100644 index 00000000..a2091cf4 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_others_test.go @@ -0,0 +1,19 @@ +// +build !windows + +package isatty + +import ( + "os" + "testing" +) + +func TestTerminal(t *testing.T) { + // test for non-panic + IsTerminal(os.Stdout.Fd()) +} + +func TestCygwinPipeName(t *testing.T) { + if IsCygwinTerminal(os.Stdout.Fd()) { + t.Fatal("should be false always") + } +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go new file mode 100644 index 00000000..1f0c6bf5 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -0,0 +1,16 @@ +// +build solaris +// +build !appengine + +package isatty + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c +func IsTerminal(fd uintptr) bool { + var termio unix.Termio + err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) + return err == nil +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go new file mode 100644 index 00000000..af51cbca --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -0,0 +1,94 @@ +// +build windows +// +build !appengine + +package isatty + +import ( + "strings" + "syscall" + "unicode/utf16" + "unsafe" +) + +const ( + fileNameInfo uintptr = 2 + fileTypePipe = 3 +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") + procGetFileType = kernel32.NewProc("GetFileType") +) + +func init() { + // Check if GetFileInformationByHandleEx is available. + if procGetFileInformationByHandleEx.Find() != nil { + procGetFileInformationByHandleEx = nil + } +} + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// Check pipe name is used for cygwin/msys2 pty. +// Cygwin/MSYS2 PTY has a name like: +// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +func isCygwinPipeName(name string) bool { + token := strings.Split(name, "-") + if len(token) < 5 { + return false + } + + if token[0] != `\msys` && token[0] != `\cygwin` { + return false + } + + if token[1] == "" { + return false + } + + if !strings.HasPrefix(token[2], "pty") { + return false + } + + if token[3] != `from` && token[3] != `to` { + return false + } + + if token[4] != "master" { + return false + } + + return true +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. +func IsCygwinTerminal(fd uintptr) bool { + if procGetFileInformationByHandleEx == nil { + return false + } + + // Cygwin/msys's pty is a pipe. + ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) + if ft != fileTypePipe || e != 0 { + return false + } + + var buf [2 + syscall.MAX_PATH]uint16 + r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), + 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), + uintptr(len(buf)*2), 0, 0) + if r == 0 || e != 0 { + return false + } + + l := *(*uint32)(unsafe.Pointer(&buf)) + return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows_test.go b/vendor/github.com/mattn/go-isatty/isatty_windows_test.go new file mode 100644 index 00000000..777e8a60 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_windows_test.go @@ -0,0 +1,35 @@ +// +build windows + +package isatty + +import ( + "testing" +) + +func TestCygwinPipeName(t *testing.T) { + tests := []struct { + name string + result bool + }{ + {``, false}, + {`\msys-`, false}, + {`\cygwin-----`, false}, + {`\msys-x-PTY5-pty1-from-master`, false}, + {`\cygwin-x-PTY5-from-master`, false}, + {`\cygwin-x-pty2-from-toaster`, false}, + {`\cygwin--pty2-from-master`, false}, + {`\\cygwin-x-pty2-from-master`, false}, + {`\cygwin-x-pty2-from-master-`, true}, // for the feature + {`\cygwin-e022582115c10879-pty4-from-master`, true}, + {`\msys-e022582115c10879-pty4-to-master`, true}, + {`\cygwin-e022582115c10879-pty4-to-master`, true}, + } + + for _, test := range tests { + want := test.result + got := isCygwinPipeName(test.name) + if want != got { + t.Fatalf("isatty(%q): got %v, want %v:", test.name, got, want) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/.gitignore b/vendor/github.com/mdlayher/dhcp6/.gitignore new file mode 100644 index 00000000..b3087b09 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/.gitignore @@ -0,0 +1 @@ +cmd/ diff --git a/vendor/github.com/mdlayher/dhcp6/.travis.yml b/vendor/github.com/mdlayher/dhcp6/.travis.yml new file mode 100644 index 00000000..a1147817 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/.travis.yml @@ -0,0 +1,15 @@ +language: go +go: + - 1.x +os: + - linux +before_install: + - go get github.com/golang/lint/golint + - go get honnef.co/go/tools/cmd/staticcheck + - go get -d ./... +script: + - go build -tags=gofuzz ./... + - go vet ./... + - staticcheck ./... + - golint -set_exit_status ./... + - go test -v -race ./... \ No newline at end of file diff --git a/vendor/github.com/mdlayher/dhcp6/LICENSE.md b/vendor/github.com/mdlayher/dhcp6/LICENSE.md new file mode 100644 index 00000000..39cc4d77 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/LICENSE.md @@ -0,0 +1,10 @@ +MIT License +=========== + +Copyright (C) 2015-2017 Matt Layher + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mdlayher/dhcp6/README.md b/vendor/github.com/mdlayher/dhcp6/README.md new file mode 100644 index 00000000..00d5fbb0 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/README.md @@ -0,0 +1,12 @@ +dhcp6 [![Build Status](https://travis-ci.org/mdlayher/dhcp6.svg?branch=master)](https://travis-ci.org/mdlayher/dhcp6) [![GoDoc](https://godoc.org/github.com/mdlayher/dhcp6?status.svg)](https://godoc.org/github.com/mdlayher/dhcp6) [![Go Report Card](https://goreportcard.com/badge/github.com/mdlayher/dhcp6)](https://goreportcard.com/report/github.com/mdlayher/dhcp6) +===== + +Package `dhcp6` implements a DHCPv6 server, as described in IETF RFC 3315. MIT Licensed. + +At this time, the API is not stable, and may change over time. The eventual +goal is to implement a server, client, and testing facilities for consumers +of this package. + +The design of this package is inspired by Go's `net/http` package. The Go +standard library is Copyright (c) 2012 The Go Authors. All rights reserved. +The Go license can be found at https://golang.org/LICENSE. diff --git a/vendor/github.com/mdlayher/dhcp6/const.go b/vendor/github.com/mdlayher/dhcp6/const.go new file mode 100644 index 00000000..d64fc77a --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/const.go @@ -0,0 +1,128 @@ +package dhcp6 + +// MessageType represents a DHCP message type, as defined in RFC 3315, +// Section 5.3. Different DHCP message types are used to perform different +// actions between a client and server. +type MessageType uint8 + +// MessageType constants which indicate the message types described in +// RFCs 3315, 5007, 5460, 6977, and 7341. +// +// These message types are taken from IANA's DHCPv6 parameters registry: +// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml. +const ( + // RFC 3315 + MessageTypeSolicit MessageType = 1 + MessageTypeAdvertise MessageType = 2 + MessageTypeRequest MessageType = 3 + MessageTypeConfirm MessageType = 4 + MessageTypeRenew MessageType = 5 + MessageTypeRebind MessageType = 6 + MessageTypeReply MessageType = 7 + MessageTypeRelease MessageType = 8 + MessageTypeDecline MessageType = 9 + MessageTypeReconfigure MessageType = 10 + MessageTypeInformationRequest MessageType = 11 + MessageTypeRelayForw MessageType = 12 + MessageTypeRelayRepl MessageType = 13 + + // RFC 5007 + MessageTypeLeasequery MessageType = 14 + MessageTypeLeasequeryReply MessageType = 15 + + // RFC 5460 + MessageTypeLeasequeryDone MessageType = 16 + MessageTypeLeasequeryData MessageType = 17 + + // RFC 6977 + MessageTypeReconfigureRequest MessageType = 18 + MessageTypeReconfigureReply MessageType = 19 + + // RFC 7341 + MessageTypeDHCPv4Query MessageType = 20 + MessageTypeDHCPv4Response MessageType = 21 +) + +// Status represesents a DHCP status code, as defined in RFC 3315, +// Section 5.4. Status codes are used to communicate success or failure +// between client and server. +type Status uint16 + +// Status constants which indicate the status codes described in +// RFCs 3315, 3633, 5007, and 5460. +// +// These status codes are taken from IANA's DHCPv6 parameters registry: +// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml. +const ( + // RFC 3315 + StatusSuccess Status = 0 + StatusUnspecFail Status = 1 + StatusNoAddrsAvail Status = 2 + StatusNoBinding Status = 3 + StatusNotOnLink Status = 4 + StatusUseMulticast Status = 5 + + // RFC 3633 + StatusNoPrefixAvail Status = 6 + + // RFC 5007 + StatusUnknownQueryType Status = 7 + StatusMalformedQuery Status = 8 + StatusNotConfigured Status = 9 + StatusNotAllowed Status = 10 + + // RFC 5460 + StatusQueryTerminated Status = 11 +) + +// OptionCode represents a DHCP option, as defined in RFC 3315, +// Section 22. Options are used to carry additional information and +// parameters in DHCP messages between client and server. +type OptionCode uint16 + +// OptionCode constants which indicate the option codes described in +// RFC 3315, RFC 3633, and RFC 5970. +// +// These option codes are taken from IANA's DHCPv6 parameters registry: +// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml. +const ( + // RFC 3315 + OptionClientID OptionCode = 1 + OptionServerID OptionCode = 2 + OptionIANA OptionCode = 3 + OptionIATA OptionCode = 4 + OptionIAAddr OptionCode = 5 + OptionORO OptionCode = 6 + OptionPreference OptionCode = 7 + OptionElapsedTime OptionCode = 8 + OptionRelayMsg OptionCode = 9 + _ OptionCode = 10 + OptionAuth OptionCode = 11 + OptionUnicast OptionCode = 12 + OptionStatusCode OptionCode = 13 + OptionRapidCommit OptionCode = 14 + OptionUserClass OptionCode = 15 + OptionVendorClass OptionCode = 16 + OptionVendorOpts OptionCode = 17 + OptionInterfaceID OptionCode = 18 + OptionReconfMsg OptionCode = 19 + OptionReconfAccept OptionCode = 20 + + // RFC 3646 + OptionDNSServers OptionCode = 23 + + // RFC 3633 + OptionIAPD OptionCode = 25 + OptionIAPrefix OptionCode = 26 + + // RFC 4649 + OptionRemoteIdentifier OptionCode = 37 + + // RFC 5970 + OptionBootFileURL OptionCode = 59 + OptionBootFileParam OptionCode = 60 + OptionClientArchType OptionCode = 61 + OptionNII OptionCode = 62 + + // BUG(mdlayher): add additional option code types defined by IANA +) diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6.go b/vendor/github.com/mdlayher/dhcp6/dhcp6.go new file mode 100644 index 00000000..4d205c87 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6.go @@ -0,0 +1,27 @@ +// Package dhcp6 implements a DHCPv6 server, as described in RFC 3315. +// +// Unless otherwise stated, any reference to "DHCP" in this package refers to +// DHCPv6 only. +package dhcp6 + +import ( + "errors" +) + +//go:generate stringer -output=string.go -type=MessageType,Status,OptionCode + +var ( + // ErrInvalidOptions is returned when invalid options data is encountered + // during parsing. The data could report an incorrect length or have + // trailing bytes which are not part of the option. + ErrInvalidOptions = errors.New("invalid options data") + + // ErrInvalidPacket is returned when a byte slice does not contain enough + // data to create a valid Packet. A Packet must have at least a message type + // and transaction ID. + ErrInvalidPacket = errors.New("not enough bytes for valid packet") + + // ErrOptionNotPresent is returned when a requested opcode is not in + // the packet. + ErrOptionNotPresent = errors.New("option code not present in packet") +) diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/authentication.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/authentication.go new file mode 100644 index 00000000..c33f6b41 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/authentication.go @@ -0,0 +1,63 @@ +package dhcp6opts + +import ( + "io" + + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// The Authentication option carries authentication information to +// authenticate the identity and contents of DHCP messages. The use of +// the Authentication option is described in section 21. +type Authentication struct { + // The authentication protocol used in this authentication option + Protocol byte + + // The algorithm used in the authentication protocol + Algorithm byte + + // The replay detection method used in this authentication option + RDM byte + + // The replay detection information for the RDM + ReplayDetection uint64 + + // The authentication information, as specified by the protocol and + // algorithm used in this authentication option. + AuthenticationInformation []byte +} + +// MarshalBinary allocates a byte slice containing the data from a Authentication. +func (a *Authentication) MarshalBinary() ([]byte, error) { + // 1 byte: Protocol + // 1 byte: Algorithm + // 1 byte: RDM + // 8 bytes: ReplayDetection + // N bytes: AuthenticationInformation (can have 0 len byte) + b := buffer.New(nil) + b.Write8(a.Protocol) + b.Write8(a.Algorithm) + b.Write8(a.RDM) + b.Write64(a.ReplayDetection) + b.WriteBytes(a.AuthenticationInformation) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a Authentication. +// If the byte slice does not contain enough data to form a valid +// Authentication, io.ErrUnexpectedEOF is returned. +func (a *Authentication) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Too short to be valid Authentication + if b.Len() < 11 { + return io.ErrUnexpectedEOF + } + + a.Protocol = b.Read8() + a.Algorithm = b.Read8() + a.RDM = b.Read8() + a.ReplayDetection = b.Read64() + a.AuthenticationInformation = b.Remaining() + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/authentication_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/authentication_test.go new file mode 100644 index 00000000..0b5b34c8 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/authentication_test.go @@ -0,0 +1,84 @@ +package dhcp6opts + +import ( + "bytes" + "encoding/binary" + "io" + "reflect" + "testing" +) + +func TestAuthenticationMarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + authentication *Authentication + }{ + { + desc: "all zero values", + buf: bytes.Repeat([]byte{0}, 11), + authentication: &Authentication{}, + }, + { + desc: "Protocol: 0, Alforithm: 1, RDM: 2, [3, 4, 5, 6, 7, 8, 9, 0xa] ReplayDetection, [0xb, 0xc, 0xd, 0xe, 0xf] AuthenticationInformation", + buf: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, + authentication: &Authentication{ + Protocol: 0, + Algorithm: 1, + RDM: 2, + ReplayDetection: binary.BigEndian.Uint64([]byte{3, 4, 5, 6, 7, 8, 9, 0xa}), + AuthenticationInformation: []byte{0xb, 0xc, 0xd, 0xe, 0xf}, + }, + }, + } + + for i, tt := range tests { + want := tt.buf + got, err := tt.authentication.MarshalBinary() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected Authentication bytes for MarshalBinary(%v)\n- want: %v\n- got: %v", + i, tt.desc, tt.buf, want, got) + } + } +} + +func TestAuthenticationUnmarshalBinary(t *testing.T) { + var tests = []struct { + buf []byte + authentication *Authentication + err error + }{ + { + buf: bytes.Repeat([]byte{0}, 10), + err: io.ErrUnexpectedEOF, + }, + { + buf: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, + authentication: &Authentication{ + Protocol: 0, + Algorithm: 1, + RDM: 2, + ReplayDetection: binary.BigEndian.Uint64([]byte{3, 4, 5, 6, 7, 8, 9, 0xa}), + AuthenticationInformation: []byte{0xb, 0xc, 0xd, 0xe, 0xf}, + }, + }, + } + + for i, tt := range tests { + authentication := new(Authentication) + if want, got := tt.err, authentication.UnmarshalBinary(tt.buf); want != got { + t.Fatalf("[%02d] unexpected error for parseAuthentication(%v):\n- want: %v\n- got: %v", i, tt.buf, want, got) + } + + if tt.err != nil { + continue + } + + if want, got := tt.authentication, authentication; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected Authentication for parseAuthentication(%v):\n- want: %v\n- got: %v", i, tt.buf, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/const.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/const.go new file mode 100644 index 00000000..ae8e8128 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/const.go @@ -0,0 +1,22 @@ +package dhcp6opts + +// An ArchType is a client system architecture type, as defined in RFC 4578, +// Section 2.1. Though this RFC indicates these constants are for DHCPv4, +// they are carried over for use in DHCPv6 in RFC 5970, Section 3.3. +type ArchType uint16 + +// ArchType constants which indicate the client system architecture types +// described in RFC 4578, Section 2.1. +const ( + // RFC 4578 + ArchTypeIntelx86PC ArchType = 0 + ArchTypeNECPC98 ArchType = 1 + ArchTypeEFIItanium ArchType = 2 + ArchTypeDECAlpha ArchType = 3 + ArchtypeArcx86 ArchType = 4 + ArchTypeIntelLeanClient ArchType = 5 + ArchTypeEFIIA32 ArchType = 6 + ArchTypeEFIBC ArchType = 7 + ArchTypeEFIXscale ArchType = 8 + ArchTypeEFIx8664 ArchType = 9 +) diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/duid.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/duid.go new file mode 100644 index 00000000..94ea06b5 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/duid.go @@ -0,0 +1,391 @@ +package dhcp6opts + +import ( + "encoding" + "errors" + "io" + "net" + "time" + + "github.com/mdlayher/dhcp6/internal/buffer" +) + +var ( + // errInvalidDUIDLLT is returned when not enough bytes are present + // to parse a valid DUIDLLT from a byte slice, or when the DUID type + // found in the byte slice is incorrect. + errInvalidDUIDLLT = errors.New("invalid DUID-LLT") + + // errInvalidDUIDEN is returned when not enough bytes are present + // to parse a valid DUIDEN from a byte slice, or when the DUID type + // found in the byte slice is incorrect. + errInvalidDUIDEN = errors.New("invalid DUID-EN") + + // errInvalidDUIDLL is returned when not enough bytes are present + // to parse a valid DUIDLL from a byte slice, or when the DUID type + // found in the byte slice is incorrect. + errInvalidDUIDLL = errors.New("invalid DUID-LL") + + // errInvalidDUIDUUID is returned when not enough bytes are present + // to parse a valid DUIDUUID from a byte slice, or when the DUID type + // found in the byte slice is incorrect. + errInvalidDUIDUUID = errors.New("invalid DUID-UUID") + + // errUnknownDUID is returned when an unknown DUID type is + // encountered, and thus, a DUID cannot be parsed. + errUnknownDUID = errors.New("unknown DUID type") +) + +var ( + // duidLLTTime is the date specified in RFC 3315, Section 9.2, for use + // with DUID-LLT generation. It is used to calculate a duration from an + // input time after this date. Dates before this time are not valid for + // creation of DUIDLLT values. + duidLLTTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) +) + +// DUIDType is a type of DHCP Unique Identifier, as defined in RFC +// 3315, Section 9. DUIDs are used to uniquely identify a client to a +// server, or vice-versa. +type DUIDType uint16 + +// DUIDType constants which indicate DUID types described in RFCs 3315 and 6355. +// +// These DUID types are taken from IANA's DHCPv6 parameters registry: +// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml. +const ( + // RFC 3315 + DUIDTypeLLT DUIDType = 1 + DUIDTypeEN DUIDType = 2 + DUIDTypeLL DUIDType = 3 + + // RFC 6355 + DUIDTypeUUID DUIDType = 4 +) + +// DUID represents a DHCP Unique Identifier, as defined in RFC +// 3315, Section 9. A DUID is used by a DHCP server to identify +// unique clients. A DUID can also be used by a DHCP client to identify +// a unique server, when needed. +// +// The DUID interface represents a generic DUID, but DUIDs can be +// type-asserted to one of four specific types outlined in RFC 3315 +// and RFC 6355: +// - DUIDLLT - DUID Based on Link-layer Address Plus Time +// - DUIDEN - DUID Assigned by Vendor Based on Enterprise Number +// - DUIDLL - DUID Based on Link-layer Address +// - DUIDUUID - DUID Based on Universally Unique Identifier +// +// If further introspection of the DUID is needed, a type switch is +// recommended: +// switch d := duid.(type) { +// case *dhcp6.DUIDLLT: +// fmt.Println(d.Time) +// case *dhcp6.DUIDEN: +// fmt.Println(d.EnterpriseNumber) +// case *dhcp6.DUIDLL: +// fmt.Println(d.HardwareAddr) +// case *dhcp6.DUIDUUID: +// fmt.Println(d.UUID) +// } +type DUID interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler +} + +// DUIDLLT represents a DUID Based on Link-layer Address Plus Time [DUID-LLT], +// as defined in RFC 3315, Section 9.2. +// +// This DUID type must only be used with clients and servers with stable, +// persistent storage. It is the recommended DUID type for all general +// purpose computing devices. +type DUIDLLT struct { + // Type specifies the DUID type. For a DUIDLLT, this should always be + // DUIDTypeLLT. + Type DUIDType + + // HardwareType specifies an IANA-assigned hardware type, as described + // in RFC 826. + HardwareType uint16 + + // Time specifies the duration of the time this DUID was generated, minus + // midnight (UTC), January 1, 2000. + Time time.Duration + + // HardwareAddr specifies the hardware address for an arbitrary link-layer + // interface on a device, used in generating the DUIDLLT. This value + // could represent any arbitrary interface on a system, and should not be + // treated as a client or server's communicating hardware address. + HardwareAddr net.HardwareAddr +} + +// NewDUIDLLT generates a new DUIDLLT from an input IANA-assigned hardware +// type, time value, and a hardware address. +// +// The time value must be greater than midnight (UTC), January 1, 2000. +func NewDUIDLLT(hardwareType uint16, time time.Time, hardwareAddr net.HardwareAddr) (*DUIDLLT, error) { + // Do not accept dates before duidLLTTime. + if time.Before(duidLLTTime) { + return nil, ErrInvalidDUIDLLTTime + } + + return &DUIDLLT{ + Type: DUIDTypeLLT, + HardwareType: hardwareType, + Time: time.Sub(duidLLTTime), + HardwareAddr: hardwareAddr, + }, nil +} + +// MarshalBinary allocates a byte slice containing the data from a DUIDLLT. +func (d *DUIDLLT) MarshalBinary() ([]byte, error) { + // 2 bytes: DUID type + // 2 bytes: hardware type + // 4 bytes: time duration + // N bytes: hardware address + b := buffer.New(nil) + + b.Write16(uint16(d.Type)) + b.Write16(d.HardwareType) + b.Write32(uint32(d.Time / time.Second)) + b.WriteBytes(d.HardwareAddr) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a DUIDLLT. +// If the byte slice does not contain enough data to form a valid +// DUIDLLT, or another DUID type is indicated, errInvalidDUIDLLT is returned. +func (d *DUIDLLT) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Too short to be valid DUIDLLT + if b.Len() < 8 { + return io.ErrUnexpectedEOF + } + + // Verify DUID type + dType := DUIDType(b.Read16()) + if dType != DUIDTypeLLT { + return errInvalidDUIDLLT + } + d.Type = dType + d.HardwareType = b.Read16() + d.Time = time.Duration(b.Read32()) * time.Second + + d.HardwareAddr = b.Remaining() + + return nil +} + +// DUIDEN represents a DUID Assigned by Vendor Based on Enterprise Number +// [DUID-EN], as defined in RFC 3315, Section 9.3. This DUID type +// uses an IANA-assigned Private Enterprise Number for a given vendor. +type DUIDEN struct { + // Type specifies the DUID type. For a DUIDEN, this should always be + // DUIDTypeEN. + Type DUIDType + + // EnterpriseNumber specifies an IANA-assigned vendor Private Enterprise + // Number. + EnterpriseNumber uint32 + + // Identifier specifies a unique identifier of arbitrary length. This + // value is typically assigned when a device is manufactured. + Identifier []byte +} + +// NewDUIDEN generates a new DUIDEN from an input IANA-assigned Private +// Enterprise Number and a variable length unique identifier byte slice. +func NewDUIDEN(enterpriseNumber uint32, identifier []byte) *DUIDEN { + return &DUIDEN{ + Type: DUIDTypeEN, + EnterpriseNumber: enterpriseNumber, + Identifier: identifier, + } +} + +// MarshalBinary allocates a byte slice containing the data from a DUIDEN. +func (d *DUIDEN) MarshalBinary() ([]byte, error) { + // 2 bytes: DUID type + // 4 bytes: enterprise number + // N bytes: identifier + b := buffer.New(nil) + + b.Write16(uint16(d.Type)) + b.Write32(d.EnterpriseNumber) + b.WriteBytes(d.Identifier) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a DUIDEN. +// If the byte slice does not contain enough data to form a valid +// DUIDEN, or another DUID type is indicated, errInvalidDUIDEN is returned. +func (d *DUIDEN) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Too short to be valid DUIDEN + if b.Len() < 6 { + return io.ErrUnexpectedEOF + } + + // Verify DUID type + dType := DUIDType(b.Read16()) + if dType != DUIDTypeEN { + return errInvalidDUIDEN + } + d.Type = dType + d.EnterpriseNumber = b.Read32() + d.Identifier = b.Remaining() + return nil +} + +// DUIDLL represents a DUID Based on Link-layer Address [DUID-LL], +// as defined in RFC 3315, Section 9.4. +// +// This DUID type is recommended for devices with a +// permanently-connected network interface, but without stable, +// persistent storage. +// +// DUIDLL values are generated automatically for Servers which are not +// created with a ServerID, using the hardware type found by HardwareType +// and the hardware address of the listening network interface. +type DUIDLL struct { + // Type specifies the DUID type. For a DUIDLL, this should always be + // DUIDTypeLL. + Type DUIDType + + // HardwareType specifies an IANA-assigned hardware type, as described + // in RFC 826. + HardwareType uint16 + + // HardwareAddr specifies the hardware address for an arbitrary link-layer + // interface on a device, used in generating the DUIDLL. This value + // could represent any arbitrary interface on a system, and should not be + // treated as a client or server's communicating hardware address. + HardwareAddr net.HardwareAddr +} + +// NewDUIDLL generates a new DUIDLL from an input IANA-assigned hardware +// type and a hardware address. +func NewDUIDLL(hardwareType uint16, hardwareAddr net.HardwareAddr) *DUIDLL { + return &DUIDLL{ + Type: DUIDTypeLL, + HardwareType: hardwareType, + HardwareAddr: hardwareAddr, + } +} + +// MarshalBinary allocates a byte slice containing the data from a DUIDLL. +func (d *DUIDLL) MarshalBinary() ([]byte, error) { + // 2 bytes: DUID type + // 2 bytes: hardware type + // N bytes: hardware address + b := buffer.New(nil) + + b.Write16(uint16(d.Type)) + b.Write16(d.HardwareType) + b.WriteBytes(d.HardwareAddr) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a DUIDLL. +// If the byte slice does not contain enough data to form a valid +// DUIDLL, or another DUID type is indicated, errInvalidDUIDLL is returned. +func (d *DUIDLL) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Too short to be DUIDLL + if b.Len() < 4 { + return io.ErrUnexpectedEOF + } + + // Verify DUID type + dType := DUIDType(b.Read16()) + if dType != DUIDTypeLL { + return errInvalidDUIDLL + } + d.Type = dType + d.HardwareType = b.Read16() + d.HardwareAddr = b.Remaining() + + return nil +} + +// DUIDUUID represents a DUID based on Universally Unique Identifier +// [DUID-UUID], as defined in RFC 6355. This DUID type uses a UUID to +// identify clients or servers. +type DUIDUUID struct { + // Type specifies the DUID type. For a DUIDUUID, this should always be + // DUIDTypeUUID. + Type DUIDType + + // UUID specifies a Universally Unique Identifier, as described in RFC 4578. + UUID [16]byte +} + +// NewDUIDUUID generates a new DUIDUUID using an input UUID. +func NewDUIDUUID(uuid [16]byte) *DUIDUUID { + return &DUIDUUID{ + Type: DUIDTypeUUID, + UUID: uuid, + } +} + +// MarshalBinary allocates a byte slice containing the data from a DUIDUUID. +func (d *DUIDUUID) MarshalBinary() ([]byte, error) { + // 2 bytes: DUID type + // 16 bytes: UUID + b := buffer.New(nil) + + b.Write16(uint16(d.Type)) + b.WriteBytes(d.UUID[:]) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a DUIDUUID. +// If the byte slice does not contain the exact number of bytes +// needed to form a valid DUIDUUID, or another DUID type is indicated, +// errInvalidDUIDUUID is returned. +func (d *DUIDUUID) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // DUIDUUIDs are fixed-length structures + if b.Len() != 18 { + return io.ErrUnexpectedEOF + } + + // Verify DUID type + dType := DUIDType(b.Read16()) + if dType != DUIDTypeUUID { + return errInvalidDUIDUUID + } + d.Type = dType + b.ReadBytes(d.UUID[:]) + return nil +} + +// parseDUID returns the correct DUID type of the input byte slice as a +// DUID interface type. +func parseDUID(p []byte) (DUID, error) { + b := buffer.New(p) + // DUID must have enough bytes to determine its type + if b.Len() < 2 { + return nil, io.ErrUnexpectedEOF + } + + var d DUID + switch DUIDType(b.Read16()) { + case DUIDTypeLLT: + d = new(DUIDLLT) + case DUIDTypeEN: + d = new(DUIDEN) + case DUIDTypeLL: + d = new(DUIDLL) + case DUIDTypeUUID: + d = new(DUIDUUID) + default: + return nil, errUnknownDUID + } + + return d, d.UnmarshalBinary(p) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/duid_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/duid_test.go new file mode 100644 index 00000000..5546e71f --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/duid_test.go @@ -0,0 +1,476 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "net" + "reflect" + "testing" + "time" +) + +// TestNewDUIDLLT verifies that NewDUIDLLT generates a proper DUIDLLT or error +// from an input hardware type, time value, and hardware address. +func TestNewDUIDLLT(t *testing.T) { + var tests = []struct { + desc string + hardwareType uint16 + time time.Time + hardwareAddr net.HardwareAddr + duid *DUIDLLT + err error + }{ + { + desc: "date too early", + time: duidLLTTime.Add(-1 * time.Minute), + err: ErrInvalidDUIDLLTTime, + }, + { + desc: "OK", + hardwareType: 1, + time: duidLLTTime.Add(1 * time.Minute), + hardwareAddr: net.HardwareAddr([]byte{0, 1, 0, 1, 0, 1}), + duid: &DUIDLLT{ + Type: DUIDTypeLLT, + HardwareType: 1, + Time: duidLLTTime.Add(1 * time.Minute).Sub(duidLLTTime), + HardwareAddr: net.HardwareAddr([]byte{0, 1, 0, 1, 0, 1}), + }, + }, + } + + for i, tt := range tests { + duid, err := NewDUIDLLT(tt.hardwareType, tt.time, tt.hardwareAddr) + if err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + if want, got := tt.duid, duid; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] test %q, unexpected DUIDLLT:\n- want %v\n- got %v", + i, tt.desc, want, got) + } + } +} + +// TestDUIDLLTUnmarshalBinary verifies that DUIDLLT.UnmarshalBinary creates +// appropriate DUIDLLTs and errors for various input byte slices. +func TestDUIDLLTUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + duid *DUIDLLT + err error + }{ + { + desc: "nil buffer, invalid DUID-LLT", + err: io.ErrUnexpectedEOF, + }, + { + desc: "empty buffer, invalid DUID-LLT", + buf: []byte{}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "length 7 buffer, invalid DUID-LLT", + buf: bytes.Repeat([]byte{0}, 7), + err: io.ErrUnexpectedEOF, + }, + { + desc: "wrong DUID type", + buf: []byte{ + 0, 2, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + }, + err: errInvalidDUIDLLT, + }, + { + desc: "OK DUIDLLT", + buf: []byte{ + 0, 1, + 0, 1, + 0, 0, 0, 60, + 0, 1, 0, 1, 0, 1, + }, + duid: &DUIDLLT{ + Type: DUIDTypeLLT, + HardwareType: 1, + Time: 1 * time.Minute, + HardwareAddr: []byte{0, 1, 0, 1, 0, 1}, + }, + }, + } + + for i, tt := range tests { + duid := new(DUIDLLT) + if err := duid.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + if want, got := tt.duid, duid; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] test %q, unexpected DUID-LLT:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestNewDUIDEN verifies that NewDUIDEN generates a proper DUIDEN from +// an input enterprise number and identifier. +func TestNewDUIDEN(t *testing.T) { + var tests = []struct { + enterpriseNumber uint32 + identifier []byte + duid *DUIDEN + }{ + { + enterpriseNumber: 100, + identifier: []byte{0, 1, 2, 3, 4}, + duid: &DUIDEN{ + Type: DUIDTypeEN, + EnterpriseNumber: 100, + Identifier: []byte{0, 1, 2, 3, 4}, + }, + }, + } + + for i, tt := range tests { + if want, got := tt.duid, NewDUIDEN(tt.enterpriseNumber, tt.identifier); !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected DUIDEN:\n- want %v\n- got %v", i, want, got) + } + } +} + +// TestDUIDENUnmarshalBinary verifies that DUIDEN.UnmarshalBinary creates +// appropriate DUIDENs and errors for various input byte slices. +func TestDUIDENUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + duid *DUIDEN + err error + }{ + { + desc: "nil buffer, invalid DUID-EN", + err: io.ErrUnexpectedEOF, + }, + { + desc: "empty buffer, invalid DUID-EN", + buf: []byte{}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "length 5 buffer, invalid DUID-EN", + buf: bytes.Repeat([]byte{0}, 5), + err: io.ErrUnexpectedEOF, + }, + { + desc: "wrong DUID type", + buf: []byte{ + 0, 3, + 0, 0, 0, 0, + }, + err: errInvalidDUIDEN, + }, + { + desc: "OK DUIDEN", + buf: []byte{ + 0, 2, + 0, 0, 0, 100, + 0, 1, 2, 3, 4, 5, + }, + duid: &DUIDEN{ + Type: DUIDTypeEN, + EnterpriseNumber: 100, + Identifier: []byte{0, 1, 2, 3, 4, 5}, + }, + }, + } + + for i, tt := range tests { + duid := new(DUIDEN) + if err := duid.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + if want, got := tt.duid, duid; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] test %q, unexpected DUID-EN:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestNewDUIDLL verifies that NewDUIDLL generates a proper DUIDLL from +// an input hardware type and hardware address. +func TestNewDUIDLL(t *testing.T) { + var tests = []struct { + hardwareType uint16 + hardwareAddr net.HardwareAddr + duid *DUIDLL + }{ + { + hardwareType: 1, + hardwareAddr: net.HardwareAddr([]byte{0, 0, 0, 0, 0, 0}), + duid: &DUIDLL{ + Type: DUIDTypeLL, + HardwareType: 1, + HardwareAddr: net.HardwareAddr([]byte{0, 0, 0, 0, 0, 0}), + }, + }, + } + + for i, tt := range tests { + if want, got := tt.duid, NewDUIDLL(tt.hardwareType, tt.hardwareAddr); !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected DUIDLL:\n- want %v\n- got %v", i, want, got) + } + } +} + +// TestDUIDLLUnmarshalBinary verifies that DUIDLL.UnmarshalBinary creates +// appropriate DUIDLLs and errors for various input byte slices. +func TestDUIDLLUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + duid *DUIDLL + err error + }{ + { + desc: "nil buffer, invalid DUID-LL", + err: io.ErrUnexpectedEOF, + }, + { + desc: "empty buffer, invalid DUID-LL", + buf: []byte{}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "length 7 buffer, invalid DUID-LL", + buf: bytes.Repeat([]byte{0}, 7), + err: errInvalidDUIDLL, + }, + { + desc: "wrong DUID type", + buf: []byte{ + 0, 1, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + }, + err: errInvalidDUIDLL, + }, + { + desc: "OK DUIDLL", + buf: []byte{ + 0, 3, + 0, 1, + 0, 1, 0, 1, 0, 1, + }, + duid: &DUIDLL{ + Type: DUIDTypeLL, + HardwareType: 1, + HardwareAddr: []byte{0, 1, 0, 1, 0, 1}, + }, + }, + } + + for i, tt := range tests { + duid := new(DUIDLL) + if err := duid.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + if want, got := tt.duid, duid; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] test %q, unexpected DUID-LL:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestNewDUIDUUID verifies that NewDUIDUUID generates a proper DUIDUUID from +// an input UUID. +func TestNewDUIDUUID(t *testing.T) { + var tests = []struct { + uuid [16]byte + duid *DUIDUUID + }{ + { + uuid: [16]byte{ + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + }, + duid: &DUIDUUID{ + Type: DUIDTypeUUID, + UUID: [16]byte{ + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + }, + }, + }, + } + + for i, tt := range tests { + if want, got := tt.duid, NewDUIDUUID(tt.uuid); !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected DUIDUUID:\n- want %v\n- got %v", i, want, got) + } + } +} + +// TestDUIDUUIDUnmarshalBinary verifies that DUIDUUID.UnmarshalBinary returns +// appropriate DUIDUUIDs and errors for various input byte slices. +func TestDUIDUUIDUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + duid *DUIDUUID + err error + }{ + { + desc: "nil buffer, invalid DUID-UUID", + err: io.ErrUnexpectedEOF, + }, + { + desc: "empty buffer, invalid DUID-UUID", + buf: []byte{}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "length 17 buffer, invalid DUID-UUID", + buf: bytes.Repeat([]byte{0}, 17), + err: io.ErrUnexpectedEOF, + }, + { + desc: "length 19 buffer, invalid DUID-UUID", + buf: bytes.Repeat([]byte{0}, 19), + err: io.ErrUnexpectedEOF, + }, + { + desc: "wrong DUID type", + buf: []byte{ + 0, 2, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + }, + err: errInvalidDUIDUUID, + }, + { + desc: "OK DUIDUUID", + buf: []byte{ + 0, 4, + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + }, + duid: &DUIDUUID{ + Type: DUIDTypeUUID, + UUID: [16]byte{ + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + }, + }, + }, + } + + for i, tt := range tests { + duid := new(DUIDUUID) + if err := duid.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + if want, got := tt.duid, duid; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] test %q, unexpected DUID-UUID:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// Test_parseDUID verifies that parseDUID detects the correct DUID type for a +// variety of input data. +func Test_parseDUID(t *testing.T) { + var tests = []struct { + buf []byte + result reflect.Type + err error + }{ + { + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + buf: []byte{0, 0}, + err: errUnknownDUID, + }, + // Known types padded out to be just long enough to not error + { + buf: []byte{0, 1, 0, 0, 0, 0, 0, 0}, + result: reflect.TypeOf(&DUIDLLT{}), + }, + { + buf: []byte{0, 2, 0, 0, 0, 0}, + result: reflect.TypeOf(&DUIDEN{}), + }, + { + buf: []byte{0, 3, 0, 0}, + result: reflect.TypeOf(&DUIDLL{}), + }, + { + buf: append([]byte{0, 4}, bytes.Repeat([]byte{0}, 16)...), + result: reflect.TypeOf(&DUIDUUID{}), + }, + { + buf: []byte{0, 5}, + err: errUnknownDUID, + }, + } + + for i, tt := range tests { + d, err := parseDUID(tt.buf) + if err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] unexpected error for parseDUID(%v): %v != %v", + i, tt.buf, want, got) + } + + continue + } + + if want, got := tt.result, reflect.TypeOf(d); want != got { + t.Fatalf("[%02d] unexpected type for parseDUID(%v): %v != %v", + i, tt.buf, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaaddr.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaaddr.go new file mode 100644 index 00000000..d74b475e --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaaddr.go @@ -0,0 +1,121 @@ +package dhcp6opts + +import ( + "io" + "net" + "time" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// IAAddr represents an Identity Association Address, as defined in RFC 3315, +// Section 22.6. +// +// DHCP clients use identity association addresses (IAAddrs) to request IPv6 +// addresses from a DHCP server, using the lifetimes specified in the preferred +// lifetime and valid lifetime fields. Multiple IAAddrs may be present in a +// single DHCP request, but only enscapsulated within an IANA or IATA options +// field. +type IAAddr struct { + // IP specifies the IPv6 address to offer to a client. The validity of the + // address is controlled by the PreferredLifetime and ValidLifetime fields. + IP net.IP + + // PreferredLifetime specifies the preferred lifetime of an IPv6 address. + // When the preferred lifetime of an address expires, the address becomes + // deprecated, and should not be used in new communications. + // + // The preferred lifetime of an address must not be greater than its + // valid lifetime. + PreferredLifetime time.Duration + + // ValidLifetime specifies the valid lifetime of an IPv6 address. When the + // valid lifetime of an address expires, the address should not be used for + // any further communication. + // + // The valid lifetime of an address must be greater than its preferred + // lifetime. + ValidLifetime time.Duration + + // Options specifies a map of DHCP options specific to this IAAddr. + // Its methods can be used to retrieve data from an incoming IAAddr, or + // send data with an outgoing IAAddr. + Options dhcp6.Options +} + +// NewIAAddr creates a new IAAddr from an IPv6 address, preferred and valid lifetime +// durations, and an optional Options map. +// +// The IP must be exactly 16 bytes, the correct length for an IPv6 address. +// The preferred lifetime duration must be less than the valid lifetime +// duration. Failure to meet either of these conditions will result in an error. +// If an Options map is not specified, a new one will be allocated. +func NewIAAddr(ip net.IP, preferred time.Duration, valid time.Duration, options dhcp6.Options) (*IAAddr, error) { + // From documentation: If ip is not an IPv4 address, To4 returns nil. + if ip.To4() != nil { + return nil, ErrInvalidIP + } + + // Preferred lifetime must always be less than valid lifetime. + if preferred > valid { + return nil, ErrInvalidLifetimes + } + + // If no options set, make empty map + if options == nil { + options = make(dhcp6.Options) + } + + return &IAAddr{ + IP: ip, + PreferredLifetime: preferred, + ValidLifetime: valid, + Options: options, + }, nil +} + +// MarshalBinary allocates a byte slice containing the data from a IAAddr. +func (i *IAAddr) MarshalBinary() ([]byte, error) { + // 16 bytes: IPv6 address + // 4 bytes: preferred lifetime + // 4 bytes: valid lifetime + // N bytes: options + b := buffer.New(nil) + + copy(b.WriteN(net.IPv6len), i.IP) + b.Write32(uint32(i.PreferredLifetime / time.Second)) + b.Write32(uint32(i.ValidLifetime / time.Second)) + opts, err := i.Options.MarshalBinary() + if err != nil { + return nil, err + } + b.WriteBytes(opts) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a IAAddr. +// +// If the byte slice does not contain enough data to form a valid IAAddr, +// io.ErrUnexpectedEOF is returned. If the preferred lifetime value in the +// byte slice is less than the valid lifetime, ErrInvalidLifetimes is returned. +func (i *IAAddr) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + if b.Len() < 24 { + return io.ErrUnexpectedEOF + } + + i.IP = make(net.IP, net.IPv6len) + copy(i.IP, b.Consume(net.IPv6len)) + + i.PreferredLifetime = time.Duration(b.Read32()) * time.Second + i.ValidLifetime = time.Duration(b.Read32()) * time.Second + + // Preferred lifetime must always be less than valid lifetime. + if i.PreferredLifetime > i.ValidLifetime { + return ErrInvalidLifetimes + } + + return (&i.Options).UnmarshalBinary(b.Remaining()) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaaddr_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaaddr_test.go new file mode 100644 index 00000000..88c6aa2a --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaaddr_test.go @@ -0,0 +1,198 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "net" + "testing" + "time" + + "github.com/mdlayher/dhcp6" +) + +// TestNewIAAddr verifies that NewIAAddr creates a proper IAAddr value or returns +// a correct error for input values. +func TestNewIAAddr(t *testing.T) { + var tests = []struct { + desc string + ip net.IP + preferred time.Duration + valid time.Duration + options dhcp6.Options + iaaddr *IAAddr + err error + }{ + { + desc: "all zero values", + iaaddr: &IAAddr{}, + }, + { + desc: "IPv4 address", + ip: net.IP([]byte{192, 168, 1, 1}), + err: ErrInvalidIP, + }, + { + desc: "preferred greater than valid lifetime", + ip: net.IPv6loopback, + preferred: 2 * time.Second, + valid: 1 * time.Second, + err: ErrInvalidLifetimes, + }, + { + desc: "IPv6 localhost, 1s preferred, 2s valid, no options", + ip: net.IPv6loopback, + preferred: 1 * time.Second, + valid: 2 * time.Second, + iaaddr: &IAAddr{ + IP: net.IPv6loopback, + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + }, + }, + { + desc: "IPv6 localhost, 1s preferred, 2s valid, option client ID [0 1]", + ip: net.IPv6loopback, + preferred: 1 * time.Second, + valid: 2 * time.Second, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + iaaddr: &IAAddr{ + IP: net.IPv6loopback, + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iaaddr, err := NewIAAddr(tt.ip, tt.preferred, tt.valid, tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error for NewIAAddr: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + want, err := tt.iaaddr.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iaaddr.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IAAddr bytes:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestIAAddrUnmarshalBinary verifies that IAAddr.UnmarshalBinary produces a +// correct IAAddr value or error for an input buffer. +func TestIAAddrUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + iaaddr *IAAddr + err error + }{ + { + desc: "one byte IAAddr", + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "23 bytes IAAddr", + buf: bytes.Repeat([]byte{0}, 23), + err: io.ErrUnexpectedEOF, + }, + { + desc: "preferred greater than valid lifetime", + buf: append(net.IPv6zero, []byte{ + 0, 0, 0, 2, + 0, 0, 0, 1, + }...), + err: ErrInvalidLifetimes, + }, + { + desc: "invalid options (length mismatch)", + buf: append(net.IPv6zero, []byte{ + 0, 0, 0, 1, + 0, 0, 0, 2, + 0, 1, 0, 1, + }...), + err: dhcp6.ErrInvalidOptions, + }, + { + desc: "IPv6 loopback, 1s preferred, 2s valid, no options", + buf: append(net.IPv6loopback, []byte{ + 0, 0, 0, 1, + 0, 0, 0, 2, + }...), + iaaddr: &IAAddr{ + IP: net.IPv6loopback, + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + }, + }, + { + desc: "IPv6 loopback, 1s preferred, 2s valid, option client ID [0 1]", + buf: append(net.IPv6loopback, []byte{ + 0, 0, 0, 1, + 0, 0, 0, 2, + 0, 1, 0, 2, 0, 1, + }...), + iaaddr: &IAAddr{ + IP: net.IPv6loopback, + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iaaddr := new(IAAddr) + if err := iaaddr.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error for parseIAAddr: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + want, err := tt.iaaddr.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iaaddr.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IAAddr bytes for parseIAAddr:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + + for _, v := range iaaddr.Options { + for ii := range v { + if want, got := cap(v[ii]), cap(v[ii]); want != got { + t.Fatalf("[%02d] test %q, unexpected capacity option data:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iana.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iana.go new file mode 100644 index 00000000..a1323f98 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iana.go @@ -0,0 +1,88 @@ +package dhcp6opts + +import ( + "io" + "time" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// IANA represents an Identity Association for Non-temporary Addresses, as +// defined in RFC 3315, Section 22.4. +// +// Multiple IANAs may be present in a single DHCP request. +type IANA struct { + // IAID specifies a DHCP identity association identifier. The IAID + // is a unique, client-generated identifier. + IAID [4]byte + + // T1 specifies how long a DHCP client will wait to contact this server, + // to extend the lifetimes of the addresses assigned to this IANA + // by this server. + T1 time.Duration + + // T2 specifies how long a DHCP client will wait to contact any server, + // to extend the lifetimes of the addresses assigned to this IANA + // by this server. + T2 time.Duration + + // Options specifies a map of DHCP options specific to this IANA. + // Its methods can be used to retrieve data from an incoming IANA, or send + // data with an outgoing IANA. + Options dhcp6.Options +} + +// NewIANA creates a new IANA from an IAID, T1 and T2 durations, and an +// Options map. If an Options map is not specified, a new one will be +// allocated. +func NewIANA(iaid [4]byte, t1 time.Duration, t2 time.Duration, options dhcp6.Options) *IANA { + if options == nil { + options = make(dhcp6.Options) + } + + return &IANA{ + IAID: iaid, + T1: t1, + T2: t2, + Options: options, + } +} + +// MarshalBinary allocates a byte slice containing the data from a IANA. +func (i IANA) MarshalBinary() ([]byte, error) { + // 4 bytes: IAID + // 4 bytes: T1 + // 4 bytes: T2 + // N bytes: options slice byte count + b := buffer.New(nil) + + b.WriteBytes(i.IAID[:]) + b.Write32(uint32(i.T1 / time.Second)) + b.Write32(uint32(i.T2 / time.Second)) + opts, err := i.Options.MarshalBinary() + if err != nil { + return nil, err + } + b.WriteBytes(opts) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a IANA. +// +// If the byte slice does not contain enough data to form a valid IANA, +// io.ErrUnexpectedEOF is returned. +func (i *IANA) UnmarshalBinary(p []byte) error { + // IANA must contain at least an IAID, T1, and T2. + b := buffer.New(p) + if b.Len() < 12 { + return io.ErrUnexpectedEOF + } + + b.ReadBytes(i.IAID[:]) + i.T1 = time.Duration(b.Read32()) * time.Second + i.T2 = time.Duration(b.Read32()) * time.Second + + return (&i.Options).UnmarshalBinary(b.Remaining()) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iana_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iana_test.go new file mode 100644 index 00000000..a92f0653 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iana_test.go @@ -0,0 +1,199 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "reflect" + "testing" + "time" + + "github.com/mdlayher/dhcp6" +) + +// TestNewIANA verifies that NewIANA creates a proper IANA value for +// input values. +func TestNewIANA(t *testing.T) { + var tests = []struct { + desc string + iaid [4]byte + t1 time.Duration + t2 time.Duration + options dhcp6.Options + iana *IANA + }{ + { + desc: "all zero values", + iana: &IANA{}, + }, + { + desc: "[0 1 2 3] IAID, 30s T1, 60s T2, option client ID [0 1]", + iaid: [4]byte{0, 1, 2, 3}, + t1: 30 * time.Second, + t2: 60 * time.Second, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + iana: &IANA{ + IAID: [4]byte{0, 1, 2, 3}, + T1: 30 * time.Second, + T2: 60 * time.Second, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iana := NewIANA(tt.iaid, tt.t1, tt.t2, tt.options) + + want, err := tt.iana.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iana.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IANA bytes for NewIANA(%v, %v, %v, %v)\n- want: %v\n- got: %v", + i, tt.desc, tt.iaid, tt.t1, tt.t2, tt.options, want, got) + } + } +} + +// TestIANAMarshalBinary verifies that IANA.MarshalBinary allocates and returns a correct +// byte slice for a variety of input data. +func TestIANAMarshalBinary(t *testing.T) { + var tests = []struct { + desc string + iana *IANA + buf []byte + }{ + { + desc: "empty IANA", + iana: &IANA{}, + buf: []byte{ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + }, + }, + { + desc: "[1 2 3 4] IAID only", + iana: &IANA{ + IAID: [4]byte{1, 2, 3, 4}, + }, + buf: []byte{ + 1, 2, 3, 4, + 0, 0, 0, 0, + 0, 0, 0, 0, + }, + }, + { + desc: "[1 2 3 4] IAID, 30s T1, 60s T2", + iana: &IANA{ + IAID: [4]byte{1, 2, 3, 4}, + T1: 30 * time.Second, + T2: 60 * time.Second, + }, + buf: []byte{ + 1, 2, 3, 4, + 0, 0, 0, 30, + 0, 0, 0, 60, + }, + }, + { + desc: "[1 2 3 4] IAID, 30s T1, 60s T2, option client ID [0 1]", + iana: &IANA{ + IAID: [4]byte{1, 2, 3, 4}, + T1: 30 * time.Second, + T2: 60 * time.Second, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + buf: []byte{ + 1, 2, 3, 4, + 0, 0, 0, 30, + 0, 0, 0, 60, + 0, 1, 0, 2, 0, 1, + }, + }, + } + + for i, tt := range tests { + got, err := tt.iana.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if want := tt.buf; !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IANA bytes:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestIANAUnmarshalBinary verifies that IANA.UnmarshalBinary produces a correct +// IANA value or error for an input buffer. +func TestIANAUnmarshalBinary(t *testing.T) { + var tests = []struct { + buf []byte + iana *IANA + options dhcp6.Options + err error + }{ + { + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + buf: bytes.Repeat([]byte{0}, 11), + err: io.ErrUnexpectedEOF, + }, + { + buf: []byte{ + 1, 2, 3, 4, + 0, 0, 1, 0, + 0, 0, 2, 0, + 0, 1, 0, 1, + }, + err: dhcp6.ErrInvalidOptions, + }, + { + buf: []byte{ + 1, 2, 3, 4, + 0, 0, 1, 0, + 0, 0, 2, 0, + 0, 1, 0, 2, 0, 1, + }, + iana: &IANA{ + IAID: [4]byte{1, 2, 3, 4}, + T1: (4 * time.Minute) + 16*time.Second, + T2: (8 * time.Minute) + 32*time.Second, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iana := new(IANA) + if err := iana.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] unexpected error for parseIANA(%v): %v != %v", + i, tt.buf, want, got) + } + + continue + } + + if want, got := tt.iana, iana; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected IANA for parseIANA(%v):\n- want: %v\n- got: %v", + i, tt.buf, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iapd.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iapd.go new file mode 100644 index 00000000..591cacd8 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iapd.go @@ -0,0 +1,88 @@ +package dhcp6opts + +import ( + "io" + "time" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// IAPD represents an Identity Association for Prefix Delegation, as +// defined in RFC 3633, Section 9. +// +// Multiple IAPDs may be present in a single DHCP request. +type IAPD struct { + // IAID specifies a DHCP identity association identifier. The IAID + // is a unique, client-generated identifier. + IAID [4]byte + + // T1 specifies how long a requesting router will wait to contact a + // delegating router, to extend the lifetimes of the prefixes delegated + // to this IAPD, by the delegating router. + T1 time.Duration + + // T2 specifies how long a requesting router will wait to contact any + // available delegating router, to extend the lifetimes of the prefixes + // delegated to this IAPD. + T2 time.Duration + + // Options specifies a map of DHCP options specific to this IAPD. + // Its methods can be used to retrieve data from an incoming IAPD, or send + // data with an outgoing IAPD. + Options dhcp6.Options +} + +// NewIAPD creates a new IAPD from an IAID, T1 and T2 durations, and an +// Options map. If an Options map is not specified, a new one will be +// allocated. +func NewIAPD(iaid [4]byte, t1 time.Duration, t2 time.Duration, options dhcp6.Options) *IAPD { + if options == nil { + options = make(dhcp6.Options) + } + + return &IAPD{ + IAID: iaid, + T1: t1, + T2: t2, + Options: options, + } +} + +// MarshalBinary allocates a byte slice containing the data from a IAPD. +func (i *IAPD) MarshalBinary() ([]byte, error) { + // 4 bytes: IAID + // 4 bytes: T1 + // 4 bytes: T2 + // N bytes: options slice byte count + buf := buffer.New(nil) + + buf.WriteBytes(i.IAID[:]) + buf.Write32(uint32(i.T1 / time.Second)) + buf.Write32(uint32(i.T2 / time.Second)) + opts, err := i.Options.MarshalBinary() + if err != nil { + return nil, err + } + buf.WriteBytes(opts) + + return buf.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a IAPD. +// +// If the byte slice does not contain enough data to form a valid IAPD, +// io.ErrUnexpectedEOF is returned. +func (i *IAPD) UnmarshalBinary(b []byte) error { + // IAPD must contain at least an IAID, T1, and T2. + buf := buffer.New(b) + if buf.Len() < 12 { + return io.ErrUnexpectedEOF + } + + copy(i.IAID[:], buf.Consume(4)) + i.T1 = time.Duration(buf.Read32()) * time.Second + i.T2 = time.Duration(buf.Read32()) * time.Second + + return (&i.Options).UnmarshalBinary(buf.Remaining()) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iapd_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iapd_test.go new file mode 100644 index 00000000..653ec48d --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iapd_test.go @@ -0,0 +1,126 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "reflect" + "testing" + "time" + + "github.com/mdlayher/dhcp6" +) + +// TestNewIAPD verifies that NewIAPD creates a proper IAPD value for +// input values. +func TestNewIAPD(t *testing.T) { + var tests = []struct { + desc string + iaid [4]byte + t1 time.Duration + t2 time.Duration + options dhcp6.Options + iapd *IAPD + }{ + { + desc: "all zero values", + iapd: &IAPD{}, + }, + { + desc: "[0 1 2 3] IAID, 30s T1, 60s T2, option client ID [0 1]", + iaid: [4]byte{0, 1, 2, 3}, + t1: 30 * time.Second, + t2: 60 * time.Second, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + iapd: &IAPD{ + IAID: [4]byte{0, 1, 2, 3}, + T1: 30 * time.Second, + T2: 60 * time.Second, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iapd := NewIAPD(tt.iaid, tt.t1, tt.t2, tt.options) + + want, err := tt.iapd.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iapd.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IAPD bytes for NewIAPD(%v, %v, %v, %v)\n- want: %v\n- got: %v", + i, tt.desc, tt.iaid, tt.t1, tt.t2, tt.options, want, got) + } + } +} + +// TestIAPDUnmarshalBinary verifies that IAPD.UnmarshalBinary produces a +// correct IAPD value or error for an input buffer. +func TestIAPDUnmarshalBinary(t *testing.T) { + var tests = []struct { + buf []byte + iapd *IAPD + options dhcp6.Options + err error + }{ + { + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + buf: bytes.Repeat([]byte{0}, 11), + err: io.ErrUnexpectedEOF, + }, + { + buf: []byte{ + 1, 2, 3, 4, + 0, 0, 1, 0, + 0, 0, 2, 0, + 0, 1, 0, 1, + }, + err: dhcp6.ErrInvalidOptions, + }, + { + buf: []byte{ + 1, 2, 3, 4, + 0, 0, 1, 0, + 0, 0, 2, 0, + 0, 1, 0, 2, 0, 1, + }, + iapd: &IAPD{ + IAID: [4]byte{1, 2, 3, 4}, + T1: (4 * time.Minute) + 16*time.Second, + T2: (8 * time.Minute) + 32*time.Second, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iapd := new(IAPD) + if err := iapd.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] unexpected error for parseIAPD(%v): %v != %v", + i, tt.buf, want, got) + } + + continue + } + + if want, got := tt.iapd, iapd; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected IAPD for parseIAPD(%v):\n- want: %v\n- got: %v", + i, tt.buf, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaprefix.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaprefix.go new file mode 100644 index 00000000..327e202d --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaprefix.go @@ -0,0 +1,134 @@ +package dhcp6opts + +import ( + "io" + "net" + "time" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// IAPrefix represents an Identity Association Prefix, as defined in RFC 3633, +// Section 10. +// +// Routers may use identity association prefixes (IAPrefixes) to request IPv6 +// prefixes to assign individual address to IPv6 clients, using the lifetimes +// specified in the preferred lifetime and valid lifetime fields. Multiple +// IAPrefixes may be present in a single DHCP request, but only enscapsulated +// within an IAPD's options. +type IAPrefix struct { + // PreferredLifetime specifies the preferred lifetime of an IPv6 prefix. + // When the preferred lifetime of a prefix expires, the prefix becomes + // deprecated, and addresses from the prefix should not be used in new + // communications. + // + // The preferred lifetime of a prefix must not be greater than its valid + // lifetime. + PreferredLifetime time.Duration + + // ValidLifetime specifies the valid lifetime of an IPv6 prefix. When the + // valid lifetime of a prefix expires, addresses from the prefix the address + // should not be used for any further communication. + // + // The valid lifetime of a prefix must be greater than its preferred + // lifetime. + ValidLifetime time.Duration + + // PrefixLength specifies the length in bits of an IPv6 address prefix, such + // as 32, 64, etc. + PrefixLength uint8 + + // Prefix specifies the IPv6 address prefix from which IPv6 addresses can + // be allocated. + Prefix net.IP + + // Options specifies a map of DHCP options specific to this IAPrefix. + // Its methods can be used to retrieve data from an incoming IAPrefix, or + // send data with an outgoing IAPrefix. + Options dhcp6.Options +} + +// NewIAPrefix creates a new IAPrefix from preferred and valid lifetime +// durations, an IPv6 prefix length, an IPv6 prefix, and an optional Options +// map. +// +// The preferred lifetime duration must be less than the valid lifetime +// duration. The IPv6 prefix must be exactly 16 bytes, the correct length +// for an IPv6 address. Failure to meet either of these conditions will result +// in an error. If an Options map is not specified, a new one will be +// allocated. +func NewIAPrefix(preferred time.Duration, valid time.Duration, prefixLength uint8, prefix net.IP, options dhcp6.Options) (*IAPrefix, error) { + // Preferred lifetime must always be less than valid lifetime. + if preferred > valid { + return nil, ErrInvalidLifetimes + } + + // From documentation: If ip is not an IPv4 address, To4 returns nil. + if prefix.To4() != nil { + return nil, ErrInvalidIP + } + + // If no options set, make empty map + if options == nil { + options = make(dhcp6.Options) + } + + return &IAPrefix{ + PreferredLifetime: preferred, + ValidLifetime: valid, + PrefixLength: prefixLength, + Prefix: prefix, + Options: options, + }, nil +} + +// MarshalBinary allocates a byte slice containing the data from a IAPrefix. +func (i *IAPrefix) MarshalBinary() ([]byte, error) { + // 4 bytes: preferred lifetime + // 4 bytes: valid lifetime + // 1 byte : prefix length + // 16 bytes: IPv6 prefix + // N bytes: options + b := buffer.New(nil) + + b.Write32(uint32(i.PreferredLifetime / time.Second)) + b.Write32(uint32(i.ValidLifetime / time.Second)) + b.Write8(i.PrefixLength) + copy(b.WriteN(net.IPv6len), i.Prefix) + opts, err := i.Options.MarshalBinary() + if err != nil { + return nil, err + } + b.WriteBytes(opts) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a IAPrefix. +// +// If the byte slice does not contain enough data to form a valid IAPrefix, +// io.ErrUnexpectedEOF is returned. If the preferred lifetime value in the +// byte slice is less than the valid lifetime, ErrInvalidLifetimes is +// returned. +func (i *IAPrefix) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // IAPrefix must at least contain lifetimes, prefix length, and prefix + if b.Len() < 25 { + return io.ErrUnexpectedEOF + } + + i.PreferredLifetime = time.Duration(b.Read32()) * time.Second + i.ValidLifetime = time.Duration(b.Read32()) * time.Second + + // Preferred lifetime must always be less than valid lifetime. + if i.PreferredLifetime > i.ValidLifetime { + return ErrInvalidLifetimes + } + + i.PrefixLength = b.Read8() + i.Prefix = make(net.IP, net.IPv6len) + copy(i.Prefix, b.Consume(net.IPv6len)) + + return (&i.Options).UnmarshalBinary(b.Remaining()) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaprefix_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaprefix_test.go new file mode 100644 index 00000000..c796175a --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iaprefix_test.go @@ -0,0 +1,204 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "net" + "testing" + "time" + + "github.com/mdlayher/dhcp6" +) + +// TestNewIAPrefix verifies that NewIAPrefix creates a proper IAPrefix value +// or returns a correct error for input values. +func TestNewIAPrefix(t *testing.T) { + var tests = []struct { + desc string + preferred time.Duration + valid time.Duration + pLength uint8 + prefix net.IP + options dhcp6.Options + iaprefix *IAPrefix + err error + }{ + { + desc: "all zero values", + iaprefix: &IAPrefix{}, + }, + { + desc: "preferred greater than valid lifetime", + preferred: 2 * time.Second, + valid: 1 * time.Second, + err: ErrInvalidLifetimes, + }, + { + desc: "IPv4 address", + prefix: net.IP([]byte{192, 168, 1, 1}), + err: ErrInvalidIP, + }, + { + desc: "1s preferred, 2s valid, '2001:db8::/32', no options", + preferred: 1 * time.Second, + valid: 2 * time.Second, + pLength: 32, + prefix: net.ParseIP("2001:db8::"), + iaprefix: &IAPrefix{ + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + PrefixLength: 32, + Prefix: net.ParseIP("2001:db8::"), + }, + }, + { + desc: "1s preferred, 2s valid, '2001:db8::6:1/64', option client ID [0 1]", + preferred: 1 * time.Second, + valid: 2 * time.Second, + pLength: 64, + prefix: net.ParseIP("2001:db8::6:1"), + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + iaprefix: &IAPrefix{ + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + PrefixLength: 64, + Prefix: net.ParseIP("2001:db8::6:1"), + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iaprefix, err := NewIAPrefix(tt.preferred, tt.valid, tt.pLength, tt.prefix, tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error for NewIAPrefix: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + want, err := tt.iaprefix.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iaprefix.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IAPrefix bytes:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestIAPrefixUnmarshalBinary verifies that IAPrefix.UnmarshalBinary produces +// a correct IAPrefix value or error for an input buffer. +func TestIAPrefixUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + iaprefix *IAPrefix + err error + }{ + { + desc: "one byte IAPrefix", + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "24 bytes IAPrefix", + buf: bytes.Repeat([]byte{0}, 24), + err: io.ErrUnexpectedEOF, + }, + { + desc: "preferred greater than valid lifetime", + buf: append([]byte{ + 0, 0, 0, 2, + 0, 0, 0, 1, + }, bytes.Repeat([]byte{0}, 17)...), + err: ErrInvalidLifetimes, + }, + { + desc: "invalid options (length mismatch)", + buf: []byte{ + 0, 0, 0, 1, + 0, 0, 0, 2, + 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, + }, + err: dhcp6.ErrInvalidOptions, + }, + { + desc: "1s preferred, 2s valid, '2001:db8::/32', no options", + buf: []byte{ + 0, 0, 0, 1, + 0, 0, 0, 2, + 32, + 32, 1, 13, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + iaprefix: &IAPrefix{ + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + PrefixLength: 32, + Prefix: net.ParseIP("2001:db8::"), + }, + }, + { + desc: "1s preferred, 2s valid, '2001:db8::6:1/64', option client ID [0 1]", + buf: []byte{ + 0, 0, 0, 1, + 0, 0, 0, 2, + 64, + 32, 1, 13, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 1, + 0, 1, 0, 2, 0, 1, + }, + iaprefix: &IAPrefix{ + PreferredLifetime: 1 * time.Second, + ValidLifetime: 2 * time.Second, + PrefixLength: 64, + Prefix: net.ParseIP("2001:db8::6:1"), + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iaprefix := new(IAPrefix) + if err := iaprefix.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error for parseIAPrefix: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + want, err := tt.iaprefix.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iaprefix.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IAPrefix bytes for parseIAPrefix:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iata.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iata.go new file mode 100644 index 00000000..c93a3cfa --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iata.go @@ -0,0 +1,67 @@ +package dhcp6opts + +import ( + "io" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// IATA represents an Identity Association for Temporary Addresses, as +// defined in RFC 3315, Section 22.5. +// +// Multiple IATAs may be present in a single DHCP request. +type IATA struct { + // IAID specifies a DHCP identity association identifier. The IAID + // is a unique, client-generated identifier. + IAID [4]byte + + // Options specifies a map of DHCP options specific to this IATA. + // Its methods can be used to retrieve data from an incoming IATA, or send + // data with an outgoing IATA. + Options dhcp6.Options +} + +// NewIATA creates a new IATA from an IAID and an Options map. If an Options +// map is not specified, a new one will be allocated. +func NewIATA(iaid [4]byte, options dhcp6.Options) *IATA { + if options == nil { + options = make(dhcp6.Options) + } + + return &IATA{ + IAID: iaid, + Options: options, + } +} + +// MarshalBinary allocates a byte slice containing the data from a IATA. +func (i *IATA) MarshalBinary() ([]byte, error) { + // 4 bytes: IAID + // N bytes: options slice byte count + b := buffer.New(nil) + + b.WriteBytes(i.IAID[:]) + opts, err := i.Options.MarshalBinary() + if err != nil { + return nil, err + } + b.WriteBytes(opts) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a IATA. +// +// If the byte slice does not contain enough data to form a valid IATA, +// io.ErrUnexpectedEOF is returned. +func (i *IATA) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // IATA must contain at least an IAID. + if b.Len() < 4 { + return io.ErrUnexpectedEOF + } + + b.ReadBytes(i.IAID[:]) + return (&i.Options).UnmarshalBinary(b.Remaining()) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iata_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iata_test.go new file mode 100644 index 00000000..a721186e --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/iata_test.go @@ -0,0 +1,113 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/mdlayher/dhcp6" +) + +// TestNewIATA verifies that NewIATA creates a proper IATA value for +// input values. +func TestNewIATA(t *testing.T) { + var tests = []struct { + desc string + iaid [4]byte + options dhcp6.Options + iata *IATA + }{ + { + desc: "all zero values", + iata: &IATA{}, + }, + { + desc: "[0 1 2 3] IAID, option client ID [0 1]", + iaid: [4]byte{0, 1, 2, 3}, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + iata: &IATA{ + IAID: [4]byte{0, 1, 2, 3}, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iata := NewIATA(tt.iaid, tt.options) + + want, err := tt.iata.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iata.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected IATA bytes for NewIATA(%v, %v)\n- want: %v\n- got: %v", + i, tt.desc, tt.iaid, tt.options, want, got) + } + } +} + +// TestIATAUnmarshalBinary verifies that IATAUnmarshalBinary produces a +// correct IATA value or error for an input buffer. +func TestIATAUnmarshalBinary(t *testing.T) { + var tests = []struct { + buf []byte + iata *IATA + options dhcp6.Options + err error + }{ + { + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + buf: bytes.Repeat([]byte{0}, 3), + err: io.ErrUnexpectedEOF, + }, + { + buf: []byte{ + 1, 2, 3, 4, + 0, 1, 0, 1, + }, + err: dhcp6.ErrInvalidOptions, + }, + { + buf: []byte{ + 1, 2, 3, 4, + 0, 1, 0, 2, 0, 1, + }, + iata: &IATA{ + IAID: [4]byte{1, 2, 3, 4}, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + iata := new(IATA) + if err := iata.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] unexpected error for parseIATA(%v): %v != %v", + i, tt.buf, want, got) + } + + continue + } + + if want, got := tt.iata, iata; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected IATA for parseIATA(%v):\n- want: %v\n- got: %v", + i, tt.buf, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/miscoptions.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/miscoptions.go new file mode 100644 index 00000000..7cfef9bc --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/miscoptions.go @@ -0,0 +1,450 @@ +package dhcp6opts + +import ( + "io" + "math" + "net" + "net/url" + "time" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// A OptionRequestOption is a list OptionCode, as defined in RFC 3315, Section 22.7. +// +// The Option Request option is used to identify a list of options in a +// message between a client and a server. +type OptionRequestOption []dhcp6.OptionCode + +// MarshalBinary allocates a byte slice containing the data from a OptionRequestOption. +func (oro OptionRequestOption) MarshalBinary() ([]byte, error) { + b := buffer.New(nil) + for _, opt := range oro { + b.Write16(uint16(opt)) + } + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a OptionRequestOption. +// +// If the length of byte slice is not be be divisible by 2, +// errInvalidOptionRequest is returned. +func (oro *OptionRequestOption) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Length must be divisible by 2. + if b.Len()%2 != 0 { + return io.ErrUnexpectedEOF + } + + // Fill slice by parsing every two bytes using index i. + *oro = make(OptionRequestOption, 0, b.Len()/2) + for b.Len() > 1 { + *oro = append(*oro, dhcp6.OptionCode(b.Read16())) + } + return nil +} + +// A Preference is a preference value, as defined in RFC 3315, Section 22.8. +// +// A preference value is sent by a server to a client to affect the selection +// of a server by the client. +type Preference uint8 + +// MarshalBinary allocates a byte slice containing the data from a Preference. +func (p Preference) MarshalBinary() ([]byte, error) { + return []byte{byte(p)}, nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a Preference. +// +// If the byte slice is not exactly 1 byte in length, io.ErrUnexpectedEOF is +// returned. +func (p *Preference) UnmarshalBinary(b []byte) error { + if len(b) != 1 { + return io.ErrUnexpectedEOF + } + + *p = Preference(b[0]) + return nil +} + +// An ElapsedTime is a client's elapsed request time value, as defined in RFC +// 3315, Section 22.9. +// +// The duration returned reports the time elapsed during a DHCP transaction, +// as reported by a client. +type ElapsedTime time.Duration + +// MarshalBinary allocates a byte slice containing the data from an +// ElapsedTime. +func (t ElapsedTime) MarshalBinary() ([]byte, error) { + b := buffer.New(nil) + + unit := 10 * time.Millisecond + // The elapsed time value is an unsigned, 16 bit integer. + // The client uses the value 0xffff to represent any + // elapsed time values greater than the largest time value + // that can be represented in the Elapsed Time option. + if max := time.Duration(math.MaxUint16) * unit; time.Duration(t) > max { + t = ElapsedTime(max) + } + b.Write16(uint16(time.Duration(t) / unit)) + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a ElapsedTime. +// +// If the byte slice is not exactly 2 bytes in length, io.ErrUnexpectedEOF is +// returned. +func (t *ElapsedTime) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + if b.Len() != 2 { + return io.ErrUnexpectedEOF + } + + // Time is reported in hundredths of seconds, so we convert it to a more + // manageable milliseconds + *t = ElapsedTime(time.Duration(b.Read16()) * 10 * time.Millisecond) + return nil +} + +// An IP is an IPv6 address. The IP type is provided for convenience. +// It can be used to easily add IPv6 addresses to an Options map. +type IP net.IP + +// MarshalBinary allocates a byte slice containing the data from a IP. +func (i IP) MarshalBinary() ([]byte, error) { + ip := make([]byte, net.IPv6len) + copy(ip, i) + return ip, nil +} + +// UnmarshalBinary unmarshals a raw byte slice into an IP. +// +// If the byte slice is not an IPv6 address, io.ErrUnexpectedEOF is +// returned. +func (i *IP) UnmarshalBinary(b []byte) error { + if len(b) != net.IPv6len { + return io.ErrUnexpectedEOF + } + + if ip := net.IP(b); ip.To4() != nil { + return io.ErrUnexpectedEOF + } + + *i = make(IP, net.IPv6len) + copy(*i, b) + return nil +} + +// IPs represents a list of IPv6 addresses. +type IPs []net.IP + +// MarshalBinary allocates a byte slice containing the consecutive data of all +// IPs. +func (i IPs) MarshalBinary() ([]byte, error) { + ips := make([]byte, 0, len(i)*net.IPv6len) + for _, ip := range i { + ips = append(ips, ip.To16()...) + } + return ips, nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a list of IPs. +// +// If the byte slice contains any non-IPv6 addresses, io.ErrUnexpectedEOF is +// returned. +func (i *IPs) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + if b.Len()%net.IPv6len != 0 || b.Len() == 0 { + return io.ErrUnexpectedEOF + } + + *i = make(IPs, 0, b.Len()/net.IPv6len) + for b.Len() > 0 { + ip := make(net.IP, net.IPv6len) + b.ReadBytes(ip) + *i = append(*i, ip) + } + return nil +} + +// Data is a raw collection of byte slices, typically carrying user class +// data, vendor class data, or PXE boot file parameters. +type Data [][]byte + +// MarshalBinary allocates a byte slice containing the data from a Data +// structure. +func (d Data) MarshalBinary() ([]byte, error) { + // Count number of bytes needed to allocate at once + var c int + for _, dd := range d { + c += 2 + len(dd) + } + + b := buffer.New(nil) + d.Marshal(b) + return b.Data(), nil +} + +// Marshal marshals to a given buffer from a Data structure. +func (d Data) Marshal(b *buffer.Buffer) { + for _, dd := range d { + // 2 byte: length of data + b.Write16(uint16(len(dd))) + + // N bytes: actual raw data + b.WriteBytes(dd) + } +} + +// UnmarshalBinary unmarshals a raw byte slice into a Data structure. +func (d *Data) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + return d.Unmarshal(b) +} + +// Unmarshal marshals from a given buffer into a Data structure. +// Data is packed in the form: +// - 2 bytes: data length +// - N bytes: raw data +func (d *Data) Unmarshal(b *buffer.Buffer) error { + data := make(Data, 0, b.Len()) + + // Iterate until not enough bytes remain to parse another length value + for b.Len() > 1 { + // 2 bytes: length of data. + length := int(b.Read16()) + + // N bytes: actual data. + data = append(data, b.Consume(length)) + } + + // At least one instance of class data must be present + if len(data) == 0 { + return io.ErrUnexpectedEOF + } + + // If we encounter any trailing bytes, report an error + if b.Len() != 0 { + return io.ErrUnexpectedEOF + } + + *d = data + return nil +} + +// A URL is a uniform resource locater. The URL type is provided for +// convenience. It can be used to easily add URLs to an Options map. +type URL url.URL + +// MarshalBinary allocates a byte slice containing the data from a URL. +func (u URL) MarshalBinary() ([]byte, error) { + uu := url.URL(u) + return []byte(uu.String()), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into an URL. +// +// If the byte slice is not an URLv6 address, io.ErrUnexpectedEOF is +// returned. +func (u *URL) UnmarshalBinary(b []byte) error { + uu, err := url.Parse(string(b)) + if err != nil { + return err + } + + *u = URL(*uu) + return nil +} + +// ArchTypes is a slice of ArchType values. It is provided for convenient +// marshaling and unmarshaling of a slice of ArchType values from an Options +// map. +type ArchTypes []ArchType + +// MarshalBinary allocates a byte slice containing the data from ArchTypes. +func (a ArchTypes) MarshalBinary() ([]byte, error) { + b := buffer.New(nil) + for _, aType := range a { + b.Write16(uint16(aType)) + } + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into an ArchTypes slice. +// +// If the byte slice is less than 2 bytes in length, or is not a length that +// is divisible by 2, io.ErrUnexpectedEOF is returned. +func (a *ArchTypes) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Length must be at least 2, and divisible by 2. + if b.Len() < 2 || b.Len()%2 != 0 { + return io.ErrUnexpectedEOF + } + + // Allocate ArchTypes at once and unpack every two bytes into an element + arch := make(ArchTypes, 0, b.Len()/2) + for b.Len() > 1 { + arch = append(arch, ArchType(b.Read16())) + } + + *a = arch + return nil +} + +// A NII is a Client Network Interface Identifier, as defined in RFC 5970, +// Section 3.4. +// +// A NII is used to indicate a client's level of Universal Network Device +// Interface (UNDI) support. +type NII struct { + // Type specifies a network interface type. + Type uint8 + + // Major specifies the UNDI major revisision which this client supports. + Major uint8 + + // Minor specifies the UNDI minor revision which this client supports. + Minor uint8 +} + +// MarshalBinary allocates a byte slice containing the data from a NII. +func (n *NII) MarshalBinary() ([]byte, error) { + b := make([]byte, 3) + + b[0] = n.Type + b[1] = n.Major + b[2] = n.Minor + + return b, nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a NII. +// +// If the byte slice is not exactly 3 bytes in length, io.ErrUnexpectedEOF +// is returned. +func (n *NII) UnmarshalBinary(b []byte) error { + // Length must be exactly 3 + if len(b) != 3 { + return io.ErrUnexpectedEOF + } + + n.Type = b[0] + n.Major = b[1] + n.Minor = b[2] + + return nil +} + +// A RelayMessageOption is used by a DHCPv6 Relay Agent to relay messages +// between clients and servers or other relay agents through Relay-Forward +// and Relay-Reply message types. The original client DHCP message (i.e., +// the packet payload, excluding UDP and IP headers) is encapsulated in a +// Relay Message option. +type RelayMessageOption []byte + +// MarshalBinary allocates a byte slice containing the data from a RelayMessageOption. +func (r *RelayMessageOption) MarshalBinary() ([]byte, error) { + return *r, nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a RelayMessageOption. +func (r *RelayMessageOption) UnmarshalBinary(b []byte) error { + *r = make([]byte, len(b)) + copy(*r, b) + return nil +} + +// SetClientServerMessage sets a Packet (e.g. Solicit, Advertise ...) into this option. +func (r *RelayMessageOption) SetClientServerMessage(p *dhcp6.Packet) error { + b, err := p.MarshalBinary() + if err != nil { + return err + } + + *r = b + return nil +} + +// SetRelayMessage sets a RelayMessage (e.g. Relay Forward, Relay Reply) into this option. +func (r *RelayMessageOption) SetRelayMessage(p *RelayMessage) error { + b, err := p.MarshalBinary() + if err != nil { + return err + } + + *r = b + return nil +} + +// ClientServerMessage gets the client server message (e.g. Solicit, +// Advertise ...) into this option (when hopcount = 0 of outer RelayMessage). +func (r *RelayMessageOption) ClientServerMessage() (*dhcp6.Packet, error) { + p := new(dhcp6.Packet) + err := p.UnmarshalBinary(*r) + if err != nil { + return nil, err + } + + return p, nil +} + +// RelayMessage gets the relay message (e.g. Relay Forward, Relay Reply) into +// this option (when hopcount > 0 of outer RelayMessage). +func (r *RelayMessageOption) RelayMessage() (*RelayMessage, error) { + rm := new(RelayMessage) + err := rm.UnmarshalBinary(*r) + if err != nil { + return nil, err + } + + return rm, nil +} + +// An InterfaceID is an opaque value of arbitrary length generated +// by the relay agent to identify one of the +// relay agent's interfaces. +type InterfaceID []byte + +// MarshalBinary allocates a byte slice containing the data from a InterfaceID. +func (i *InterfaceID) MarshalBinary() ([]byte, error) { + return *i, nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a InterfaceID. +func (i *InterfaceID) UnmarshalBinary(b []byte) error { + *i = make([]byte, len(b)) + copy(*i, b) + return nil +} + +// A BootFileParam are boot file parameters. +type BootFileParam []string + +// MarshalBinary allocates a byte slice containing the data from a +// BootFileParam. +func (bfp BootFileParam) MarshalBinary() ([]byte, error) { + // Convert []string to [][]byte. + bb := make(Data, 0, len(bfp)) + for _, param := range bfp { + bb = append(bb, []byte(param)) + } + return bb.MarshalBinary() +} + +// UnmarshalBinary unmarshals a raw byte slice into a BootFileParam. +func (bfp *BootFileParam) UnmarshalBinary(b []byte) error { + var d Data + if err := (&d).UnmarshalBinary(b); err != nil { + return err + } + // Convert [][]byte to []string. + *bfp = make([]string, 0, len(d)) + for _, param := range d { + *bfp = append(*bfp, string(param)) + } + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/options.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/options.go new file mode 100644 index 00000000..c4baeb15 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/options.go @@ -0,0 +1,430 @@ +package dhcp6opts + +import ( + "github.com/mdlayher/dhcp6" +) + +// GetClientID returns the Client Identifier Option value, as described in RFC +// 3315, Section 22.2. +// +// The DUID returned allows unique identification of a client to a server. +func GetClientID(o dhcp6.Options) (DUID, error) { + v, err := o.GetOne(dhcp6.OptionClientID) + if err != nil { + return nil, err + } + + return parseDUID(v) +} + +// GetServerID returns the Server Identifier Option value, as described in RFC +// 3315, Section 22.3. +// +// The DUID returned allows unique identification of a server to a client. +func GetServerID(o dhcp6.Options) (DUID, error) { + v, err := o.GetOne(dhcp6.OptionServerID) + if err != nil { + return nil, err + } + + return parseDUID(v) +} + +// GetIANA returns the Identity Association for Non-temporary Addresses Option +// value, as described in RFC 3315, Section 22.4. +// +// Multiple IANA values may be present in a single DHCP request. +func GetIANA(o dhcp6.Options) ([]*IANA, error) { + vv, err := o.Get(dhcp6.OptionIANA) + if err != nil { + return nil, err + } + + // Parse each IA_NA value + iana := make([]*IANA, len(vv)) + for i := range vv { + iana[i] = &IANA{} + if err := iana[i].UnmarshalBinary(vv[i]); err != nil { + return nil, err + } + } + return iana, nil +} + +// GetIATA returns the Identity Association for Temporary Addresses Option +// value, as described in RFC 3315, Section 22.5. +// +// Multiple IATA values may be present in a single DHCP request. +func GetIATA(o dhcp6.Options) ([]*IATA, error) { + vv, err := o.Get(dhcp6.OptionIATA) + if err != nil { + return nil, err + } + + // Parse each IA_NA value + iata := make([]*IATA, len(vv)) + for i := range vv { + iata[i] = &IATA{} + if err := iata[i].UnmarshalBinary(vv[i]); err != nil { + return nil, err + } + } + return iata, nil +} + +// GetIAAddr returns the Identity Association Address Option value, as described +// in RFC 3315, Section 22.6. +// +// The IAAddr option must always appear encapsulated in the Options map of a +// IANA or IATA option. Multiple IAAddr values may be present in a single DHCP +// request. +func GetIAAddr(o dhcp6.Options) ([]*IAAddr, error) { + vv, err := o.Get(dhcp6.OptionIAAddr) + if err != nil { + return nil, err + } + + iaAddr := make([]*IAAddr, len(vv)) + for i := range vv { + iaAddr[i] = &IAAddr{} + if err := iaAddr[i].UnmarshalBinary(vv[i]); err != nil { + return nil, err + } + } + return iaAddr, nil +} + +// GetOptionRequest returns the Option Request Option value, as described in +// RFC 3315, Section 22.7. +// +// The slice of OptionCode values indicates the options a DHCP client is +// interested in receiving from a server. +func GetOptionRequest(o dhcp6.Options) (OptionRequestOption, error) { + v, err := o.GetOne(dhcp6.OptionORO) + if err != nil { + return nil, err + } + + var oro OptionRequestOption + err = oro.UnmarshalBinary(v) + return oro, err +} + +// GetPreference returns the Preference Option value, as described in RFC 3315, +// Section 22.8. +// +// The integer preference value is sent by a server to a client to affect the +// selection of a server by the client. +func GetPreference(o dhcp6.Options) (Preference, error) { + v, err := o.GetOne(dhcp6.OptionPreference) + if err != nil { + return 0, err + } + + var p Preference + err = (&p).UnmarshalBinary(v) + return p, err +} + +// GetElapsedTime returns the Elapsed Time Option value, as described in RFC +// 3315, Section 22.9. +// +// The time.Duration returned reports the time elapsed during a DHCP +// transaction, as reported by a client. +func GetElapsedTime(o dhcp6.Options) (ElapsedTime, error) { + v, err := o.GetOne(dhcp6.OptionElapsedTime) + if err != nil { + return 0, err + } + + var t ElapsedTime + err = (&t).UnmarshalBinary(v) + return t, err +} + +// GetRelayMessageOption returns the Relay Message Option value, as described +// in RFC 3315, Section 22.10. +// +// The RelayMessage option carries a DHCP message in a Relay-forward or +// Relay-reply message. +func GetRelayMessageOption(o dhcp6.Options) (RelayMessageOption, error) { + v, err := o.GetOne(dhcp6.OptionRelayMsg) + if err != nil { + return nil, err + } + + var r RelayMessageOption + err = (&r).UnmarshalBinary(v) + return r, err +} + +// GetAuthentication returns the Authentication Option value, as described in +// RFC 3315, Section 22.11. +// +// The Authentication option carries authentication information to +// authenticate the identity and contents of DHCP messages. +func GetAuthentication(o dhcp6.Options) (*Authentication, error) { + v, err := o.GetOne(dhcp6.OptionAuth) + if err != nil { + return nil, err + } + + a := new(Authentication) + err = a.UnmarshalBinary(v) + return a, err +} + +// GetUnicast returns the IP from a Unicast Option value, described in RFC +// 3315, Section 22.12. +// +// The IP return value indicates a server's IPv6 address, which a client may +// use to contact the server via unicast. +func GetUnicast(o dhcp6.Options) (IP, error) { + v, err := o.GetOne(dhcp6.OptionUnicast) + if err != nil { + return nil, err + } + + var ip IP + err = ip.UnmarshalBinary(v) + return ip, err +} + +// GetStatusCode returns the Status Code Option value, described in RFC 3315, +// Section 22.13. +// +// The StatusCode return value may be used to determine a code and an +// explanation for the status. +func GetStatusCode(o dhcp6.Options) (*StatusCode, error) { + v, err := o.GetOne(dhcp6.OptionStatusCode) + if err != nil { + return nil, err + } + + s := new(StatusCode) + err = s.UnmarshalBinary(v) + return s, err +} + +// GetRapidCommit returns the Rapid Commit Option value, described in RFC 3315, +// Section 22.14. +// +// Nil is returned if OptionRapidCommit was present in the Options map. +func GetRapidCommit(o dhcp6.Options) error { + v, err := o.GetOne(dhcp6.OptionRapidCommit) + if err != nil { + return err + } + + // Data must be completely empty; presence of the Rapid Commit option + // indicates it is requested. + if len(v) != 0 { + return dhcp6.ErrInvalidPacket + } + return nil +} + +// GetUserClass returns the User Class Option value, described in RFC 3315, +// Section 22.15. +// +// The Data structure returned contains any raw class data present in +// the option. +func GetUserClass(o dhcp6.Options) (Data, error) { + v, err := o.GetOne(dhcp6.OptionUserClass) + if err != nil { + return nil, err + } + + var d Data + err = d.UnmarshalBinary(v) + return d, err +} + +// GetVendorClass returns the Vendor Class Option value, described in RFC 3315, +// Section 22.16. +// +// The VendorClass structure returned contains VendorClass in +// the option. +func GetVendorClass(o dhcp6.Options) (*VendorClass, error) { + v, err := o.GetOne(dhcp6.OptionVendorClass) + if err != nil { + return nil, err + } + + vc := new(VendorClass) + err = vc.UnmarshalBinary(v) + return vc, err +} + +// GetVendorOpts returns the Vendor-specific Information Option value, +// described in RFC 3315, Section 22.17. +// +// The VendorOpts structure returned contains Vendor-specific Information data +// present in the option. +func GetVendorOpts(o dhcp6.Options) (*VendorOpts, error) { + v, err := o.GetOne(dhcp6.OptionVendorOpts) + if err != nil { + return nil, err + } + + vo := new(VendorOpts) + err = vo.UnmarshalBinary(v) + return vo, err +} + +// GetInterfaceID returns the Interface-Id Option value, described in RFC 3315, +// Section 22.18. +// +// The InterfaceID structure returned contains any raw class data present in +// the option. +func GetInterfaceID(o dhcp6.Options) (InterfaceID, error) { + v, err := o.GetOne(dhcp6.OptionInterfaceID) + if err != nil { + return nil, err + } + + var i InterfaceID + err = i.UnmarshalBinary(v) + return i, err +} + +// GetIAPD returns the Identity Association for Prefix Delegation Option value, +// described in RFC 3633, Section 9. +// +// Multiple IAPD values may be present in a a single DHCP request. +func GetIAPD(o dhcp6.Options) ([]*IAPD, error) { + vv, err := o.Get(dhcp6.OptionIAPD) + if err != nil { + return nil, err + } + + // Parse each IA_PD value + iapd := make([]*IAPD, len(vv)) + for i := range vv { + iapd[i] = &IAPD{} + if err := iapd[i].UnmarshalBinary(vv[i]); err != nil { + return nil, err + } + } + + return iapd, nil +} + +// GetIAPrefix returns the Identity Association Prefix Option value, as +// described in RFC 3633, Section 10. +// +// Multiple IAPrefix values may be present in a a single DHCP request. +func GetIAPrefix(o dhcp6.Options) ([]*IAPrefix, error) { + vv, err := o.Get(dhcp6.OptionIAPrefix) + if err != nil { + return nil, err + } + + // Parse each IAPrefix value + iaPrefix := make([]*IAPrefix, len(vv)) + for i := range vv { + iaPrefix[i] = &IAPrefix{} + if err := iaPrefix[i].UnmarshalBinary(vv[i]); err != nil { + return nil, err + } + } + + return iaPrefix, nil +} + +// GetRemoteIdentifier returns the Remote Identifier, described in RFC 4649. +// +// This option may be added by DHCPv6 relay agents that terminate +// switched or permanent circuits and have mechanisms to identify the +// remote host end of the circuit. +func GetRemoteIdentifier(o dhcp6.Options) (*RemoteIdentifier, error) { + v, err := o.GetOne(dhcp6.OptionRemoteIdentifier) + if err != nil { + return nil, err + } + + r := new(RemoteIdentifier) + err = r.UnmarshalBinary(v) + return r, err +} + +// GetBootFileURL returns the Boot File URL Option value, described in RFC +// 5970, Section 3.1. +// +// The URL return value contains a URL which may be used by clients to obtain +// a boot file for PXE. +func GetBootFileURL(o dhcp6.Options) (*URL, error) { + v, err := o.GetOne(dhcp6.OptionBootFileURL) + if err != nil { + return nil, err + } + + u := new(URL) + err = u.UnmarshalBinary(v) + return u, err +} + +// GetBootFileParam returns the Boot File Parameters Option value, described in +// RFC 5970, Section 3.2. +// +// The Data structure returned contains any parameters needed for a boot +// file, such as a root filesystem label or a path to a configuration file for +// further chainloading. +func GetBootFileParam(o dhcp6.Options) (BootFileParam, error) { + v, err := o.GetOne(dhcp6.OptionBootFileParam) + if err != nil { + return nil, err + } + + var bfp BootFileParam + err = bfp.UnmarshalBinary(v) + return bfp, err +} + +// GetClientArchType returns the Client System Architecture Type Option value, +// described in RFC 5970, Section 3.3. +// +// The ArchTypes slice returned contains a list of one or more ArchType values. +// The first ArchType listed is the client's most preferable value. +func GetClientArchType(o dhcp6.Options) (ArchTypes, error) { + v, err := o.GetOne(dhcp6.OptionClientArchType) + if err != nil { + return nil, err + } + + var a ArchTypes + err = a.UnmarshalBinary(v) + return a, err +} + +// GetNII returns the Client Network Interface Identifier Option value, +// described in RFC 5970, Section 3.4. +// +// The NII value returned indicates a client's level of Universal Network +// Device Interface (UNDI) support. +func GetNII(o dhcp6.Options) (*NII, error) { + v, err := o.GetOne(dhcp6.OptionNII) + if err != nil { + return nil, err + } + + n := new(NII) + err = n.UnmarshalBinary(v) + return n, err +} + +// GetDNSServers returns the DNS Recursive Name Servers Option value, as +// described in RFC 3646, Section 3. +// +// The DNS servers are listed in the order of preference for use by the client +// resolver. +func GetDNSServers(o dhcp6.Options) (IPs, error) { + v, err := o.GetOne(dhcp6.OptionDNSServers) + if err != nil { + return nil, err + } + + var ips IPs + err = ips.UnmarshalBinary(v) + return ips, err +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/options_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/options_test.go new file mode 100644 index 00000000..51b1fc85 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/options_test.go @@ -0,0 +1,1909 @@ +package dhcp6opts + +import ( + "bytes" + "encoding" + "encoding/binary" + "io" + "net" + "net/url" + "reflect" + "testing" + "time" + + "github.com/mdlayher/dhcp6" +) + +// TestOptionsAddBinaryMarshaler verifies that dhcp6.Options.Add correctly creates or +// appends OptionCode keys with BinaryMarshaler bytes values to an dhcp6.Options map. +// +// TODO: This should just be atest for each of the binary marshalers. +func TestOptionsAddBinaryMarshaler(t *testing.T) { + var tests = []struct { + desc string + code dhcp6.OptionCode + bin encoding.BinaryMarshaler + options dhcp6.Options + }{ + { + desc: "DUID-LLT", + code: dhcp6.OptionClientID, + bin: &DUIDLLT{ + Type: DUIDTypeLLT, + HardwareType: 1, + Time: duidLLTTime.Add(1 * time.Minute).Sub(duidLLTTime), + HardwareAddr: net.HardwareAddr([]byte{0, 1, 0, 1, 0, 1}), + }, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{ + 0, 1, + 0, 1, + 0, 0, 0, 60, + 0, 1, 0, 1, 0, 1, + }}, + }, + }, + { + desc: "DUID-EN", + code: dhcp6.OptionClientID, + bin: &DUIDEN{ + Type: DUIDTypeEN, + EnterpriseNumber: 100, + Identifier: []byte{0, 1, 2, 3, 4}, + }, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{ + 0, 2, + 0, 0, 0, 100, + 0, 1, 2, 3, 4, + }}, + }, + }, + { + desc: "DUID-LL", + code: dhcp6.OptionClientID, + bin: &DUIDLL{ + Type: DUIDTypeLL, + HardwareType: 1, + HardwareAddr: net.HardwareAddr([]byte{0, 1, 0, 1, 0, 1}), + }, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{ + 0, 3, + 0, 1, + 0, 1, 0, 1, 0, 1, + }}, + }, + }, + { + desc: "DUID-UUID", + code: dhcp6.OptionClientID, + bin: &DUIDUUID{ + Type: DUIDTypeUUID, + UUID: [16]byte{ + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + }, + }, + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{ + 0, 4, + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + }}, + }, + }, + { + desc: "IA_NA", + code: dhcp6.OptionIANA, + bin: &IANA{ + IAID: [4]byte{0, 1, 2, 3}, + T1: 30 * time.Second, + T2: 60 * time.Second, + }, + options: dhcp6.Options{ + dhcp6.OptionIANA: [][]byte{{ + 0, 1, 2, 3, + 0, 0, 0, 30, + 0, 0, 0, 60, + }}, + }, + }, + { + desc: "IA_TA", + code: dhcp6.OptionIATA, + bin: &IATA{ + IAID: [4]byte{0, 1, 2, 3}, + }, + options: dhcp6.Options{ + dhcp6.OptionIATA: [][]byte{{ + 0, 1, 2, 3, + }}, + }, + }, + { + desc: "IAAddr", + code: dhcp6.OptionIAAddr, + bin: &IAAddr{ + IP: net.IPv6loopback, + PreferredLifetime: 30 * time.Second, + ValidLifetime: 60 * time.Second, + }, + options: dhcp6.Options{ + dhcp6.OptionIAAddr: [][]byte{{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 30, + 0, 0, 0, 60, + }}, + }, + }, + { + desc: "Preference", + code: dhcp6.OptionPreference, + bin: Preference(255), + options: dhcp6.Options{ + dhcp6.OptionPreference: [][]byte{{255}}, + }, + }, + { + desc: "ElapsedTime", + code: dhcp6.OptionElapsedTime, + bin: ElapsedTime(60 * time.Second), + options: dhcp6.Options{ + dhcp6.OptionElapsedTime: [][]byte{{23, 112}}, + }, + }, + { + desc: "Unicast IP", + code: dhcp6.OptionUnicast, + bin: IP(net.IPv6loopback), + options: dhcp6.Options{ + dhcp6.OptionUnicast: [][]byte{{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + }}, + }, + }, + { + desc: "StatusCode", + code: dhcp6.OptionStatusCode, + bin: &StatusCode{ + Code: dhcp6.StatusSuccess, + Message: "hello world", + }, + options: dhcp6.Options{ + dhcp6.OptionStatusCode: [][]byte{{ + 0, 0, + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', + }}, + }, + }, + { + desc: "RapidCommit", + code: dhcp6.OptionRapidCommit, + bin: nil, + options: dhcp6.Options{ + dhcp6.OptionRapidCommit: [][]byte{nil}, + }, + }, + { + desc: "Data (UserClass, VendorClass, BootFileParam)", + code: dhcp6.OptionUserClass, + bin: Data{ + []byte{0}, + []byte{0, 1}, + []byte{0, 1, 2}, + }, + options: dhcp6.Options{ + dhcp6.OptionUserClass: [][]byte{{ + 0, 1, 0, + 0, 2, 0, 1, + 0, 3, 0, 1, 2, + }}, + }, + }, + { + desc: "IA_PD", + code: dhcp6.OptionIAPD, + bin: &IAPD{ + IAID: [4]byte{0, 1, 2, 3}, + T1: 30 * time.Second, + T2: 60 * time.Second, + }, + options: dhcp6.Options{ + dhcp6.OptionIAPD: [][]byte{{ + 0, 1, 2, 3, + 0, 0, 0, 30, + 0, 0, 0, 60, + }}, + }, + }, + { + desc: "IAPrefix", + code: dhcp6.OptionIAPrefix, + bin: &IAPrefix{ + PreferredLifetime: 30 * time.Second, + ValidLifetime: 60 * time.Second, + PrefixLength: 64, + Prefix: net.IP{ + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + options: dhcp6.Options{ + dhcp6.OptionIAPrefix: [][]byte{{ + 0, 0, 0, 30, + 0, 0, 0, 60, + 64, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + }}, + }, + }, + { + desc: "URL", + code: dhcp6.OptionBootFileURL, + bin: &URL{ + Scheme: "tftp", + Host: "192.168.1.1:69", + }, + options: dhcp6.Options{ + dhcp6.OptionBootFileURL: [][]byte{[]byte("tftp://192.168.1.1:69")}, + }, + }, + { + desc: "ArchTypes", + code: dhcp6.OptionClientArchType, + bin: ArchTypes{ + ArchTypeEFIx8664, + ArchTypeIntelx86PC, + ArchTypeIntelLeanClient, + }, + options: dhcp6.Options{ + dhcp6.OptionClientArchType: [][]byte{{0, 9, 0, 0, 0, 5}}, + }, + }, + { + desc: "NII", + code: dhcp6.OptionNII, + bin: &NII{ + Type: 1, + Major: 2, + Minor: 3, + }, + options: dhcp6.Options{ + dhcp6.OptionNII: [][]byte{{1, 2, 3}}, + }, + }, + } + + for i, tt := range tests { + o := make(dhcp6.Options) + if err := o.Add(tt.code, tt.bin); err != nil { + t.Fatal(err) + } + + if want, got := tt.options, o; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected dhcp6.Options map:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetClientID verifies that dhcp6.Options.ClientID properly parses and returns +// a DUID value, if one is available with OptionClientID. +func TestGetClientID(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + duid DUID + err error + }{ + { + desc: "OptionClientID not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionClientID present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{ + 0, 3, + 0, 1, + 0, 1, 0, 1, 0, 1, + }}, + }, + duid: &DUIDLL{ + Type: DUIDTypeLL, + HardwareType: 1, + HardwareAddr: []byte{0, 1, 0, 1, 0, 1}, + }, + }, + } + + for i, tt := range tests { + // DUID parsing is tested elsewhere, so errors should automatically fail + // test here + duid, err := GetClientID(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected err for dhcp6.Options.ClientID(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.duid, duid; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.ClientID():\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetServerID verifies that dhcp6.Options.ServerID properly parses and returns +// a DUID value, if one is available with OptionServerID. +func TestGetServerID(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + duid DUID + err error + }{ + { + desc: "OptionServerID not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionServerID present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionServerID: [][]byte{{ + 0, 3, + 0, 1, + 0, 1, 0, 1, 0, 1, + }}, + }, + duid: &DUIDLL{ + Type: DUIDTypeLL, + HardwareType: 1, + HardwareAddr: []byte{0, 1, 0, 1, 0, 1}, + }, + }, + } + + for i, tt := range tests { + // DUID parsing is tested elsewhere, so errors should automatically fail + // test here + duid, err := GetServerID(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected err for dhcp6.Options.ServerID(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.duid, duid; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.ServerID():\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetIANA verifies that dhcp6.Options.IANA properly parses and +// returns multiple IANA values, if one or more are available with OptionIANA. +func TestGetIANA(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + iana []*IANA + err error + }{ + { + desc: "OptionIANA not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionIANA present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionIANA: [][]byte{bytes.Repeat([]byte{0}, 11)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "one OptionIANA present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIANA: [][]byte{{ + 1, 2, 3, 4, + 0, 0, 0, 30, + 0, 0, 0, 60, + }}, + }, + iana: []*IANA{ + { + IAID: [4]byte{1, 2, 3, 4}, + T1: 30 * time.Second, + T2: 60 * time.Second, + }, + }, + }, + { + desc: "two OptionIANA present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIANA: [][]byte{ + append(bytes.Repeat([]byte{0}, 12), []byte{0, 1, 0, 1, 1}...), + append(bytes.Repeat([]byte{0}, 12), []byte{0, 2, 0, 1, 2}...), + }, + }, + iana: []*IANA{ + { + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{1}}, + }, + }, + { + Options: dhcp6.Options{ + dhcp6.OptionServerID: [][]byte{{2}}, + }, + }, + }, + }, + } + + for i, tt := range tests { + iana, err := GetIANA(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.IANA: %v != %v", + i, tt.desc, want, got) + } + continue + } + + for j := range tt.iana { + want, err := tt.iana[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iana[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.IANA():\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestGetIATA verifies that dhcp6.Options.IATA properly parses and +// returns multiple IATA values, if one or more are available with OptionIATA. +func TestGetIATA(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + iata []*IATA + err error + }{ + { + desc: "OptionIATA not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionIATA present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionIATA: [][]byte{{0, 0, 0}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "one OptionIATA present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIATA: [][]byte{{ + 1, 2, 3, 4, + }}, + }, + iata: []*IATA{ + { + IAID: [4]byte{1, 2, 3, 4}, + }, + }, + }, + { + desc: "two OptionIATA present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIATA: [][]byte{ + {0, 1, 2, 3, 0, 1, 0, 1, 1}, + {4, 5, 6, 7, 0, 2, 0, 1, 2}, + }, + }, + iata: []*IATA{ + { + IAID: [4]byte{0, 1, 2, 3}, + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{1}}, + }, + }, + { + IAID: [4]byte{4, 5, 6, 7}, + Options: dhcp6.Options{ + dhcp6.OptionServerID: [][]byte{{2}}, + }, + }, + }, + }, + } + + for i, tt := range tests { + iata, err := GetIATA(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.IATA: %v != %v", + i, tt.desc, want, got) + } + continue + } + + for j := range tt.iata { + want, err := tt.iata[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iata[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.IATA():\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestGetIAAddr verifies that dhcp6.Options.IAAddr properly parses and +// returns multiple IAAddr values, if one or more are available with +// OptionIAAddr. +func TestGetIAAddr(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + iaaddr []*IAAddr + err error + }{ + { + desc: "OptionIAAddr not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionIAAddr present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionIAAddr: [][]byte{bytes.Repeat([]byte{0}, 23)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "one OptionIAAddr present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIAAddr: [][]byte{{ + 0, 0, 0, 0, + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + 0, 0, 0, 30, + 0, 0, 0, 60, + }}, + }, + iaaddr: []*IAAddr{ + { + IP: net.IP{ + 0, 0, 0, 0, + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + }, + PreferredLifetime: 30 * time.Second, + ValidLifetime: 60 * time.Second, + }, + }, + }, + { + desc: "two OptionIAAddr present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIAAddr: [][]byte{ + bytes.Repeat([]byte{0}, 24), + bytes.Repeat([]byte{0}, 24), + }, + }, + iaaddr: []*IAAddr{ + { + IP: net.IPv6zero, + }, + { + IP: net.IPv6zero, + }, + }, + }, + } + + for i, tt := range tests { + iaaddr, err := GetIAAddr(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.IAAddr: %v != %v", + i, tt.desc, want, got) + } + continue + } + + for j := range tt.iaaddr { + want, err := tt.iaaddr[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iaaddr[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.IAAddr():\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestGetOptionRequest verifies that dhcp6.Options.OptionRequest properly parses +// and returns a slice of OptionCode values, if they are available with +// OptionORO. +func TestGetOptionRequest(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + codes OptionRequestOption + err error + }{ + { + desc: "OptionORO not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionORO present in dhcp6.Options map, but not even length", + options: dhcp6.Options{ + dhcp6.OptionORO: [][]byte{{0}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionORO present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionORO: [][]byte{{0, 1}}, + }, + codes: []dhcp6.OptionCode{1}, + }, + { + desc: "OptionORO present in dhcp6.Options map, with multiple values", + options: dhcp6.Options{ + dhcp6.OptionORO: [][]byte{{0, 1, 0, 2, 0, 3}}, + }, + codes: []dhcp6.OptionCode{1, 2, 3}, + }, + } + + for i, tt := range tests { + codes, err := GetOptionRequest(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.OptionRequest(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.codes, codes; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.OptionRequest():\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetPreference verifies that dhcp6.Options.Preference properly parses +// and returns an integer value, if it is available with OptionPreference. +func TestGetPreference(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + preference Preference + err error + }{ + { + desc: "OptionPreference not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionPreference present in dhcp6.Options map, but too short length", + options: dhcp6.Options{ + dhcp6.OptionPreference: [][]byte{{}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionPreference present in dhcp6.Options map, but too long length", + options: dhcp6.Options{ + dhcp6.OptionPreference: [][]byte{{0, 1}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionPreference present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionPreference: [][]byte{{255}}, + }, + preference: 255, + }, + } + + for i, tt := range tests { + preference, err := GetPreference(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.Preference(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.preference, preference; want != got { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.Preference(): %v != %v", + i, tt.desc, want, got) + } + } +} + +// TestGetUnicast verifies that dhcp6.Options.Unicast properly parses +// and returns an IPv6 address or an error, if available with OptionUnicast. +func TestGetUnicast(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + ip IP + err error + }{ + { + desc: "OptionUnicast not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionUnicast present in dhcp6.Options map, but too short length", + options: dhcp6.Options{ + dhcp6.OptionUnicast: [][]byte{bytes.Repeat([]byte{0}, 15)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionUnicast present in dhcp6.Options map, but too long length", + options: dhcp6.Options{ + dhcp6.OptionUnicast: [][]byte{bytes.Repeat([]byte{0}, 17)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionUnicast present in dhcp6.Options map with IPv4 address", + options: dhcp6.Options{ + dhcp6.OptionUnicast: [][]byte{net.IPv4(192, 168, 1, 1)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionUnicast present in dhcp6.Options map with IPv6 address", + options: dhcp6.Options{ + dhcp6.OptionUnicast: [][]byte{net.IPv6loopback}, + }, + ip: IP(net.IPv6loopback), + }, + } + + for i, tt := range tests { + ip, err := GetUnicast(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.Unicast(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.ip, ip; !bytes.Equal(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.Unicast():\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetStatusCode verifies that dhcp6.Options.StatusCode properly parses +// and returns a StatusCode value, if it is available with OptionStatusCode. +func TestGetStatusCode(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + sc *StatusCode + err error + }{ + { + desc: "OptionStatusCode not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionStatusCode present in dhcp6.Options map, but too short length", + options: dhcp6.Options{ + dhcp6.OptionStatusCode: [][]byte{{}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionStatusCode present in dhcp6.Options map, no message", + options: dhcp6.Options{ + dhcp6.OptionStatusCode: [][]byte{{0, 0}}, + }, + sc: &StatusCode{ + Code: dhcp6.StatusSuccess, + }, + }, + { + desc: "OptionStatusCode present in dhcp6.Options map, with message", + options: dhcp6.Options{ + dhcp6.OptionStatusCode: [][]byte{append([]byte{0, 0}, []byte("deadbeef")...)}, + }, + sc: &StatusCode{ + Code: dhcp6.StatusSuccess, + Message: "deadbeef", + }, + }, + } + + for i, tt := range tests { + sc, err := GetStatusCode(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.StatusCode(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.sc, sc; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.StatusCode():\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetElapsedTime verifies that dhcp6.Options.ElapsedTime properly parses and +// returns a time.Duration value, if one is available with OptionElapsedTime. +func TestGetElapsedTime(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + duration ElapsedTime + err error + }{ + { + desc: "OptionElapsedTime not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionElapsedTime present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionElapsedTime: [][]byte{{1}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionElapsedTime present in dhcp6.Options map, but too long", + options: dhcp6.Options{ + dhcp6.OptionElapsedTime: [][]byte{{1, 2, 3}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionElapsedTime present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionElapsedTime: [][]byte{{1, 1}}, + }, + duration: ElapsedTime(2570 * time.Millisecond), + }, + } + + for i, tt := range tests { + duration, err := GetElapsedTime(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.ElapsedTime: %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.duration, duration; want != got { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.ElapsedTime(): %v != %v", + i, tt.desc, want, got) + } + } +} + +// TestElapsedTimeMarshalBinary verifies that dhcp6.Options.ElapsedTime properly +// marsharls into bytes array. +func TestElapsedTimeMarshalBinary(t *testing.T) { + var tests = []struct { + desc string + elapsedTime ElapsedTime + buf []byte + err error + }{ + { + desc: "OptionElapsedTime elapsed-time = 0", + buf: []byte{0, 0}, + }, + { + desc: "OptionElapsedTime elapsed-time = 65534 hundredths of a second", + elapsedTime: ElapsedTime(655340 * time.Millisecond), + buf: []byte{0xff, 0xfe}, + }, + { + desc: "OptionElapsedTime elapsed-time = 65535 hundredths of a second", + elapsedTime: ElapsedTime(655350 * time.Millisecond), + buf: []byte{0xff, 0xff}, + }, + { + desc: "OptionElapsedTime elapsed-time = 65537 hundredths of a second", + elapsedTime: ElapsedTime(655370 * time.Millisecond), + buf: []byte{0xff, 0xff}, + }, + } + + for i, tt := range tests { + buf, err := tt.elapsedTime.MarshalBinary() + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.ElapsedTime\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + + if tt.err != nil { + continue + } + + if want, got := tt.buf, buf; !bytes.Equal(want, got) { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.ElapsedTime\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetRelayMessage verifies that dhcp6.Options.RelayMessageOption properly parses and +// returns an relay message option value, if one is available with RelayMessageOption. +func TestGetRelayMessage(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + authentication RelayMessageOption + err error + }{ + { + desc: "RelayMessageOption not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "RelayMessageOption present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionRelayMsg: [][]byte{{1, 1, 2, 3}}, + }, + authentication: []byte{1, 1, 2, 3}, + }, + } + + for i, tt := range tests { + relayMsg, err := GetRelayMessageOption(tt.options) + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.RelayMessageOption\n- want: %v\n- got: %v", i, tt.desc, want, got) + } + + if tt.err != nil { + continue + } + + if want, got := tt.authentication, relayMsg; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.RelayMessageOption()\n- want: %v\n- got: %v", i, tt.desc, want, got) + } + + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected err for dhcp6.Options.RelayMessageOption(): %v != %v", i, tt.desc, want, got) + } + } +} + +// TestAuthentication verifies that dhcp6.Options.Authentication properly parses and +// returns an authentication value, if one is available with Authentication. +func TestAuthentication(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + authentication *Authentication + err error + }{ + { + desc: "Authentication not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "Authentication present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionAuth: [][]byte{bytes.Repeat([]byte{0}, 10)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "Authentication present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionAuth: [][]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}}, + }, + authentication: &Authentication{ + Protocol: 0, + Algorithm: 1, + RDM: 2, + ReplayDetection: binary.BigEndian.Uint64([]byte{3, 4, 5, 6, 7, 8, 9, 0xa}), + AuthenticationInformation: []byte{0xb, 0xc, 0xd, 0xe, 0xf}, + }, + }, + } + + for i, tt := range tests { + authentication, err := GetAuthentication(tt.options) + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.Authentication\n- want: %v\n- got: %v", i, tt.desc, want, got) + } + + if tt.err != nil { + continue + } + + if want, got := tt.authentication, authentication; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.Authentication()\n- want: %v\n- got: %v", i, tt.desc, want, got) + } + + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected err for dhcp6.Options.Authentication(): %v != %v", i, tt.desc, want, got) + } + } +} + +// TestGetRapidCommit verifies that dhcp6.Options.RapidCommit properly indicates +// if OptionRapidCommit was present in dhcp6.Options. +func TestGetRapidCommit(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + err error + }{ + { + desc: "OptionRapidCommit not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionRapidCommit present in dhcp6.Options map, but non-empty", + options: dhcp6.Options{ + dhcp6.OptionRapidCommit: [][]byte{{1}}, + }, + err: dhcp6.ErrInvalidPacket, + }, + { + desc: "OptionRapidCommit present in dhcp6.Options map, empty", + options: dhcp6.Options{ + dhcp6.OptionRapidCommit: [][]byte{}, + }, + }, + } + + for i, tt := range tests { + err := GetRapidCommit(tt.options) + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.RapidCommit: %v != %v", + i, tt.desc, want, got) + } + } +} + +// TestGetUserClass verifies that dhcp6.Options.UserClass properly parses +// and returns raw user class data, if it is available with OptionUserClass. +func TestGetUserClass(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + classes [][]byte + err error + }{ + { + desc: "OptionUserClass not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionUserClass present in dhcp6.Options map, but empty", + options: dhcp6.Options{ + dhcp6.OptionUserClass: [][]byte{{}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionUserClass present in dhcp6.Options map, one item, zero length", + options: dhcp6.Options{ + dhcp6.OptionUserClass: [][]byte{{ + 0, 0, + }}, + }, + classes: [][]byte{{}}, + }, + { + desc: "OptionUserClass present in dhcp6.Options map, one item, extra byte", + options: dhcp6.Options{ + dhcp6.OptionUserClass: [][]byte{{ + 0, 1, 1, 255, + }}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionUserClass present in dhcp6.Options map, one item", + options: dhcp6.Options{ + dhcp6.OptionUserClass: [][]byte{{ + 0, 1, 1, + }}, + }, + classes: [][]byte{{1}}, + }, + { + desc: "OptionUserClass present in dhcp6.Options map, three items", + options: dhcp6.Options{ + dhcp6.OptionUserClass: [][]byte{{ + 0, 1, 1, + 0, 2, 2, 2, + 0, 3, 3, 3, 3, + }}, + }, + classes: [][]byte{{1}, {2, 2}, {3, 3, 3}}, + }, + } + + for i, tt := range tests { + classes, err := GetUserClass(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.UserClass: %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := len(tt.classes), len(classes); want != got { + t.Errorf("[%02d] test %q, unexpected classes slice length: %v != %v", + i, tt.desc, want, got) + + } + + for j := range classes { + if want, got := tt.classes[j], classes[j]; !bytes.Equal(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.UserClass()\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestGetVendorClass verifies that dhcp6.Options.VendorClass properly parses +// and returns raw vendor class data, if it is available with OptionVendorClass. +func TestGetVendorClass(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + classes [][]byte + err error + }{ + { + desc: "OptionVendorClass not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionVendorClass present in dhcp6.Options map, but empty", + options: dhcp6.Options{ + dhcp6.OptionVendorClass: [][]byte{{}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionVendorClass present in dhcp6.Options map, zero item", + options: dhcp6.Options{ + dhcp6.OptionVendorClass: [][]byte{{ + 0, 0, 5, 0x58, + }}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionVendorClass present in dhcp6.Options map, one item, zero length", + options: dhcp6.Options{ + dhcp6.OptionVendorClass: [][]byte{{ + 0, 0, 5, 0x58, + 0, 0, + }}, + }, + classes: [][]byte{{}}, + }, + { + desc: "OptionVendorClass present in dhcp6.Options map, one item, extra byte", + options: dhcp6.Options{ + dhcp6.OptionVendorClass: [][]byte{{ + 0, 0, 5, 0x58, + 0, 1, 1, 255, + }}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionVendorClass present in dhcp6.Options map, one item", + options: dhcp6.Options{ + dhcp6.OptionVendorClass: [][]byte{{ + 0, 0, 5, 0x58, + 0, 1, 1, + }}, + }, + classes: [][]byte{{1}}, + }, + { + desc: "OptionVendorClass present in dhcp6.Options map, three items", + options: dhcp6.Options{ + dhcp6.OptionVendorClass: [][]byte{{ + 0, 0, 5, 0x58, + 0, 1, 1, + 0, 2, 2, 2, + 0, 3, 3, 3, 3, + }}, + }, + classes: [][]byte{{1}, {2, 2}, {3, 3, 3}}, + }, + } + + for i, tt := range tests { + classes, err := GetVendorClass(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.VendorClass: %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := len(tt.classes), len(classes.VendorClassData); want != got { + t.Errorf("[%02d] test %q, unexpected classes slice length: %v != %v", + i, tt.desc, want, got) + + } + + for j := range classes.VendorClassData { + if want, got := tt.classes[j], classes.VendorClassData[j]; !bytes.Equal(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.VendorClass()\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestInterfaceID verifies that dhcp6.Options.InterfaceID properly parses +// and returns raw interface-id data, if it is available with InterfaceID. +func TestInterfaceID(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + interfaceID InterfaceID + err error + }{ + { + desc: "InterfaceID not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "InterfaceID present in dhcp6.Options map, one item", + options: dhcp6.Options{ + dhcp6.OptionInterfaceID: [][]byte{{ + 0, 1, 1, + }}, + }, + interfaceID: []byte{0, 1, 1}, + }, + { + desc: "InterfaceID present in dhcp6.Options map with no interface-id data", + options: dhcp6.Options{ + dhcp6.OptionInterfaceID: [][]byte{{}}, + }, + interfaceID: []byte{}, + }, + } + + for i, tt := range tests { + interfaceID, err := GetInterfaceID(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.InterfaceID: %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.interfaceID, interfaceID; !bytes.Equal(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.InterfaceID()\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestGetIAPD verifies that dhcp6.Options.IAPD properly parses and +// returns multiple IAPD values, if one or more are available with OptionIAPD. +func TestGetIAPD(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + iapd []*IAPD + err error + }{ + { + desc: "OptionIAPD not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionIAPD present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionIAPD: [][]byte{bytes.Repeat([]byte{0}, 11)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "one OptionIAPD present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIAPD: [][]byte{{ + 1, 2, 3, 4, + 0, 0, 0, 30, + 0, 0, 0, 60, + }}, + }, + iapd: []*IAPD{ + { + IAID: [4]byte{1, 2, 3, 4}, + T1: 30 * time.Second, + T2: 60 * time.Second, + }, + }, + }, + { + desc: "two OptionIAPD present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIAPD: [][]byte{ + append(bytes.Repeat([]byte{0}, 12), []byte{0, 1, 0, 1, 1}...), + append(bytes.Repeat([]byte{0}, 12), []byte{0, 2, 0, 1, 2}...), + }, + }, + iapd: []*IAPD{ + { + Options: dhcp6.Options{ + dhcp6.OptionClientID: [][]byte{{1}}, + }, + }, + { + Options: dhcp6.Options{ + dhcp6.OptionServerID: [][]byte{{2}}, + }, + }, + }, + }, + } + + for i, tt := range tests { + iapd, err := GetIAPD(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.IAPD: %v != %v", + i, tt.desc, want, got) + } + continue + } + + for j := range tt.iapd { + want, err := tt.iapd[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iapd[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.IAPD():\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestGetIAPrefix verifies that dhcp6.Options.IAPrefix properly parses and +// returns multiple IAPrefix values, if one or more are available with +// OptionIAPrefix. +func TestGetIAPrefix(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + iaprefix []*IAPrefix + err error + }{ + { + desc: "OptionIAPrefix not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionIAPrefix present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionIAPrefix: [][]byte{bytes.Repeat([]byte{0}, 24)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "one OptionIAPrefix present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIAPrefix: [][]byte{{ + 0, 0, 0, 30, + 0, 0, 0, 60, + 32, + 32, 1, 13, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }}, + }, + iaprefix: []*IAPrefix{ + { + PreferredLifetime: 30 * time.Second, + ValidLifetime: 60 * time.Second, + PrefixLength: 32, + Prefix: net.IP{ + 32, 1, 13, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + }, + }, + { + desc: "two OptionIAPrefix present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionIAPrefix: [][]byte{ + bytes.Repeat([]byte{0}, 25), + bytes.Repeat([]byte{0}, 25), + }, + }, + iaprefix: []*IAPrefix{ + { + Prefix: net.IPv6zero, + }, + { + Prefix: net.IPv6zero, + }, + }, + }, + } + + for i, tt := range tests { + iaprefix, err := GetIAPrefix(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.IAPrefix: %v != %v", + i, tt.desc, want, got) + } + continue + } + + for j := range tt.iaprefix { + want, err := tt.iaprefix[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := iaprefix[j].MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.IAPrefix():\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestRemoteIdentifier verifies that dhcp6.Options.RemoteIdentifier properly parses +// and returns a RemoteIdentifier, if it is available with dhcp6.OptionsRemoteIdentifier. +func TestGetRemoteIdentifier(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + remoteIdentifier *RemoteIdentifier + err error + }{ + { + desc: "OptionsRemoteIdentifier not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionsRemoteIdentifier present in dhcp6.Options map, but too short", + options: dhcp6.Options{ + dhcp6.OptionRemoteIdentifier: [][]byte{{ + 0, 0, 5, 0x58, + }}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionsRemoteIdentifier present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionRemoteIdentifier: [][]byte{{ + 0, 0, 5, 0x58, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xe, 0xf, + }}, + }, + remoteIdentifier: &RemoteIdentifier{ + EnterpriseNumber: 1368, + RemoteID: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xe, 0xf}, + }, + }, + } + for i, tt := range tests { + remoteIdentifier, err := GetRemoteIdentifier(tt.options) + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.RemoteIdentifier\n- want: %v\n- got: %v", i, tt.desc, want, got) + } + + if tt.err != nil { + continue + } + + if want, got := tt.remoteIdentifier, remoteIdentifier; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.RemoteIdentifier()\n- want: %v\n- got: %v", i, tt.desc, want, got) + } + + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected err for dhcp6.Options.RemoteIdentifier(): %v != %v", i, tt.desc, want, got) + } + } +} + +// TestGetBootFileURL verifies that dhcp6.Options.BootFileURL properly parses +// and returns a URL, if it is available with OptionBootFileURL. +func TestGetBootFileURL(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + u *url.URL + err error + }{ + { + desc: "OptionBootFileURL not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionBootFileURL present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionBootFileURL: [][]byte{[]byte("tftp://192.168.1.1:69")}, + }, + u: &url.URL{ + Scheme: "tftp", + Host: "192.168.1.1:69", + }, + }, + } + + for i, tt := range tests { + u, err := GetBootFileURL(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected err for dhcp6.Options.BootFileURL(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + ttuu := url.URL(*tt.u) + uu := url.URL(*u) + if want, got := ttuu.String(), uu.String(); want != got { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.BootFileURL(): %v != %v", + i, tt.desc, want, got) + } + } +} + +// TestGetBootFileParam verifies that dhcp6.Options.BootFileParam properly parses +// and returns boot file parameter data, if it is available with +// OptionBootFileParam. +func TestGetBootFileParam(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + param BootFileParam + err error + }{ + { + desc: "OptionBootFileParam not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionBootFileParam present in dhcp6.Options map, but empty", + options: dhcp6.Options{ + dhcp6.OptionBootFileParam: [][]byte{{}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionBootFileParam present in dhcp6.Options map, one item, zero length", + options: dhcp6.Options{ + dhcp6.OptionBootFileParam: [][]byte{{ + 0, 0, + }}, + }, + param: []string{""}, + }, + { + desc: "OptionBootFileParam present in dhcp6.Options map, one item, extra byte", + options: dhcp6.Options{ + dhcp6.OptionBootFileParam: [][]byte{{ + 0, 1, 1, 255, + }}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionBootFileParam present in dhcp6.Options map, one item", + options: dhcp6.Options{ + dhcp6.OptionBootFileParam: [][]byte{{ + 0, 3, 'f', 'o', 'o', + }}, + }, + param: []string{"foo"}, + }, + { + desc: "OptionBootFileParam present in dhcp6.Options map, three items", + options: dhcp6.Options{ + dhcp6.OptionBootFileParam: [][]byte{{ + 0, 1, 'a', + 0, 2, 'a', 'b', + 0, 3, 'a', 'b', 'c', + }}, + }, + param: []string{"a", "ab", "abc"}, + }, + } + + for i, tt := range tests { + param, err := GetBootFileParam(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.BootFileParam: %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := len(tt.param), len(param); want != got { + t.Errorf("[%02d] test %q, unexpected param slice length: %v != %v", + i, tt.desc, want, got) + + } + + for j := range param { + if want, got := tt.param[j], param[j]; want != got { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.BootFileParam()\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestGetClientArchType verifies that dhcp6.Options.ClientArchType properly parses +// and returns client architecture type data, if it is available with +// OptionClientArchType. +func TestGetClientArchType(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + arch ArchTypes + err error + }{ + { + desc: "OptionClientArchType not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionClientArchType present in dhcp6.Options map, but empty", + options: dhcp6.Options{ + dhcp6.OptionClientArchType: [][]byte{{}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionClientArchType present in dhcp6.Options map, but not divisible by 2", + options: dhcp6.Options{ + dhcp6.OptionClientArchType: [][]byte{{0, 0, 0}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionClientArchType present in dhcp6.Options map, one architecture", + options: dhcp6.Options{ + dhcp6.OptionClientArchType: [][]byte{{0, 9}}, + }, + arch: ArchTypes{ArchTypeEFIx8664}, + }, + { + desc: "OptionClientArchType present in dhcp6.Options map, three architectures", + options: dhcp6.Options{ + dhcp6.OptionClientArchType: [][]byte{{0, 5, 0, 9, 0, 0}}, + }, + arch: ArchTypes{ + ArchTypeIntelLeanClient, + ArchTypeEFIx8664, + ArchTypeIntelx86PC, + }, + }, + } + + for i, tt := range tests { + arch, err := GetClientArchType(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.ClientArchType: %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := len(tt.arch), len(arch); want != got { + t.Errorf("[%02d] test %q, unexpected arch slice length: %v != %v", + i, tt.desc, want, got) + } + + for j := range arch { + if want, got := tt.arch[j], arch[j]; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d:%02d] test %q, unexpected value for dhcp6.Options.ClientArchType()\n- want: %v\n- got: %v", + i, j, tt.desc, want, got) + } + } + } +} + +// TestGetNII verifies that dhcp6.Options.NII properly parses and returns a +// Network Interface Identifier value, if it is available with OptionNII. +func TestGetNII(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + nii *NII + err error + }{ + { + desc: "OptionNII not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionNII present in dhcp6.Options map, but too short length", + options: dhcp6.Options{ + dhcp6.OptionNII: [][]byte{{1, 2}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionNII present in dhcp6.Options map, but too long length", + options: dhcp6.Options{ + dhcp6.OptionNII: [][]byte{{1, 2, 3, 4}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionNII present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionNII: [][]byte{{1, 2, 3}}, + }, + nii: &NII{ + Type: 1, + Major: 2, + Minor: 3, + }, + }, + } + + for i, tt := range tests { + nii, err := GetNII(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for dhcp6.Options.NII(): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.nii, nii; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for dhcp6.Options.NII(): %v != %v", + i, tt.desc, want, got) + } + } +} + +// TestGetDNSServers verifies that dhcp6opts.GetDNSServers properly parses and +// returns a list of net.IPs, if it is available with OptionDNSServers. +func TestGetDNSServers(t *testing.T) { + var tests = []struct { + desc string + options dhcp6.Options + dns IPs + err error + }{ + { + desc: "OptionDNSServers not present in dhcp6.Options map", + err: dhcp6.ErrOptionNotPresent, + }, + { + desc: "OptionDNSServers present in dhcp6.Options map, but too short length", + options: dhcp6.Options{ + dhcp6.OptionDNSServers: [][]byte{{255, 255, 255}}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "OptionDNSServers present in dhcp6.Options map, but too long length", + options: dhcp6.Options{ + dhcp6.OptionDNSServers: [][]byte{bytes.Repeat([]byte{0xff}, 17)}, + }, + err: io.ErrUnexpectedEOF, + }, + { + desc: "One OptionDNSServers present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionDNSServers: [][]byte{bytes.Repeat([]byte{0xfe}, 16)}, + }, + dns: IPs{ + net.IP(bytes.Repeat([]byte{0xfe}, 16)), + }, + }, + { + desc: "Two OptionDNSServers present in dhcp6.Options map", + options: dhcp6.Options{ + dhcp6.OptionDNSServers: [][]byte{append(bytes.Repeat([]byte{0xfd}, 16), bytes.Repeat([]byte{0xfc}, 16)...)}, + }, + dns: IPs{ + net.IP(bytes.Repeat([]byte{0xfd}, 16)), + net.IP(bytes.Repeat([]byte{0xfc}, 16)), + }, + }, + } + + for i, tt := range tests { + dns, err := GetDNSServers(tt.options) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for GetDNSServers(dhcp6.Options): %v != %v", + i, tt.desc, want, got) + } + continue + } + + if want, got := tt.dns, dns; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected value for GetDNSServers(dhcp6.Options): %v != %v", + i, tt.desc, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/opts.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/opts.go new file mode 100644 index 00000000..b4506979 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/opts.go @@ -0,0 +1,29 @@ +package dhcp6opts + +import ( + "errors" +) + +//go:generate stringer -output=string.go -type=ArchType,DUIDType + +var ( + // ErrHardwareTypeNotImplemented is returned when HardwareType is not + // implemented on the current platform. + ErrHardwareTypeNotImplemented = errors.New("hardware type detection not implemented on this platform") + + // ErrInvalidDUIDLLTTime is returned when a time before midnight (UTC), + // January 1, 2000 is used in NewDUIDLLT. + ErrInvalidDUIDLLTTime = errors.New("DUID-LLT time must be after midnight (UTC), January 1, 2000") + + // ErrInvalidIP is returned when an input net.IP value is not recognized as a + // valid IPv6 address. + ErrInvalidIP = errors.New("IP must be an IPv6 address") + + // ErrInvalidLifetimes is returned when an input preferred lifetime is shorter + // than a valid lifetime parameter. + ErrInvalidLifetimes = errors.New("preferred lifetime must be less than valid lifetime") + + // ErrParseHardwareType is returned when a valid hardware type could + // not be found for a given interface. + ErrParseHardwareType = errors.New("could not parse hardware type for interface") +) diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/relaymessage.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/relaymessage.go new file mode 100644 index 00000000..94a63b1d --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/relaymessage.go @@ -0,0 +1,87 @@ +package dhcp6opts + +import ( + "io" + "net" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// RelayMessage represents a raw RelayMessage generated by DHCPv6 relay agent, using RFC 3315, +// Section 7. +type RelayMessage struct { + // RELAY-FORW or RELAY-REPL only + MessageType dhcp6.MessageType + + // Number of relay agents that have relayed this + // message. + HopCount uint8 + + // A global or site-local address that will be used by + // the server to identify the link on which the client + // is located. + LinkAddress net.IP + + // The address of the client or relay agent from which + // the message to be relayed was received. + PeerAddress net.IP + + // Options specifies a map of DHCP options. Its methods can be used to + // retrieve data from an incoming RelayMessage, or send data with an outgoing + // RelayMessage. + // MUST include a "Relay Message option" (see + // section 22.10); MAY include other options added by + // the relay agent. + Options dhcp6.Options +} + +// MarshalBinary allocates a byte slice containing the data +// from a RelayMessage. +func (rm *RelayMessage) MarshalBinary() ([]byte, error) { + // 1 byte: message type + // 1 byte: hop-count + // 16 bytes: link-address + // 16 bytes: peer-address + // N bytes: options slice byte count + b := buffer.New(nil) + + b.Write8(uint8(rm.MessageType)) + b.Write8(rm.HopCount) + copy(b.WriteN(net.IPv6len), rm.LinkAddress) + copy(b.WriteN(net.IPv6len), rm.PeerAddress) + opts, err := rm.Options.MarshalBinary() + if err != nil { + return nil, err + } + b.WriteBytes(opts) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a RelayMessage. +// +// If the byte slice does not contain enough data to form a valid RelayMessage, +// ErrInvalidPacket is returned. +func (rm *RelayMessage) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // RelayMessage must contain at least message type, hop-count, link-address and peer-address + if b.Len() < 34 { + return io.ErrUnexpectedEOF + } + + rm.MessageType = dhcp6.MessageType(b.Read8()) + rm.HopCount = b.Read8() + + rm.LinkAddress = make(net.IP, net.IPv6len) + copy(rm.LinkAddress, b.Consume(net.IPv6len)) + + rm.PeerAddress = make(net.IP, net.IPv6len) + copy(rm.PeerAddress, b.Consume(net.IPv6len)) + + if err := (&rm.Options).UnmarshalBinary(b.Remaining()); err != nil { + // Invalid options means an invalid RelayMessage + return dhcp6.ErrInvalidPacket + } + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/relaymessage_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/relaymessage_test.go new file mode 100644 index 00000000..1307c7d2 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/relaymessage_test.go @@ -0,0 +1,136 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "net" + "reflect" + "testing" + + "github.com/mdlayher/dhcp6" +) + +// TestRelayMessageMarshalBinary verifies that RelayMessage.MarshalBinary allocates and returns a correct +// byte slice for a variety of input data. +func TestRelayMessageMarshalBinary(t *testing.T) { + var tests = []struct { + desc string + relayMsg *RelayMessage + buf []byte + }{ + { + desc: "empty packet", + relayMsg: &RelayMessage{}, + buf: make([]byte, 34), + }, + { + desc: "RelayForw only", + relayMsg: &RelayMessage{ + MessageType: dhcp6.MessageTypeRelayForw, + }, + buf: []byte{12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + desc: "RelayReply only", + relayMsg: &RelayMessage{ + MessageType: dhcp6.MessageTypeRelayRepl, + }, + buf: []byte{13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + desc: "RelayForw, 15 Hopcount, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] LinkAddress, [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32] PeerAddress", + relayMsg: &RelayMessage{ + MessageType: dhcp6.MessageTypeRelayForw, + HopCount: 15, + LinkAddress: net.IP([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), + PeerAddress: net.IP([]byte{17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}), + }, + buf: []byte{12, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, + }, + } + + for i, tt := range tests { + buf, err := tt.relayMsg.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if want, got := tt.buf, buf; !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected packet bytes:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestRelayMessageUnmarshalBinary verifies that RelayMessage.UnmarshalBinary returns +// appropriate RelayMessages and errors for various input byte slices. +func TestRelayMessageUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + relayMsg *RelayMessage + err error + }{ + { + desc: "nil buffer, malformed packet", + err: io.ErrUnexpectedEOF, + }, + { + desc: "empty buffer, malformed packet", + buf: []byte{}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "length 1 buffer, malformed packet", + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + desc: "length 33 buffer, malformed packet", + buf: make([]byte, 33), + err: io.ErrUnexpectedEOF, + }, + { + desc: "invalid options in packet", + buf: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}, + err: dhcp6.ErrInvalidPacket, + }, + { + desc: "length 34 buffer, OK", + buf: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + relayMsg: &RelayMessage{ + LinkAddress: net.IP(make([]byte, net.IPv6len)), + PeerAddress: net.IP(make([]byte, net.IPv6len)), + Options: make(dhcp6.Options), + }, + }, + { + desc: "RelayForw, 15 Hopcount, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] LinkAddress, [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32] PeerAddress", + buf: []byte{12, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, + relayMsg: &RelayMessage{ + MessageType: dhcp6.MessageTypeRelayForw, + HopCount: 15, + LinkAddress: net.IP([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), + PeerAddress: net.IP([]byte{17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}), + Options: make(dhcp6.Options), + }, + }, + } + + for i, tt := range tests { + p := new(RelayMessage) + if err := p.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + if want, got := tt.relayMsg, p; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected packet:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/remoteid.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/remoteid.go new file mode 100644 index 00000000..03068882 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/remoteid.go @@ -0,0 +1,53 @@ +package dhcp6opts + +import ( + "io" + + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// A RemoteIdentifier carries vendor-specific options. +// +// The vendor is indicated in the enterprise-number field. +// The remote-id field may be used to encode, for instance: +// - a "caller ID" telephone number for dial-up connection +// - a "user name" prompted for by a Remote Access Server +// - a remote caller ATM address +// - a "modem ID" of a cable data modem +// - the remote IP address of a point-to-point link +// - a remote X.25 address for X.25 connections +// - an interface or port identifier +type RemoteIdentifier struct { + // EnterpriseNumber specifies an IANA-assigned vendor Private Enterprise + // Number. + EnterpriseNumber uint32 + + // The opaque value for the remote-id. + RemoteID []byte +} + +// MarshalBinary allocates a byte slice containing the data +// from a RemoteIdentifier. +func (r *RemoteIdentifier) MarshalBinary() ([]byte, error) { + // 4 bytes: EnterpriseNumber + // N bytes: RemoteId + b := buffer.New(nil) + b.Write32(r.EnterpriseNumber) + b.WriteBytes(r.RemoteID) + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a RemoteIdentifier. +// If the byte slice does not contain enough data to form a valid +// RemoteIdentifier, io.ErrUnexpectedEOF is returned. +func (r *RemoteIdentifier) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Too short to be valid RemoteIdentifier + if b.Len() < 5 { + return io.ErrUnexpectedEOF + } + + r.EnterpriseNumber = b.Read32() + r.RemoteID = b.Remaining() + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/remoteid_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/remoteid_test.go new file mode 100644 index 00000000..fb7870df --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/remoteid_test.go @@ -0,0 +1,77 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "reflect" + "testing" +) + +func TestRemoteIdentifierMarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + remoteIdentifier *RemoteIdentifier + }{ + { + desc: "all zero values", + buf: bytes.Repeat([]byte{0}, 5), + remoteIdentifier: &RemoteIdentifier{ + RemoteID: []byte{0}, + }, + }, + { + desc: "[0, 0, 5, 0x58] EnterpriseNumber, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xe, 0xf] RemoteID", + buf: []byte{0, 0, 5, 0x58, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xe, 0xf}, + remoteIdentifier: &RemoteIdentifier{ + EnterpriseNumber: 1368, + RemoteID: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xe, 0xf}, + }, + }, + } + + for i, tt := range tests { + want := tt.buf + got, err := tt.remoteIdentifier.MarshalBinary() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected RemoteIdentifier bytes for MarshalBinary(%v)\n- want: %v\n- got: %v", + i, tt.desc, tt.buf, want, got) + } + } +} + +func TestRemoteIdentifierUnmarshalBinary(t *testing.T) { + var tests = []struct { + buf []byte + remoteIdentifier *RemoteIdentifier + err error + }{ + { + buf: []byte{0, 0, 5, 0x58, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xe, 0xf}, + remoteIdentifier: &RemoteIdentifier{ + EnterpriseNumber: 1368, + RemoteID: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xe, 0xf}, + }, + }, + { + buf: bytes.Repeat([]byte{0}, 4), + err: io.ErrUnexpectedEOF, + }, + } + + for i, tt := range tests { + remoteIdentifier := new(RemoteIdentifier) + if want, got := tt.err, remoteIdentifier.UnmarshalBinary(tt.buf); want != got { + t.Fatalf("[%02d] unexpected error for parseRemoteIdentifier(%v):\n- want: %v\n- got: %v", i, tt.buf, want, got) + } + + if tt.err == nil { + if want, got := tt.remoteIdentifier, remoteIdentifier; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected RemoteIdentifier for parseRemoteIdentifier(%v):\n- want: %v\n- got: %v", i, tt.buf, want, got) + } + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/statuscode.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/statuscode.go new file mode 100644 index 00000000..f62482c5 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/statuscode.go @@ -0,0 +1,57 @@ +package dhcp6opts + +import ( + "io" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// StatusCode represents a Status Code, as defined in RFC 3315, Section 5.4. +// DHCP clients and servers can use status codes to communicate successes +// or failures, and provide additional information using a message to describe +// specific failures. +type StatusCode struct { + // Code specifies the Status value stored within this StatusCode, such as + // StatusSuccess, StatusUnspecFail, etc. + Code dhcp6.Status + + // Message specifies a human-readable message within this StatusCode, useful + // for providing information about successes or failures. + Message string +} + +// NewStatusCode creates a new StatusCode from an input Status value and a +// string message. +func NewStatusCode(code dhcp6.Status, message string) *StatusCode { + return &StatusCode{ + Code: code, + Message: message, + } +} + +// MarshalBinary allocates a byte slice containing the data from a StatusCode. +func (s *StatusCode) MarshalBinary() ([]byte, error) { + // 2 bytes: status code + // N bytes: message + b := buffer.New(nil) + b.Write16(uint16(s.Code)) + b.WriteBytes([]byte(s.Message)) + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a StatusCode. +// +// If the byte slice does not contain enough data to form a valid StatusCode, +// errInvalidStatusCode is returned. +func (s *StatusCode) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Too short to contain valid StatusCode + if b.Len() < 2 { + return io.ErrUnexpectedEOF + } + + s.Code = dhcp6.Status(b.Read16()) + s.Message = string(b.Remaining()) + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/statuscode_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/statuscode_test.go new file mode 100644 index 00000000..cd4b67ef --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/statuscode_test.go @@ -0,0 +1,90 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/mdlayher/dhcp6" +) + +// TestNewStatusCode verifies that NewStatusCode creates a proper StatusCode +// value for the input values. +func TestNewStatusCode(t *testing.T) { + var tests = []struct { + status dhcp6.Status + message string + sc *StatusCode + }{ + { + status: dhcp6.StatusSuccess, + message: "Success", + sc: &StatusCode{ + Code: dhcp6.StatusSuccess, + Message: "Success", + }, + }, + } + + for i, tt := range tests { + if want, got := tt.sc, NewStatusCode(tt.status, tt.message); !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected StatusCode for NewStatusCode(%v, %q)\n- want: %v\n- got: %v", + i, tt.status, tt.message, want, got) + } + } +} + +// TestStatusCodeUnmarshalBinary verifies that StatusCode.UnmarshalBinary +// returns correct StatusCode and error values for several input values. +func TestStatusCodeUnmarshalBinary(t *testing.T) { + var tests = []struct { + buf []byte + sc *StatusCode + err error + }{ + { + buf: []byte{0}, + err: io.ErrUnexpectedEOF, + }, + { + buf: []byte{0, 0}, + sc: &StatusCode{ + Code: dhcp6.StatusSuccess, + }, + }, + { + buf: append([]byte{0, 1}, []byte("deadbeef")...), + sc: &StatusCode{ + Code: dhcp6.StatusUnspecFail, + Message: "deadbeef", + }, + }, + } + + for i, tt := range tests { + sc := new(StatusCode) + if err := sc.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] unexpected error for parseStatusCode(%v): %v != %v", + i, tt.buf, want, got) + } + + continue + } + + want, err := tt.sc.MarshalBinary() + if err != nil { + t.Fatal(err) + } + got, err := sc.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] unexpected StatusCode for parseStatusCode(%v)\n- want: %v\n- got: %v", + i, tt.buf, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/string.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/string.go new file mode 100644 index 00000000..a3fa864c --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/string.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -output=string.go -type=ArchType,DUIDType"; DO NOT EDIT. + +package dhcp6opts + +import "fmt" + +const _ArchType_name = "ArchTypeIntelx86PCArchTypeNECPC98ArchTypeEFIItaniumArchTypeDECAlphaArchtypeArcx86ArchTypeIntelLeanClientArchTypeEFIIA32ArchTypeEFIBCArchTypeEFIXscaleArchTypeEFIx8664" + +var _ArchType_index = [...]uint8{0, 18, 33, 51, 67, 81, 104, 119, 132, 149, 165} + +func (i ArchType) String() string { + if i >= ArchType(len(_ArchType_index)-1) { + return fmt.Sprintf("ArchType(%d)", i) + } + return _ArchType_name[_ArchType_index[i]:_ArchType_index[i+1]] +} + +const _DUIDType_name = "DUIDTypeLLTDUIDTypeENDUIDTypeLLDUIDTypeUUID" + +var _DUIDType_index = [...]uint8{0, 11, 21, 31, 43} + +func (i DUIDType) String() string { + i -= 1 + if i >= DUIDType(len(_DUIDType_index)-1) { + return fmt.Sprintf("DUIDType(%d)", i+1) + } + return _DUIDType_name[_DUIDType_index[i]:_DUIDType_index[i+1]] +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendorclass.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendorclass.go new file mode 100644 index 00000000..37adfb5b --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendorclass.go @@ -0,0 +1,47 @@ +package dhcp6opts + +import ( + "io" + + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// VendorClass is used by a client to identify the vendor that +// manufactured the hardware on which the client is running. The +// information contained in the data area of this option is contained in +// one or more opaque fields that identify details of the hardware +// configuration. +type VendorClass struct { + // EnterpriseNumber specifies an IANA-assigned vendor Private Enterprise + // Number. + EnterpriseNumber uint32 + + // The vendor-class-data is composed of a series of separate items, each + // of which describes some characteristic of the client's hardware + // configuration. Examples of vendor-class-data instances might include + // the version of the operating system the client is running or the + // amount of memory installed on the client. + VendorClassData Data +} + +// MarshalBinary allocates a byte slice containing the data from a VendorClass. +func (vc *VendorClass) MarshalBinary() ([]byte, error) { + b := buffer.New(nil) + b.Write32(vc.EnterpriseNumber) + vc.VendorClassData.Marshal(b) + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a VendorClass. +// +// If the byte slice is less than 4 bytes in length, or if VendorClassData is +// malformed, io.ErrUnexpectedEOF is returned. +func (vc *VendorClass) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + if b.Len() < 4 { + return io.ErrUnexpectedEOF + } + + vc.EnterpriseNumber = b.Read32() + return vc.VendorClassData.Unmarshal(b) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendoropts.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendoropts.go new file mode 100644 index 00000000..5ba61614 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendoropts.go @@ -0,0 +1,55 @@ +package dhcp6opts + +import ( + "io" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// A VendorOpts is used by clients and servers to exchange +// VendorOpts information. +type VendorOpts struct { + // EnterpriseNumber specifies an IANA-assigned vendor Private Enterprise + // Number. + EnterpriseNumber uint32 + + // An opaque object of option-len octets, + // interpreted by vendor-specific code on the + // clients and servers + Options dhcp6.Options +} + +// MarshalBinary allocates a byte slice containing the data from a VendorOpts. +func (v *VendorOpts) MarshalBinary() ([]byte, error) { + // 4 bytes: EnterpriseNumber + // N bytes: options slice byte count + b := buffer.New(nil) + b.Write32(v.EnterpriseNumber) + opts, err := v.Options.MarshalBinary() + if err != nil { + return nil, err + } + b.WriteBytes(opts) + + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a VendorOpts. +// If the byte slice does not contain enough data to form a valid +// VendorOpts, io.ErrUnexpectedEOF is returned. +// If option-data are invalid, then ErrInvalidPacket is returned. +func (v *VendorOpts) UnmarshalBinary(p []byte) error { + b := buffer.New(p) + // Too short to be valid VendorOpts + if b.Len() < 4 { + return io.ErrUnexpectedEOF + } + + v.EnterpriseNumber = b.Read32() + if err := (&v.Options).UnmarshalBinary(b.Remaining()); err != nil { + // Invalid options means an invalid RelayMessage + return dhcp6.ErrInvalidPacket + } + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendoropts_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendoropts_test.go new file mode 100644 index 00000000..2fd0948a --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6opts/vendoropts_test.go @@ -0,0 +1,110 @@ +package dhcp6opts + +import ( + "bytes" + "io" + "reflect" + "testing" + + "github.com/mdlayher/dhcp6" +) + +// TestVendorOptsMarshalBinary verifies that VendorOpts marshals properly for the input values. +func TestVendorOptsMarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + vendorOpts *VendorOpts + }{ + { + desc: "all zero values", + buf: bytes.Repeat([]byte{0}, 4), + vendorOpts: &VendorOpts{}, + }, + { + desc: "[0, 0, 5, 0x58] EnterpriseNumber, [1: []byte{3, 4}, 2: []byte{0x04, 0xa3, 0x9e}] vendorOpts", + buf: []byte{ + 0, 0, 5, 0x58, + 0, 1, 0, 2, 3, 4, + 0, 2, 0, 3, 0x04, 0xa3, 0x9e, + }, + vendorOpts: &VendorOpts{ + EnterpriseNumber: 1368, + Options: dhcp6.Options{ + 1: [][]byte{ + {3, 4}, + }, + 2: [][]byte{ + {0x04, 0xa3, 0x9e}, + }, + }, + }, + }, + } + + for i, tt := range tests { + want := tt.buf + got, err := tt.vendorOpts.MarshalBinary() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected VendorOpts bytes for MarshalBinary(%v)\n- want: %v\n- got: %v", + i, tt.desc, tt.buf, want, got) + } + } +} + +// TestVendorOptsUnmarshalBinary verifies that VendorOpts unmarshals properly for the input values. +func TestVendorOptsUnmarshalBinary(t *testing.T) { + var tests = []struct { + buf []byte + vendorOpts *VendorOpts + err error + }{ + { + buf: bytes.Repeat([]byte{0}, 3), + err: io.ErrUnexpectedEOF, + }, + { + buf: []byte{ + 0, 0, 5, 0x58, + 0, 1, 0, 0xa, + }, + err: dhcp6.ErrInvalidPacket, + }, + { + buf: []byte{ + 0, 0, 5, 0x58, + 0, 1, 0, 2, 3, 4, + 0, 2, 0, 3, 0x04, 0xa3, 0x9e, + }, + vendorOpts: &VendorOpts{ + EnterpriseNumber: 1368, + Options: dhcp6.Options{ + 1: [][]byte{ + {3, 4}, + }, + 2: [][]byte{ + {0x04, 0xa3, 0x9e}, + }, + }, + }, + }, + } + + for i, tt := range tests { + vendorOpts := new(VendorOpts) + if want, got := tt.err, vendorOpts.UnmarshalBinary(tt.buf); want != got { + t.Fatalf("[%02d] unexpected error for parseVendorOpts(%v):\n- want: %v\n- got: %v", i, tt.buf, want, got) + } + + if tt.err != nil { + continue + } + + if want, got := tt.vendorOpts, vendorOpts; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] unexpected VendorOpts for parseVendorOpts(%v):\n- want: %v\n- got: %v", i, tt.buf, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6server/dhcp6.go b/vendor/github.com/mdlayher/dhcp6/dhcp6server/dhcp6.go new file mode 100644 index 00000000..c893c7a8 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6server/dhcp6.go @@ -0,0 +1,48 @@ +package dhcp6server + +import ( + "github.com/mdlayher/dhcp6" +) + +// Handler provides an interface which allows structs to act as DHCPv6 server +// handlers. ServeDHCP implementations receive a copy of the incoming DHCP +// request via the Request parameter, and allow outgoing communication via +// the ResponseSender. +// +// ServeDHCP implementations can choose to write a response packet using the +// ResponseSender interface, or choose to not write anything at all. If no packet +// is sent back to the client, it may choose to back off and retry, or attempt +// to pursue communication with other DHCP servers. +type Handler interface { + ServeDHCP(ResponseSender, *Request) +} + +// HandlerFunc is an adapter type which allows the use of normal functions as +// DHCP handlers. If f is a function with the appropriate signature, +// HandlerFunc(f) is a Handler struct that calls f. +type HandlerFunc func(ResponseSender, *Request) + +// ServeDHCP calls f(w, r), allowing regular functions to implement Handler. +func (f HandlerFunc) ServeDHCP(w ResponseSender, r *Request) { + f(w, r) +} + +// ResponseSender provides an interface which allows a DHCP handler to construct +// and send a DHCP response packet. In addition, the server automatically handles +// copying certain options from a client Request to a ResponseSender's Options, +// including: +// - Client ID (OptionClientID) +// - Server ID (OptionServerID) +// +// ResponseSender implementations should use the same transaction ID sent in a +// client Request. +type ResponseSender interface { + // Options returns the Options map that will be sent to a client + // after a call to Send. + Options() dhcp6.Options + + // Send generates a DHCP response packet using the input message type + // and any options set by Options. Send returns the number of bytes + // sent and any errors which occurred. + Send(dhcp6.MessageType) (int, error) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6server/mux.go b/vendor/github.com/mdlayher/dhcp6/dhcp6server/mux.go new file mode 100644 index 00000000..1e0ed543 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6server/mux.go @@ -0,0 +1,54 @@ +package dhcp6server + +import ( + "sync" + + "github.com/mdlayher/dhcp6" +) + +// ServeMux is a DHCP request multiplexer, which implements Handler. ServeMux +// matches handlers based on their MessageType, enabling different handlers +// to be used for different types of DHCP messages. ServeMux can be helpful +// for structuring your application, but may not be needed for very simple +// DHCP servers. +type ServeMux struct { + mu sync.RWMutex + m map[dhcp6.MessageType]Handler +} + +// NewServeMux creates a new ServeMux which is ready to accept Handlers. +func NewServeMux() *ServeMux { + return &ServeMux{ + m: make(map[dhcp6.MessageType]Handler), + } +} + +// ServeDHCP implements Handler for ServeMux, and serves a DHCP request using +// the appropriate handler for an input Request's MessageType. If the +// MessageType does not match a valid Handler, ServeDHCP does not invoke any +// handlers, ignoring a client's request. +func (mux *ServeMux) ServeDHCP(w ResponseSender, r *Request) { + mux.mu.RLock() + defer mux.mu.RUnlock() + h, ok := mux.m[r.MessageType] + if !ok { + return + } + + h.ServeDHCP(w, r) +} + +// Handle registers a MessageType and Handler with a ServeMux, so that +// future requests with that MessageType will invoke the Handler. +func (mux *ServeMux) Handle(mt dhcp6.MessageType, handler Handler) { + mux.mu.Lock() + mux.m[mt] = handler + mux.mu.Unlock() +} + +// HandleFunc registers a MessageType and function as a HandlerFunc with a +// ServeMux, so that future requests with that MessageType will invoke the +// HandlerFunc. +func (mux *ServeMux) HandleFunc(mt dhcp6.MessageType, handler func(ResponseSender, *Request)) { + mux.Handle(mt, HandlerFunc(handler)) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6server/mux_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6server/mux_test.go new file mode 100644 index 00000000..d54fd566 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6server/mux_test.go @@ -0,0 +1,86 @@ +package dhcp6server_test + +import ( + "testing" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/dhcp6server" + "github.com/mdlayher/dhcp6/dhcp6test" +) + +// TestServeMuxHandleNoResponse verifies that no Handler is invoked when a +// ServeMux does not have a Handler registered for a given message type. +func TestServeMuxHandleNoResponse(t *testing.T) { + mux := dhcp6server.NewServeMux() + + r, err := dhcp6server.ParseRequest([]byte{1, 1, 2, 3}, nil) + if err != nil { + t.Fatal(err) + } + + w := dhcp6test.NewRecorder(r.TransactionID) + mux.ServeDHCP(w, r) + + if mt := w.MessageType; mt != dhcp6.MessageType(0) { + t.Fatalf("reply packet empty, but got message type: %v", mt) + } + if l := len(w.Options()); l > 0 { + t.Fatalf("reply packet empty, but got %d options", l) + } +} + +// TestServeMuxHandleOK verifies that a Handler is invoked when a ServeMux +// has a Handler registered for a given message type. +func TestServeMuxHandleOK(t *testing.T) { + mux := dhcp6server.NewServeMux() + mt := dhcp6.MessageTypeSolicit + + mux.Handle(mt, &solicitHandler{}) + + r, err := dhcp6server.ParseRequest([]byte{byte(mt), 0, 1, 2}, nil) + if err != nil { + t.Fatal(err) + } + + w := dhcp6test.NewRecorder(r.TransactionID) + mux.ServeDHCP(w, r) + + if want, got := dhcp6.MessageTypeAdvertise, w.MessageType; want != got { + t.Fatalf("unexpected response message type: %v != %v", want, got) + } +} + +// TestServeMuxHandleFuncOK verifies that a normal function which can be used +// as a Handler is invoked when a ServeMux has a HandlerFunc registered for +// a given message type. +func TestServeMuxHandleFuncOK(t *testing.T) { + mux := dhcp6server.NewServeMux() + mt := dhcp6.MessageTypeSolicit + + mux.HandleFunc(mt, solicit) + + r, err := dhcp6server.ParseRequest([]byte{byte(mt), 0, 1, 2}, nil) + if err != nil { + t.Fatal(err) + } + + w := dhcp6test.NewRecorder(r.TransactionID) + mux.ServeDHCP(w, r) + + if want, got := dhcp6.MessageTypeAdvertise, w.MessageType; want != got { + t.Fatalf("unexpected response message type: %v != %v", want, got) + } +} + +// solicitHandler is a Handler which returns an Advertise in reply +// to a Solicit request. +type solicitHandler struct{} + +func (h *solicitHandler) ServeDHCP(w dhcp6server.ResponseSender, r *dhcp6server.Request) { + solicit(w, r) +} + +// solicit is a function which can be adapted as a HandlerFunc. +func solicit(w dhcp6server.ResponseSender, r *dhcp6server.Request) { + w.Send(dhcp6.MessageTypeAdvertise) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6server/request.go b/vendor/github.com/mdlayher/dhcp6/dhcp6server/request.go new file mode 100644 index 00000000..329062b9 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6server/request.go @@ -0,0 +1,53 @@ +package dhcp6server + +import ( + "net" + + "github.com/mdlayher/dhcp6" +) + +// Request represents a processed DHCP request received by a server. +// Its struct members contain information regarding the request's message +// type, transaction ID, client ID, options, etc. +type Request struct { + // DHCP message type, such as Solicit, Request, or Renew. + MessageType dhcp6.MessageType + + // Unique transaction ID, which should be preserved across + // multiple requests to the same DHCP server. ServeDHCP + // implementations must manually verify that the same + // transaction ID is used. + TransactionID [3]byte + + // Map of options sent by client, carrying additional + // information or requesting additional information from + // the server. Its methods can be used to check for and parse + // additional information relating to a request. + Options dhcp6.Options + + // Length of the DHCP request, in bytes. + Length int64 + + // Network address which was used to contact the DHCP server. + RemoteAddr string +} + +// ParseRequest creates a new Request from an input byte slice and UDP address. +// It populates the basic struct members which can be used in a DHCP handler. +// +// If the input byte slice is not a valid DHCP packet, ErrInvalidPacket is +// returned. +func ParseRequest(b []byte, remoteAddr *net.UDPAddr) (*Request, error) { + p := new(dhcp6.Packet) + if err := p.UnmarshalBinary(b); err != nil { + return nil, err + } + + return &Request{ + MessageType: p.MessageType, + TransactionID: p.TransactionID, + Options: p.Options, + Length: int64(len(b)), + RemoteAddr: remoteAddr.String(), + }, nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6server/request_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6server/request_test.go new file mode 100644 index 00000000..c0a0919e --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6server/request_test.go @@ -0,0 +1,51 @@ +package dhcp6server + +import ( + "net" + "reflect" + "testing" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/dhcp6opts" +) + +// TestParseRequest verifies that ParseRequest returns a consistent +// Request struct for use in Handler types. +func TestParseRequest(t *testing.T) { + p := &dhcp6.Packet{ + MessageType: dhcp6.MessageTypeSolicit, + TransactionID: [3]byte{1, 2, 3}, + Options: make(dhcp6.Options), + } + var uuid [16]byte + p.Options.Add(dhcp6.OptionClientID, dhcp6opts.NewDUIDUUID(uuid)) + + addr := &net.UDPAddr{ + IP: net.ParseIP("::1"), + Port: 546, + } + + buf, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + r := &Request{ + MessageType: p.MessageType, + TransactionID: p.TransactionID, + Options: make(dhcp6.Options), + Length: int64(len(buf)), + RemoteAddr: "[::1]:546", + } + r.Options.Add(dhcp6.OptionClientID, dhcp6opts.NewDUIDUUID(uuid)) + + gotR, err := ParseRequest(buf, addr) + if err != nil { + t.Fatal(err) + } + + if want, got := r, gotR; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected Request for ParseRequest(%v, %v)\n- want: %v\n- got: %v", + p, addr, want, got) + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6server/server.go b/vendor/github.com/mdlayher/dhcp6/dhcp6server/server.go new file mode 100644 index 00000000..d6ffbe83 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6server/server.go @@ -0,0 +1,319 @@ +package dhcp6server + +import ( + "errors" + "log" + "net" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/dhcp6opts" + "golang.org/x/net/ipv6" +) + +var ( + // AllRelayAgentsAndServersAddr is the multicast address group which is + // used to communicate with neighboring (on-link) DHCP servers and relay + // agents, as defined in RFC 3315, Section 5.1. All DHCP servers + // and relay agents are members of this multicast group. + AllRelayAgentsAndServersAddr = &net.IPAddr{ + IP: net.ParseIP("ff02::1:2"), + } + + // AllServersAddr is the multicast address group which is used by a + // DHCP relay agent to communicate with DHCP servers, if the relay agent + // wishes to send messages to all servers, or does not know the unicast + // address of a server. All DHCP servers are members of this multicast + // group. + AllServersAddr = &net.IPAddr{ + IP: net.ParseIP("ff05::1:3"), + } + + // errClosing is a special value used to stop the server's read loop + // when a connection is closing. + errClosing = errors.New("use of closed network connection") +) + +// PacketConn is an interface which types must implement in order to serve +// DHCP connections using Server.Serve. +type PacketConn interface { + ReadFrom(b []byte) (n int, cm *ipv6.ControlMessage, src net.Addr, err error) + WriteTo(b []byte, cm *ipv6.ControlMessage, dst net.Addr) (n int, err error) + + Close() error + + JoinGroup(ifi *net.Interface, group net.Addr) error + LeaveGroup(ifi *net.Interface, group net.Addr) error + + SetControlMessage(cf ipv6.ControlFlags, on bool) error +} + +// Server represents a DHCP server, and is used to configure a DHCP server's +// behavior. +type Server struct { + // Iface is the the network interface on which this server should + // listen. Traffic from any other network interface will be filtered out + // and ignored by the server. + Iface *net.Interface + + // Addr is the network address which this server should bind to. The + // default value is [::]:547, as specified in RFC 3315, Section 5.2. + Addr string + + // Handler is the handler to use while serving DHCP requests. If this + // value is nil, the Server will panic. + Handler Handler + + // MulticastGroups designates which IPv6 multicast groups this server + // will join on start-up. Because the default configuration acts as a + // DHCP server, most servers will typically join both + // AllRelayAgentsAndServersAddr, and AllServersAddr. If configuring a + // DHCP relay agent, only the former value should be used. + MulticastGroups []*net.IPAddr + + // ServerID is the the server's DUID, which uniquely identifies this + // server to clients. If no DUID is specified, a DUID-LL will be + // generated using Iface's hardware type and address. If possible, + // servers with persistent storage available should generate a DUID-LLT + // and store it for future use. + ServerID dhcp6opts.DUID + + // ErrorLog is an optional logger which can be used to report errors and + // erroneous behavior while the server is accepting client requests. + // If ErrorLog is nil, logging goes to os.Stderr via the log package's + // standard logger. + ErrorLog *log.Logger +} + +// logf logs a message using the server's ErrorLog logger, or the log package +// standard logger, if ErrorLog is nil. +func (s *Server) logf(format string, args ...interface{}) { + if s.ErrorLog != nil { + s.ErrorLog.Printf(format, args...) + } else { + log.Printf(format, args...) + } +} + +// ListenAndServe listens for UDP6 connections on the specified address of the +// specified interface, using the default Server configuration and specified +// handler to handle DHCPv6 connections. The Handler must not be nil. +// +// Any traffic which reaches the Server, and is not bound for the specified +// network interface, will be filtered out and ignored. +// +// In this configuration, the server acts as a DHCP server, but NOT as a +// DHCP relay agent. For more information on DHCP relay agents, see RFC 3315, +// Section 20. +func ListenAndServe(iface string, handler Handler) error { + // Verify network interface exists + ifi, err := net.InterfaceByName(iface) + if err != nil { + return err + } + + return (&Server{ + Iface: ifi, + Addr: "[::]:547", + Handler: handler, + MulticastGroups: []*net.IPAddr{ + AllRelayAgentsAndServersAddr, + AllServersAddr, + }, + }).ListenAndServe() +} + +// ListenAndServe listens on the address specified by s.Addr using the network +// interface defined in s.Iface. Traffic from any other interface will be +// filtered out and ignored. Serve is called to handle serving DHCP traffic +// once ListenAndServe opens a UDP6 packet connection. +func (s *Server) ListenAndServe() error { + // Open UDP6 packet connection listener on specified address + conn, err := net.ListenPacket("udp6", s.Addr) + if err != nil { + return err + } + + defer conn.Close() + return s.Serve(ipv6.NewPacketConn(conn)) +} + +// Serve configures and accepts incoming connections on PacketConn p, creating a +// new goroutine for each. Serve configures IPv6 control message settings, joins +// the appropriate multicast groups, and begins listening for incoming connections. +// +// The service goroutine reads requests, generate the appropriate Request and +// ResponseSender values, then calls s.Handler to handle the request. +func (s *Server) Serve(p PacketConn) error { + // If no DUID was set for server previously, generate a DUID-LL + // now using the interface's hardware address, and just assume the + // "Ethernet 10Mb" hardware type since the caller probably doesn't care. + if s.ServerID == nil { + const ethernet10Mb uint16 = 1 + s.ServerID = dhcp6opts.NewDUIDLL(ethernet10Mb, s.Iface.HardwareAddr) + } + + // Filter any traffic which does not indicate the interface + // defined by s.Iface. + if err := p.SetControlMessage(ipv6.FlagInterface, true); err != nil { + return err + } + + // Join appropriate multicast groups + for _, g := range s.MulticastGroups { + if err := p.JoinGroup(s.Iface, g); err != nil { + return err + } + } + + // Set up IPv6 packet connection, and on return, handle leaving multicast + // groups and closing connection + defer func() { + for _, g := range s.MulticastGroups { + _ = p.LeaveGroup(s.Iface, g) + } + + _ = p.Close() + }() + + // Loop and read requests until exit + buf := make([]byte, 1500) + for { + n, cm, addr, err := p.ReadFrom(buf) + if err != nil { + // Stop serve loop gracefully when closing + if err == errClosing { + return nil + } + + // BUG(mdlayher): determine if error can be temporary + return err + } + + // Filter any traffic with a control message indicating an incorrect + // interface index + if cm != nil && cm.IfIndex != s.Iface.Index { + continue + } + + // Create conn struct with data specific to this connection + uc, err := s.newConn(p, addr.(*net.UDPAddr), n, buf) + if err != nil { + continue + } + + // Serve conn and continue looping for more connections + go uc.serve() + } +} + +// conn represents an in-flight DHCP connection, and contains information about +// the connection and server. +type conn struct { + conn PacketConn + remoteAddr *net.UDPAddr + server *Server + buf []byte +} + +// newConn creates a new conn using information received in a single DHCP +// connection. newConn makes a copy of the input buffer for use in handling +// a single connection. +// BUG(mdlayher): consider using a sync.Pool with many buffers available to avoid +// allocating a new one on each connection +func (s *Server) newConn(p PacketConn, addr *net.UDPAddr, n int, buf []byte) (*conn, error) { + c := &conn{ + conn: p, + remoteAddr: addr, + server: s, + buf: make([]byte, n, n), + } + copy(c.buf, buf[:n]) + + return c, nil +} + +// response represents a DHCP response, and implements ResponseSender so that +// outbound Packets can be appropriately created and sent to a client. +type response struct { + conn PacketConn + remoteAddr *net.UDPAddr + req *Request + + options dhcp6.Options +} + +// Options returns the Options map, which can be modified before a call +// to Write. When Write is called, the Options map is enumerated into an +// ordered slice of option codes and values. +func (r *response) Options() dhcp6.Options { + return r.options +} + +// Send uses the input message typ, the transaction ID sent by a client, +// and the options set by Options, to create and send a Packet to the +// client's address. +func (r *response) Send(mt dhcp6.MessageType) (int, error) { + p := &dhcp6.Packet{ + MessageType: mt, + TransactionID: r.req.TransactionID, + Options: r.options, + } + + b, err := p.MarshalBinary() + if err != nil { + return 0, err + } + + return r.conn.WriteTo(b, nil, r.remoteAddr) +} + +// serve handles serving an individual DHCP connection, and is invoked in a +// goroutine. +func (c *conn) serve() { + // Attempt to parse a Request from a raw packet, providing a nicer + // API for callers to implement their own DHCP request handlers. + r, err := ParseRequest(c.buf, c.remoteAddr) + if err != nil { + // Malformed packets get no response + if err == dhcp6.ErrInvalidPacket { + return + } + + // BUG(mdlayher): decide to log or handle other request errors + c.server.logf("%s: error parsing request: %s", c.remoteAddr.String(), err.Error()) + return + } + + // Filter out unknown/invalid message types, using the lowest and highest + // numbered types + if r.MessageType < dhcp6.MessageTypeSolicit || r.MessageType > dhcp6.MessageTypeDHCPv4Response { + c.server.logf("%s: unrecognized message type: %d", c.remoteAddr.String(), r.MessageType) + return + } + + // Set up response to send responses back to the original requester + w := &response{ + remoteAddr: c.remoteAddr, + conn: c.conn, + req: r, + options: make(dhcp6.Options), + } + + // Add server ID to response + if sID := c.server.ServerID; sID != nil { + _ = w.options.Add(dhcp6.OptionServerID, sID) + } + + // If available in request, add client ID to response + if cID, err := dhcp6opts.GetClientID(r.Options); err == nil { + w.options.Add(dhcp6.OptionClientID, cID) + } + + // Enforce a valid Handler. + handler := c.server.Handler + if handler == nil { + panic("nil DHCPv6 handler for server") + } + + handler.ServeDHCP(w, r) +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6server/server_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6server/server_test.go new file mode 100644 index 00000000..580c7cf8 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6server/server_test.go @@ -0,0 +1,530 @@ +package dhcp6server + +import ( + "bytes" + "io/ioutil" + "log" + "net" + "reflect" + "testing" + "time" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/dhcp6opts" + "golang.org/x/net/ipv6" +) + +func init() { + // Discard all output from Server.logf + log.SetOutput(ioutil.Discard) +} + +// TestServeIPv6ControlParameters verifies that a PacketConn successfully +// joins and leaves the appropriate multicast groups designated by a Server, +// and that the appropriate IPv6 control flags are set. +func TestServeIPv6ControlParameters(t *testing.T) { + s := &Server{ + MulticastGroups: []*net.IPAddr{ + AllRelayAgentsAndServersAddr, + AllServersAddr, + }, + } + + // Send pseudo-Packet to avoid EOF, even though it does not matter + // for this test + r := &testMessage{} + r.b.Write([]byte{0, 0, 0, 0}) + + // Don't expect a reply, don't handle a request + _, ip6, err := testServe(r, s, false, func(w ResponseSender, r *Request) {}) + if err != nil { + t.Fatal(err) + } + + for _, m := range s.MulticastGroups { + var foundJ bool + var foundL bool + + for _, j := range ip6.joined { + if m.String() == j.String() { + foundJ = true + break + } + } + for _, l := range ip6.left { + if m.String() == l.String() { + foundL = true + break + } + } + + if !foundJ { + t.Fatalf("did not find joined IPv6 multicast group: %v", m) + } + if !foundL { + t.Fatalf("did not find left IPv6 multicast group: %v", m) + } + } + + if b, ok := ip6.flags[ipv6.FlagInterface]; !ok || !b { + t.Fatalf("FlagInterface not found or not set to true:\n- found: %v\n- bool: %v", ok, b) + } + + if !ip6.closed { + t.Fatal("IPv6 connection not closed after Serve") + } +} + +// TestServeWithSetServerID verifies that Serve uses the server ID provided +// instead of generating its own, when a server ID is set. +func TestServeWithSetServerID(t *testing.T) { + p := &dhcp6.Packet{ + MessageType: dhcp6.MessageTypeSolicit, + TransactionID: [3]byte{0, 1, 2}, + } + + pb, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + r := &testMessage{} + r.b.Write(pb) + + duid, err := dhcp6opts.NewDUIDLLT(1, time.Now(), []byte{0, 1, 0, 1, 0, 1}) + if err != nil { + t.Fatal(err) + } + + s := &Server{ + ServerID: duid, + } + + // Expect a reply with type advertise + mt := dhcp6.MessageTypeAdvertise + w, _, err := testServe(r, s, true, func(w ResponseSender, r *Request) { + w.Send(mt) + }) + if err != nil { + t.Fatal(err) + } + + wp := new(dhcp6.Packet) + if err := wp.UnmarshalBinary(w.b.Bytes()); err != nil { + t.Fatal(err) + } + + if want, got := mt, wp.MessageType; want != got { + t.Fatalf("unexpected message type: %v != %v", want, got) + } + + want, err := duid.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + got, err := wp.Options.GetOne(dhcp6.OptionServerID) + if err != nil { + t.Fatal("server ID not found in reply") + } + + if !bytes.Equal(want, got) { + t.Fatalf("unexpected server ID bytes:\n- want: %v\n- got: %v", want, got) + } +} + +// TestServeCreateResponseSenderWithCorrectParameters verifies that a new ResponseSender +// gets appropriate transaction ID, client ID, and server ID values copied into +// it before a Handler is invoked. +func TestServeCreateResponseSenderWithCorrectParameters(t *testing.T) { + txID := [3]byte{0, 1, 2} + duid := dhcp6opts.NewDUIDLL(1, []byte{0, 1, 0, 1, 0, 1}) + + p := &dhcp6.Packet{ + MessageType: dhcp6.MessageTypeSolicit, + TransactionID: txID, + Options: make(dhcp6.Options), + } + p.Options.Add(dhcp6.OptionClientID, duid) + + pb, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + r := &testMessage{} + r.b.Write(pb) + + // Do not expect a reply, but do some validation to ensure that Serve + // sets up appropriate Request and ResponseSender values from an input request + _, _, err = testServe(r, nil, false, func(w ResponseSender, r *Request) { + if want, got := txID[:], r.TransactionID[:]; !bytes.Equal(want, got) { + t.Fatalf("unexpected transaction ID:\n- want: %v\n- got: %v", want, got) + } + + cID, err := dhcp6opts.GetClientID(w.Options()) + if err != nil { + t.Fatal("ResponseSender options did not contain client ID") + } + if want, got := duid, cID; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected client ID bytes:\n- want: %v\n- got: %v", want, got) + } + + if sID, err := dhcp6opts.GetServerID(w.Options()); err != nil || sID == nil { + t.Fatal("ResponseSender options did not contain server ID") + } + }) + if err != nil { + t.Fatal(err) + } +} + +// TestServeIgnoreWrongCMIfIndex verifies that Serve will ignore incoming +// connections with an incorrect IPv6 control message interface index. +func TestServeIgnoreWrongCMIfIndex(t *testing.T) { + // Wrong interface index in control message + r := &testMessage{ + cm: &ipv6.ControlMessage{ + IfIndex: -1, + }, + } + r.b.Write([]byte{0, 0, 0, 0}) + + s := &Server{ + Iface: &net.Interface{ + Index: 0, + }, + } + + // Expect no reply at all + w, _, err := testServe(r, s, false, func(w ResponseSender, r *Request) {}) + if err != nil { + t.Fatal(err) + } + + if l := w.b.Len(); l > 0 { + t.Fatalf("reply should be empty, but got length: %d", l) + } + if cm := w.cm; cm != nil { + t.Fatalf("control message should be nil, but got: %v", cm) + } + if addr := w.addr; addr != nil { + t.Fatalf("address should be nil, but got: %v", addr) + } +} + +// TestServeIgnoreInvalidPacket verifies that Serve will ignore invalid +// request packets. +func TestServeIgnoreInvalidPacket(t *testing.T) { + // Packet too short to be valid + r := &testMessage{} + r.b.Write([]byte{0, 0, 0}) + + // Expect no reply at all + w, _, err := testServe(r, nil, false, func(w ResponseSender, r *Request) {}) + if err != nil { + t.Fatal(err) + } + + if l := w.b.Len(); l > 0 { + t.Fatalf("reply should be empty, but got length: %d", l) + } + if cm := w.cm; cm != nil { + t.Fatalf("control message should be nil, but got: %v", cm) + } + if addr := w.addr; addr != nil { + t.Fatalf("address should be nil, but got: %v", addr) + } +} + +// TestServeIgnoreBadMessageType verifies that Serve will ignore request +// packets with invalid message types. +func TestServeIgnoreBadMessageType(t *testing.T) { + // Message types not known + badMT := []byte{0, 22} + for _, mt := range badMT { + r := &testMessage{} + r.b.Write([]byte{mt, 0, 0, 0}) + + // Expect no reply at all + w, _, err := testServe(r, nil, false, func(w ResponseSender, r *Request) {}) + if err != nil { + t.Fatal(err) + } + + if l := w.b.Len(); l > 0 { + t.Fatalf("reply should be empty, but got length: %d", l) + } + if cm := w.cm; cm != nil { + t.Fatalf("control message should be nil, but got: %v", cm) + } + if addr := w.addr; addr != nil { + t.Fatalf("address should be nil, but got: %v", addr) + } + } +} + +// TestServeOK verifies that Serve correctly handles an incoming request and +// all of its options, and replies with expected values. +func TestServeOK(t *testing.T) { + txID := [3]byte{0, 1, 2} + duid := dhcp6opts.NewDUIDLL(1, []byte{0, 1, 0, 1, 0, 1}) + + // Perform an entire Solicit transaction + p := &dhcp6.Packet{ + MessageType: dhcp6.MessageTypeSolicit, + TransactionID: txID, + Options: make(dhcp6.Options), + } + p.Options.Add(dhcp6.OptionClientID, duid) + + pb, err := p.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + // Send from a different mock IP + r := &testMessage{ + addr: &net.UDPAddr{ + IP: net.ParseIP("::2"), + }, + } + r.b.Write(pb) + + // Expect these option values set by server + var preference dhcp6opts.Preference = 255 + sCode := dhcp6.StatusSuccess + sMsg := "success" + + // Expect Advertise reply with several options added by server + mt := dhcp6.MessageTypeAdvertise + w, _, err := testServe(r, nil, true, func(w ResponseSender, r *Request) { + w.Options().Add(dhcp6.OptionPreference, preference) + w.Options().Add(dhcp6.OptionStatusCode, dhcp6opts.NewStatusCode(sCode, sMsg)) + + w.Send(mt) + }) + if err != nil { + t.Fatal(err) + } + + // For now, outgoing control message is always nil + if w.cm != nil { + t.Fatal("control message is never set on outgoing reply") + } + if want, got := r.addr, w.addr; want != got { + t.Fatalf("unexpected client address: %v != %v", want, got) + } + + wp := new(dhcp6.Packet) + if err := wp.UnmarshalBinary(w.b.Bytes()); err != nil { + t.Fatal(err) + } + + if want, got := mt, wp.MessageType; want != got { + t.Fatalf("unexpected message type: %v != %v", want, got) + } + + if want, got := txID[:], wp.TransactionID[:]; !bytes.Equal(want, got) { + t.Fatalf("unexpected transaction ID:\n- want: %v\n- got: %v", want, got) + } + + cID, err := dhcp6opts.GetClientID(wp.Options) + if err != nil { + t.Fatalf("response options did not contain client ID: %v", err) + } + if want, got := duid, cID; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected client ID bytes:\n- want: %v\n- got: %v", want, got) + } + if sID, err := dhcp6opts.GetServerID(wp.Options); err != nil || sID == nil { + t.Fatal("ResponseSender options did not contain server ID") + } + + pr, err := dhcp6opts.GetPreference(wp.Options) + if err != nil { + t.Fatal("response Options did not contain preference") + } + if want, got := preference, pr; want != got { + t.Fatalf("unexpected preference value: %v != %v", want, got) + } + + st, err := dhcp6opts.GetStatusCode(wp.Options) + if err != nil { + t.Fatal("response Options did not contain status code") + } + if want, got := sCode, st.Code; want != got { + t.Fatalf("unexpected status code value: %v != %v", want, got) + } + if want, got := sMsg, st.Message; want != got { + t.Fatalf("unexpected status code meesage: %q != %q", want, got) + } +} + +// testServe performs a single transaction using the input message, server +// configuration, whether or not a reply is expected, and a closure which +// acts as a HandlerFunc. +func testServe(r *testMessage, s *Server, expectReply bool, fn func(w ResponseSender, r *Request)) (*testMessage, *recordIPv6PacketConn, error) { + // If caller doesn't specify a testMessage or client address + // for it, configure it for them + if r == nil { + r = &testMessage{} + } + if r.addr == nil { + r.addr = &net.UDPAddr{ + IP: net.ParseIP("::1"), + } + } + + // If caller doesn't specify Server value, configure it for + // them using input function as HandlerFunc. + if s == nil { + s = &Server{} + } + if s.Iface == nil { + s.Iface = &net.Interface{ + Name: "foo0", + Index: 0, + } + } + s.Handler = HandlerFunc(fn) + + // Implements PacketConn to capture request/response + tc := &testPacketConn{ + r: r, + w: &testMessage{}, + + // Record IPv6 control parameters + recordIPv6PacketConn: &recordIPv6PacketConn{ + joined: make([]net.Addr, 0), + left: make([]net.Addr, 0), + flags: make(map[ipv6.ControlFlags]bool), + }, + } + + // Perform a single read and possibly write before returning + // an error on second read to close server + c := &oneReadPacketConn{ + PacketConn: tc, + + readDoneC: make(chan struct{}, 0), + writeDoneC: make(chan struct{}, 0), + } + + // If no reply is expected, this channel will never be closed, + // and should be closed immediately + if !expectReply { + close(c.writeDoneC) + } + + // Handle request + err := s.Serve(c) + + // Wait for read and write to complete + <-c.readDoneC + <-c.writeDoneC + + // Return written values, IPv6 control parameters + return tc.w, tc.recordIPv6PacketConn, err +} + +// oneReadPacketConn allows a single read and possibly write transaction using +// the embedded PacketConn before issuing errClosing to close the server. +type oneReadPacketConn struct { + PacketConn + + err error + txDone bool + + readDoneC chan struct{} + writeDoneC chan struct{} +} + +// ReadFrom reads input bytes using the underlying PacketConn only once. Once +// the read is completed, readDoneC will close and stop blocking. Any +// further ReadFrom calls result in errClosing. +func (c *oneReadPacketConn) ReadFrom(b []byte) (int, *ipv6.ControlMessage, net.Addr, error) { + if c.txDone { + return 0, nil, nil, errClosing + } + c.txDone = true + + n, cm, addr, err := c.PacketConn.ReadFrom(b) + close(c.readDoneC) + return n, cm, addr, err +} + +// WriteTo writes input bytes and IPv6 control parameters to the underlying +// PacketConn. Once the write is completed, writeDoneC will close and +// stop blocking. +func (c *oneReadPacketConn) WriteTo(b []byte, cm *ipv6.ControlMessage, dst net.Addr) (int, error) { + n, err := c.PacketConn.WriteTo(b, cm, dst) + close(c.writeDoneC) + return n, err +} + +// testPacketConn captures client requests, server responses, and IPv6 +// control parameters set by the server. +type testPacketConn struct { + r *testMessage + w *testMessage + + *recordIPv6PacketConn +} + +// testMessage captures data from a request or response. +type testMessage struct { + b bytes.Buffer + cm *ipv6.ControlMessage + addr net.Addr +} + +// ReadFrom returns data from a preconfigured testMessage containing bytes, +// an IPv6 control message, and a client address. +func (c *testPacketConn) ReadFrom(b []byte) (int, *ipv6.ControlMessage, net.Addr, error) { + n, err := c.r.b.Read(b) + return n, c.r.cm, c.r.addr, err +} + +// WriteTo writes data to a testMessage containing bytes, an IPv6 control +// message, and a client address. +func (c *testPacketConn) WriteTo(b []byte, cm *ipv6.ControlMessage, dst net.Addr) (int, error) { + n, err := c.w.b.Write(b) + c.w.cm = cm + c.w.addr = dst + + return n, err +} + +// recordIPv6PacketConn tracks IPv6 control parameters, such as joined and +// left multicast groups, control flags, and whether or not the connection +// was closed. +type recordIPv6PacketConn struct { + closed bool + joined []net.Addr + left []net.Addr + flags map[ipv6.ControlFlags]bool +} + +// Close records that an IPv6 connection was closed. +func (c *recordIPv6PacketConn) Close() error { + c.closed = true + return nil +} + +// JoinGroup records that a multicast group was joined. +func (c *recordIPv6PacketConn) JoinGroup(ifi *net.Interface, group net.Addr) error { + c.joined = append(c.joined, group) + return nil +} + +// LeaveGroup records that a multicast group was left. +func (c *recordIPv6PacketConn) LeaveGroup(ifi *net.Interface, group net.Addr) error { + c.left = append(c.left, group) + return nil +} + +// SetControlMessage records that an IPv6 control message was set. +func (c *recordIPv6PacketConn) SetControlMessage(cf ipv6.ControlFlags, on bool) error { + c.flags[cf] = on + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6test/recorder.go b/vendor/github.com/mdlayher/dhcp6/dhcp6test/recorder.go new file mode 100644 index 00000000..5da360e8 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6test/recorder.go @@ -0,0 +1,43 @@ +// Package dhcp6test provides utilities for testing DHCPv6 clients and servers. +package dhcp6test + +import ( + "github.com/mdlayher/dhcp6" +) + +// Recorder is a dhcp6.ResponseSender which captures a response's message type and +// options, for inspection during tests. +type Recorder struct { + MessageType dhcp6.MessageType + TransactionID [3]byte + OptionsMap dhcp6.Options + Packet *dhcp6.Packet +} + +// NewRecorder creates a new Recorder which uses the input transaction ID. +func NewRecorder(txID [3]byte) *Recorder { + return &Recorder{ + TransactionID: txID, + OptionsMap: make(dhcp6.Options), + } +} + +// Options returns the Options map of a Recorder. +func (r *Recorder) Options() dhcp6.Options { + return r.OptionsMap +} + +// Send creates a new DHCPv6 packet using the input message type, and stores +// it for later inspection. +func (r *Recorder) Send(mt dhcp6.MessageType) (int, error) { + r.MessageType = mt + p := &dhcp6.Packet{ + MessageType: mt, + TransactionID: r.TransactionID, + Options: r.OptionsMap, + } + r.Packet = p + + b, err := p.MarshalBinary() + return len(b), err +} diff --git a/vendor/github.com/mdlayher/dhcp6/dhcp6test/recorder_test.go b/vendor/github.com/mdlayher/dhcp6/dhcp6test/recorder_test.go new file mode 100644 index 00000000..0d9f9f28 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/dhcp6test/recorder_test.go @@ -0,0 +1,42 @@ +package dhcp6test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/mdlayher/dhcp6" + "github.com/mdlayher/dhcp6/dhcp6opts" +) + +// TestRecorder verifies that a Recorder properly captures information +// when a message is sent. +func TestRecorder(t *testing.T) { + mt := dhcp6.MessageTypeAdvertise + txID := [3]byte{0, 1, 2} + clientID := dhcp6opts.NewDUIDLL(1, []byte{0, 1, 0, 1, 0, 1}) + + r := NewRecorder(txID) + if err := r.Options().Add(dhcp6.OptionClientID, clientID); err != nil { + t.Fatal(err) + } + + if _, err := r.Send(mt); err != nil { + t.Fatal(err) + } + + if want, got := mt, r.MessageType; want != got { + t.Fatalf("unexpected message type: %v != %v", want, got) + } + if want, got := txID[:], r.TransactionID[:]; !bytes.Equal(want, got) { + t.Fatalf("unexpected transaction ID: %v != %v", want, got) + } + + duid, err := dhcp6opts.GetClientID(r.Options()) + if err != nil { + t.Fatal(err) + } + if want, got := clientID, duid; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected client ID: %v != %v", want, got) + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/internal/buffer/buffer.go b/vendor/github.com/mdlayher/dhcp6/internal/buffer/buffer.go new file mode 100644 index 00000000..a777524f --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/internal/buffer/buffer.go @@ -0,0 +1,140 @@ +package buffer + +import ( + "encoding/binary" +) + +var order = binary.BigEndian + +// Buffer encapsulates marshaling unsigned integer and byte slice values. +type Buffer struct { + // data is the underlying data. + data []byte +} + +// New consumes b for marshaling or unmarshaling. +func New(b []byte) *Buffer { + return &Buffer{b} +} + +// append appends n bytes to the Buffer and returns a slice pointing to the +// newly appended bytes. +func (b *Buffer) append(n int) []byte { + b.data = append(b.data, make([]byte, n)...) + return b.data[len(b.data)-n:] +} + +// Data is unconsumed data remaining in the Buffer. +func (b *Buffer) Data() []byte { + return b.data +} + +// Remaining consumes and returns a copy of all remaining bytes in the Buffer. +func (b *Buffer) Remaining() []byte { + p := b.Consume(len(b.Data())) + cp := make([]byte, len(p)) + copy(cp, p) + return cp +} + +// consume consumes n bytes from the Buffer. It returns nil, false if there +// aren't enough bytes left. +func (b *Buffer) consume(n int) ([]byte, bool) { + if !b.Has(n) { + return nil, false + } + rval := b.data[:n] + b.data = b.data[n:] + return rval, true +} + +// Consume consumes n bytes from the Buffer. It returns nil if there aren't +// enough bytes left. +func (b *Buffer) Consume(n int) []byte { + v, ok := b.consume(n) + if !ok { + return nil + } + return v +} + +// Has returns true if n bytes are available. +func (b *Buffer) Has(n int) bool { + return len(b.data) >= n +} + +// Len returns the length of the remaining bytes. +func (b *Buffer) Len() int { + return len(b.data) +} + +// Read8 reads a byte from the Buffer. +func (b *Buffer) Read8() uint8 { + v, ok := b.consume(1) + if !ok { + return 0 + } + return uint8(v[0]) +} + +// Read16 reads a 16-bit value from the Buffer. +func (b *Buffer) Read16() uint16 { + v, ok := b.consume(2) + if !ok { + return 0 + } + return order.Uint16(v) +} + +// Read32 reads a 32-bit value from the Buffer. +func (b *Buffer) Read32() uint32 { + v, ok := b.consume(4) + if !ok { + return 0 + } + return order.Uint32(v) +} + +// Read64 reads a 64-bit value from the Buffer. +func (b *Buffer) Read64() uint64 { + v, ok := b.consume(8) + if !ok { + return 0 + } + return order.Uint64(v) +} + +// ReadBytes reads exactly len(p) values from the Buffer. +func (b *Buffer) ReadBytes(p []byte) { + copy(p, b.Consume(len(p))) +} + +// Write8 writes a byte to the Buffer. +func (b *Buffer) Write8(v uint8) { + b.append(1)[0] = byte(v) +} + +// Write16 writes a 16-bit value to the Buffer. +func (b *Buffer) Write16(v uint16) { + order.PutUint16(b.append(2), v) +} + +// Write32 writes a 32-bit value to the Buffer. +func (b *Buffer) Write32(v uint32) { + order.PutUint32(b.append(4), v) +} + +// Write64 writes a 64-bit value to the Buffer. +func (b *Buffer) Write64(v uint64) { + order.PutUint64(b.append(8), v) +} + +// WriteN returns a newly appended n-size Buffer to write to. +func (b *Buffer) WriteN(n int) []byte { + return b.append(n) +} + +// WriteBytes writes p to the Buffer. +func (b *Buffer) WriteBytes(p []byte) { + copy(b.append(len(p)), p) +} diff --git a/vendor/github.com/mdlayher/dhcp6/options.go b/vendor/github.com/mdlayher/dhcp6/options.go new file mode 100644 index 00000000..a3302c21 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/options.go @@ -0,0 +1,149 @@ +package dhcp6 + +import ( + "encoding" + "sort" + + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// Options is a map of OptionCode keys with a slice of byte slice values. +// +// Its methods can be used to easily check for additional information from a +// packet. Get and GetOne should be used to access data from Options. +type Options map[OptionCode][][]byte + +// Add adds a new OptionCode key and BinaryMarshaler struct's bytes to the +// Options map. +func (o Options) Add(key OptionCode, value encoding.BinaryMarshaler) error { + // Special case: since OptionRapidCommit actually has zero length, it is + // possible for an option key to appear with no value. + if value == nil { + o.AddRaw(key, nil) + return nil + } + + b, err := value.MarshalBinary() + if err != nil { + return err + } + + o.AddRaw(key, b) + return nil +} + +// AddRaw adds a new OptionCode key and raw value byte slice to the +// Options map. +func (o Options) AddRaw(key OptionCode, value []byte) { + o[key] = append(o[key], value) +} + +// Get attempts to retrieve all values specified by an OptionCode key. +// +// If a value is found, get returns a non-nil [][]byte and nil. If it is not +// found, Get returns nil and ErrOptionNotPresent. +func (o Options) Get(key OptionCode) ([][]byte, error) { + // Check for value by key. + v, ok := o[key] + if !ok { + return nil, ErrOptionNotPresent + } + + // Some options can actually have zero length (OptionRapidCommit), so + // just return an empty byte slice if this is the case. + if len(v) == 0 { + return [][]byte{{}}, nil + } + return v, nil +} + +// GetOne attempts to retrieve the first and only value specified by an +// OptionCode key. GetOne should only be used for OptionCode keys that must +// have at most one value. +// +// GetOne works just like Get, but if there is more than one value for the +// OptionCode key, ErrInvalidPacket will be returned. +func (o Options) GetOne(key OptionCode) ([]byte, error) { + vv, err := o.Get(key) + if err != nil { + return nil, err + } + + if len(vv) != 1 { + return nil, ErrInvalidPacket + } + return vv[0], nil +} + +// MarshalBinary allocates a buffer and writes options in their DHCPv6 binary +// format into the buffer. +func (o Options) MarshalBinary() ([]byte, error) { + b := buffer.New(nil) + for _, code := range o.sortedCodes() { + for _, data := range o[code] { + // 2 bytes: option code + b.Write16(uint16(code)) + + // 2 bytes: option length + b.Write16(uint16(len(data))) + + // N bytes: option data + b.WriteBytes(data) + } + } + return b.Data(), nil +} + +// UnmarshalBinary fills opts with option codes and corresponding values from +// an input byte slice. +// +// It is used with various different types to enable parsing of both top-level +// options, and options embedded within other options. If options data is +// malformed, it returns ErrInvalidOptions. +func (o *Options) UnmarshalBinary(p []byte) error { + buf := buffer.New(p) + *o = make(Options) + + for buf.Len() >= 4 { + // 2 bytes: option code + // 2 bytes: option length n + // n bytes: data + code := OptionCode(buf.Read16()) + length := buf.Read16() + if length == 0 { + continue + } + + // N bytes: option data + data := buf.Consume(int(length)) + if data == nil { + return ErrInvalidOptions + } + data = data[:int(length):int(length)] + + o.AddRaw(code, data) + } + + // Report error for any trailing bytes + if buf.Len() != 0 { + return ErrInvalidOptions + } + return nil +} + +// optionCodes implements sort.Interface. +type optionCodes []OptionCode + +func (b optionCodes) Len() int { return len(b) } +func (b optionCodes) Less(i int, j int) bool { return b[i] < b[j] } +func (b optionCodes) Swap(i int, j int) { b[i], b[j] = b[j], b[i] } + +func (o Options) sortedCodes() optionCodes { + var codes optionCodes + for code := range o { + codes = append(codes, code) + } + + sort.Sort(codes) + return codes +} diff --git a/vendor/github.com/mdlayher/dhcp6/options_test.go b/vendor/github.com/mdlayher/dhcp6/options_test.go new file mode 100644 index 00000000..50d5f92d --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/options_test.go @@ -0,0 +1,240 @@ +package dhcp6 + +import ( + "bytes" + "reflect" + "testing" +) + +type option struct { + code OptionCode + data []byte +} + +// TestOptionsAddRaw verifies that Options.AddRaw correctly creates or appends +// key/value Option pairs to an Options map. +func TestOptionsAddRaw(t *testing.T) { + var tests = []struct { + desc string + kv []option + options Options + }{ + { + desc: "one key/value pair", + kv: []option{ + { + code: 1, + data: []byte("foo"), + }, + }, + options: Options{ + 1: [][]byte{[]byte("foo")}, + }, + }, + { + desc: "two key/value pairs", + kv: []option{ + { + code: 1, + data: []byte("foo"), + }, + { + code: 2, + data: []byte("bar"), + }, + }, + options: Options{ + 1: [][]byte{[]byte("foo")}, + 2: [][]byte{[]byte("bar")}, + }, + }, + { + desc: "three key/value pairs, two with same key", + kv: []option{ + { + code: 1, + data: []byte("foo"), + }, + { + code: 1, + data: []byte("baz"), + }, + { + code: 2, + data: []byte("bar"), + }, + }, + options: Options{ + 1: [][]byte{[]byte("foo"), []byte("baz")}, + 2: [][]byte{[]byte("bar")}, + }, + }, + } + + for i, tt := range tests { + o := make(Options) + for _, p := range tt.kv { + o.AddRaw(p.code, p.data) + } + + if want, got := tt.options, o; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected Options map:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestOptionsGet verifies that Options.Get correctly selects the first value +// for a given key, if the value is not empty in an Options map. +func TestOptionsGet(t *testing.T) { + var tests = []struct { + desc string + options Options + key OptionCode + value []byte + err error + }{ + { + desc: "nil Options map", + err: ErrOptionNotPresent, + }, + { + desc: "empty Options map", + options: Options{}, + err: ErrOptionNotPresent, + }, + { + desc: "value not present in Options map", + options: Options{ + 2: [][]byte{[]byte("foo")}, + }, + key: 1, + err: ErrOptionNotPresent, + }, + { + desc: "value present in Options map, but zero length value for key", + options: Options{ + 1: [][]byte{}, + }, + key: 1, + }, + { + desc: "value present in Options map", + options: Options{ + 1: [][]byte{[]byte("foo")}, + }, + key: 1, + value: []byte("foo"), + }, + { + desc: "value present in Options map, with multiple values", + options: Options{ + 1: [][]byte{[]byte("foo"), []byte("bar")}, + }, + key: 1, + err: ErrInvalidPacket, + }, + } + + for i, tt := range tests { + value, err := tt.options.GetOne(tt.key) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected err for Options.GetOne(%v): %v != %v", + i, tt.desc, tt.key, want, got) + continue + } + } + + if want, got := tt.value, value; !bytes.Equal(want, got) { + t.Errorf("[%02d] test %q, unexpected value for Options.GetOne(%v):\n- want: %v\n- got: %v", + i, tt.desc, tt.key, want, got) + } + } +} + +// Test_parseOptions verifies that parseOptions parses correct option values +// from a slice of bytes, and that it returns an empty Options map if the byte +// slice cannot contain options. +func Test_parseOptions(t *testing.T) { + var tests = []struct { + desc string + buf []byte + options Options + err error + }{ + { + desc: "nil options bytes", + options: Options{}, + }, + { + desc: "empty options bytes", + buf: []byte{}, + options: Options{}, + }, + { + desc: "too short options bytes", + buf: []byte{0}, + err: ErrInvalidOptions, + }, + { + desc: "zero code, zero length option bytes", + buf: []byte{0, 0, 0, 0}, + options: Options{}, + }, + { + desc: "zero code, zero length option bytes with trailing byte", + buf: []byte{0, 0, 0, 0, 1}, + err: ErrInvalidOptions, + }, + { + desc: "zero code, length 3, incorrect length for data", + buf: []byte{0, 0, 0, 3, 1, 2}, + err: ErrInvalidOptions, + }, + { + desc: "client ID, length 1, value [1]", + buf: []byte{0, 1, 0, 1, 1}, + options: Options{ + OptionClientID: [][]byte{{1}}, + }, + }, + { + desc: "client ID, length 2, value [1 1] + server ID, length 3, value [1 2 3]", + buf: []byte{ + 0, 1, 0, 2, 1, 1, + 0, 2, 0, 3, 1, 2, 3, + }, + options: Options{ + OptionClientID: [][]byte{{1, 1}}, + OptionServerID: [][]byte{{1, 2, 3}}, + }, + }, + } + + for i, tt := range tests { + var options Options + err := (&options).UnmarshalBinary(tt.buf) + if err != nil { + if want, got := tt.err, err; want != got { + t.Errorf("[%02d] test %q, unexpected error for parseOptions(%v): %v != %v", + i, tt.desc, tt.buf, want, got) + } + continue + } + + if want, got := tt.options, options; !reflect.DeepEqual(want, got) { + t.Errorf("[%02d] test %q, unexpected Options map for parseOptions(%v):\n- want: %v\n- got: %v", + i, tt.desc, tt.buf, want, got) + } + + for k, v := range tt.options { + for ii := range v { + if want, got := cap(v[ii]), cap(options[k][ii]); want != got { + t.Errorf("[%02d] test %q, option %d, unexpected capacity option data:\n- want: %v\n- got: %v", + i, tt.desc, ii, want, got) + } + } + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/packet.go b/vendor/github.com/mdlayher/dhcp6/packet.go new file mode 100644 index 00000000..3f223cee --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/packet.go @@ -0,0 +1,64 @@ +package dhcp6 + +import ( + "github.com/mdlayher/dhcp6/internal/buffer" +) + +// Packet represents a raw DHCPv6 packet, using the format described in RFC 3315, +// Section 6. +// +// The Packet type is typically only needed for low-level operations within the +// client, server, or in tests. +type Packet struct { + // MessageType specifies the DHCP message type constant, such as + // MessageTypeSolicit, MessageTypeAdvertise, etc. + MessageType MessageType + + // TransactionID specifies the DHCP transaction ID. The transaction ID must + // be the same for all message exchanges in one DHCP transaction. + TransactionID [3]byte + + // Options specifies a map of DHCP options. Its methods can be used to + // retrieve data from an incoming packet, or send data with an outgoing + // packet. + Options Options +} + +// MarshalBinary allocates a byte slice containing the data +// from a Packet. +func (p *Packet) MarshalBinary() ([]byte, error) { + // 1 byte: message type + // 3 bytes: transaction ID + // N bytes: options slice byte count + b := buffer.New(nil) + + b.Write8(uint8(p.MessageType)) + b.WriteBytes(p.TransactionID[:]) + + opts, err := p.Options.MarshalBinary() + if err != nil { + return nil, err + } + b.WriteBytes(opts) + return b.Data(), nil +} + +// UnmarshalBinary unmarshals a raw byte slice into a Packet. +// +// If the byte slice does not contain enough data to form a valid Packet, +// ErrInvalidPacket is returned. +func (p *Packet) UnmarshalBinary(q []byte) error { + b := buffer.New(q) + // Packet must contain at least a message type and transaction ID + if b.Len() < 4 { + return ErrInvalidPacket + } + + p.MessageType = MessageType(b.Read8()) + b.ReadBytes(p.TransactionID[:]) + + if err := (&p.Options).UnmarshalBinary(b.Remaining()); err != nil { + return ErrInvalidPacket + } + return nil +} diff --git a/vendor/github.com/mdlayher/dhcp6/packet_test.go b/vendor/github.com/mdlayher/dhcp6/packet_test.go new file mode 100644 index 00000000..3a997de9 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/packet_test.go @@ -0,0 +1,143 @@ +package dhcp6 + +import ( + "bytes" + "reflect" + "testing" +) + +// TestPacketMarshalBinary verifies that Packet.MarshalBinary allocates and returns a correct +// byte slice for a variety of input data. +func TestPacketMarshalBinary(t *testing.T) { + var tests = []struct { + desc string + packet *Packet + buf []byte + }{ + { + desc: "empty packet", + packet: &Packet{}, + buf: []byte{0, 0, 0, 0}, + }, + { + desc: "Solicit only", + packet: &Packet{ + MessageType: MessageTypeSolicit, + }, + buf: []byte{1, 0, 0, 0}, + }, + { + desc: "Solicit, [1 2 3] transaction ID", + packet: &Packet{ + MessageType: MessageTypeSolicit, + TransactionID: [3]byte{1, 2, 3}, + }, + buf: []byte{1, 1, 2, 3}, + }, + { + desc: "Solicit, [2, 3, 4] transaction ID, option client ID [0 1]", + packet: &Packet{ + MessageType: MessageTypeSolicit, + TransactionID: [3]byte{1, 2, 3}, + Options: Options{ + OptionClientID: [][]byte{{0, 1}}, + }, + }, + buf: []byte{1, 1, 2, 3, 0, 1, 0, 2, 0, 1}, + }, + } + + for i, tt := range tests { + buf, err := tt.packet.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if want, got := tt.buf, buf; !bytes.Equal(want, got) { + t.Fatalf("[%02d] test %q, unexpected packet bytes:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} + +// TestPacketUnmarshalBinary verifies that Packet.UnmarshalBinary returns +// appropriate Packets and errors for various input byte slices. +func TestPacketUnmarshalBinary(t *testing.T) { + var tests = []struct { + desc string + buf []byte + packet *Packet + err error + }{ + { + desc: "nil buffer, malformed packet", + err: ErrInvalidPacket, + }, + { + desc: "empty buffer, malformed packet", + buf: []byte{}, + err: ErrInvalidPacket, + }, + { + desc: "length 1 buffer, malformed packet", + buf: []byte{0}, + err: ErrInvalidPacket, + }, + { + desc: "length 3 buffer, malformed packet", + buf: []byte{0, 0, 0}, + err: ErrInvalidPacket, + }, + { + desc: "invalid options in packet", + buf: []byte{0, 0, 0, 0, 0, 1, 0, 1}, + err: ErrInvalidPacket, + }, + { + desc: "length 4 buffer, OK", + buf: []byte{0, 0, 0, 0}, + packet: &Packet{ + MessageType: 0, + TransactionID: [3]byte{0, 0, 0}, + Options: make(Options), + }, + }, + { + desc: "Solicit, [1 2 3] transaction ID, OK", + buf: []byte{1, 1, 2, 3}, + packet: &Packet{ + MessageType: MessageTypeSolicit, + TransactionID: [3]byte{1, 2, 3}, + Options: make(Options), + }, + }, + { + desc: "Solicit, [2 3 4] transaction ID, option client ID [0 1], OK", + buf: []byte{1, 2, 3, 4, 0, 1, 0, 2, 0, 1}, + packet: &Packet{ + MessageType: MessageTypeSolicit, + TransactionID: [3]byte{2, 3, 4}, + Options: Options{ + OptionClientID: [][]byte{{0, 1}}, + }, + }, + }, + } + + for i, tt := range tests { + p := new(Packet) + if err := p.UnmarshalBinary(tt.buf); err != nil { + if want, got := tt.err, err; want != got { + t.Fatalf("[%02d] test %q, unexpected error: %v != %v", + i, tt.desc, want, got) + } + + continue + } + + if want, got := tt.packet, p; !reflect.DeepEqual(want, got) { + t.Fatalf("[%02d] test %q, unexpected packet:\n- want: %v\n- got: %v", + i, tt.desc, want, got) + } + } +} diff --git a/vendor/github.com/mdlayher/dhcp6/string.go b/vendor/github.com/mdlayher/dhcp6/string.go new file mode 100644 index 00000000..59ae31d3 --- /dev/null +++ b/vendor/github.com/mdlayher/dhcp6/string.go @@ -0,0 +1,65 @@ +// Code generated by "stringer -output=string.go -type=MessageType,Status,OptionCode"; DO NOT EDIT. + +package dhcp6 + +import "fmt" + +const _MessageType_name = "MessageTypeSolicitMessageTypeAdvertiseMessageTypeRequestMessageTypeConfirmMessageTypeRenewMessageTypeRebindMessageTypeReplyMessageTypeReleaseMessageTypeDeclineMessageTypeReconfigureMessageTypeInformationRequestMessageTypeRelayForwMessageTypeRelayReplMessageTypeLeasequeryMessageTypeLeasequeryReplyMessageTypeLeasequeryDoneMessageTypeLeasequeryDataMessageTypeReconfigureRequestMessageTypeReconfigureReplyMessageTypeDHCPv4QueryMessageTypeDHCPv4Response" + +var _MessageType_index = [...]uint16{0, 18, 38, 56, 74, 90, 107, 123, 141, 159, 181, 210, 230, 250, 271, 297, 322, 347, 376, 403, 425, 450} + +func (i MessageType) String() string { + i -= 1 + if i >= MessageType(len(_MessageType_index)-1) { + return fmt.Sprintf("MessageType(%d)", i+1) + } + return _MessageType_name[_MessageType_index[i]:_MessageType_index[i+1]] +} + +const _Status_name = "StatusSuccessStatusUnspecFailStatusNoAddrsAvailStatusNoBindingStatusNotOnLinkStatusUseMulticastStatusNoPrefixAvailStatusUnknownQueryTypeStatusMalformedQueryStatusNotConfiguredStatusNotAllowedStatusQueryTerminated" + +var _Status_index = [...]uint8{0, 13, 29, 47, 62, 77, 95, 114, 136, 156, 175, 191, 212} + +func (i Status) String() string { + if i >= Status(len(_Status_index)-1) { + return fmt.Sprintf("Status(%d)", i) + } + return _Status_name[_Status_index[i]:_Status_index[i+1]] +} + +const ( + _OptionCode_name_0 = "OptionClientIDOptionServerIDOptionIANAOptionIATAOptionIAAddrOptionOROOptionPreferenceOptionElapsedTimeOptionRelayMsg" + _OptionCode_name_1 = "OptionAuthOptionUnicastOptionStatusCodeOptionRapidCommitOptionUserClassOptionVendorClassOptionVendorOptsOptionInterfaceIDOptionReconfMsgOptionReconfAccept" + _OptionCode_name_2 = "OptionIAPDOptionIAPrefix" + _OptionCode_name_3 = "OptionRemoteIdentifier" + _OptionCode_name_4 = "OptionBootFileURLOptionBootFileParamOptionClientArchTypeOptionNII" +) + +var ( + _OptionCode_index_0 = [...]uint8{0, 14, 28, 38, 48, 60, 69, 85, 102, 116} + _OptionCode_index_1 = [...]uint8{0, 10, 23, 39, 56, 71, 88, 104, 121, 136, 154} + _OptionCode_index_2 = [...]uint8{0, 10, 24} + _OptionCode_index_3 = [...]uint8{0, 22} + _OptionCode_index_4 = [...]uint8{0, 17, 36, 56, 65} +) + +func (i OptionCode) String() string { + switch { + case 1 <= i && i <= 9: + i -= 1 + return _OptionCode_name_0[_OptionCode_index_0[i]:_OptionCode_index_0[i+1]] + case 11 <= i && i <= 20: + i -= 11 + return _OptionCode_name_1[_OptionCode_index_1[i]:_OptionCode_index_1[i+1]] + case 25 <= i && i <= 26: + i -= 25 + return _OptionCode_name_2[_OptionCode_index_2[i]:_OptionCode_index_2[i+1]] + case i == 37: + return _OptionCode_name_3 + case 59 <= i && i <= 62: + i -= 59 + return _OptionCode_name_4[_OptionCode_index_4[i]:_OptionCode_index_4[i+1]] + default: + return fmt.Sprintf("OptionCode(%d)", i) + } +} diff --git a/vendor/github.com/mgutz/ansi/.gitignore b/vendor/github.com/mgutz/ansi/.gitignore new file mode 100644 index 00000000..9ed3b07c --- /dev/null +++ b/vendor/github.com/mgutz/ansi/.gitignore @@ -0,0 +1 @@ +*.test diff --git a/vendor/github.com/mgutz/ansi/LICENSE b/vendor/github.com/mgutz/ansi/LICENSE new file mode 100644 index 00000000..06ce0c3b --- /dev/null +++ b/vendor/github.com/mgutz/ansi/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) +Copyright (c) 2013 Mario L. Gutierrez + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/github.com/mgutz/ansi/README.md b/vendor/github.com/mgutz/ansi/README.md new file mode 100644 index 00000000..8f8e20b7 --- /dev/null +++ b/vendor/github.com/mgutz/ansi/README.md @@ -0,0 +1,121 @@ +# ansi + +Package ansi is a small, fast library to create ANSI colored strings and codes. + +## Install + +Get it + +```sh +go get -u github.com/mgutz/ansi +``` + +## Example + +```go +import "github.com/mgutz/ansi" + +// colorize a string, SLOW +msg := ansi.Color("foo", "red+b:white") + +// create a FAST closure function to avoid computation of ANSI code +phosphorize := ansi.ColorFunc("green+h:black") +msg = phosphorize("Bring back the 80s!") +msg2 := phospohorize("Look, I'm a CRT!") + +// cache escape codes and build strings manually +lime := ansi.ColorCode("green+h:black") +reset := ansi.ColorCode("reset") + +fmt.Println(lime, "Bring back the 80s!", reset) +``` + +Other examples + +```go +Color(s, "red") // red +Color(s, "red+b") // red bold +Color(s, "red+B") // red blinking +Color(s, "red+u") // red underline +Color(s, "red+bh") // red bold bright +Color(s, "red:white") // red on white +Color(s, "red+b:white+h") // red bold on white bright +Color(s, "red+B:white+h") // red blink on white bright +Color(s, "off") // turn off ansi codes +``` + +To view color combinations, from project directory in terminal. + +```sh +go test +``` + +## Style format + +```go +"foregroundColor+attributes:backgroundColor+attributes" +``` + +Colors + +* black +* red +* green +* yellow +* blue +* magenta +* cyan +* white +* 0...255 (256 colors) + +Foreground Attributes + +* B = Blink +* b = bold +* h = high intensity (bright) +* i = inverse +* s = strikethrough +* u = underline + +Background Attributes + +* h = high intensity (bright) + +## Constants + +* ansi.Reset +* ansi.DefaultBG +* ansi.DefaultFG +* ansi.Black +* ansi.Red +* ansi.Green +* ansi.Yellow +* ansi.Blue +* ansi.Magenta +* ansi.Cyan +* ansi.White +* ansi.LightBlack +* ansi.LightRed +* ansi.LightGreen +* ansi.LightYellow +* ansi.LightBlue +* ansi.LightMagenta +* ansi.LightCyan +* ansi.LightWhite + +## References + +Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) + +General [tips and formatting](http://misc.flogisoft.com/bash/tip_colors_and_formatting) + +What about support on Windows? Use [colorable by mattn](https://github.com/mattn/go-colorable). +Ansi and colorable are used by [logxi](https://github.com/mgutz/logxi) to support logging in +color on Windows. + +## MIT License + +Copyright (c) 2013 Mario Gutierrez mario@mgutz.com + +See the file LICENSE for copying permission. + diff --git a/vendor/github.com/mgutz/ansi/ansi.go b/vendor/github.com/mgutz/ansi/ansi.go new file mode 100644 index 00000000..dc041364 --- /dev/null +++ b/vendor/github.com/mgutz/ansi/ansi.go @@ -0,0 +1,285 @@ +package ansi + +import ( + "bytes" + "fmt" + "strconv" + "strings" +) + +const ( + black = iota + red + green + yellow + blue + magenta + cyan + white + defaultt = 9 + + normalIntensityFG = 30 + highIntensityFG = 90 + normalIntensityBG = 40 + highIntensityBG = 100 + + start = "\033[" + bold = "1;" + blink = "5;" + underline = "4;" + inverse = "7;" + strikethrough = "9;" + + // Reset is the ANSI reset escape sequence + Reset = "\033[0m" + // DefaultBG is the default background + DefaultBG = "\033[49m" + // DefaultFG is the default foreground + DefaultFG = "\033[39m" +) + +// Black FG +var Black string + +// Red FG +var Red string + +// Green FG +var Green string + +// Yellow FG +var Yellow string + +// Blue FG +var Blue string + +// Magenta FG +var Magenta string + +// Cyan FG +var Cyan string + +// White FG +var White string + +// LightBlack FG +var LightBlack string + +// LightRed FG +var LightRed string + +// LightGreen FG +var LightGreen string + +// LightYellow FG +var LightYellow string + +// LightBlue FG +var LightBlue string + +// LightMagenta FG +var LightMagenta string + +// LightCyan FG +var LightCyan string + +// LightWhite FG +var LightWhite string + +var ( + plain = false + // Colors maps common color names to their ANSI color code. + Colors = map[string]int{ + "black": black, + "red": red, + "green": green, + "yellow": yellow, + "blue": blue, + "magenta": magenta, + "cyan": cyan, + "white": white, + "default": defaultt, + } +) + +func init() { + for i := 0; i < 256; i++ { + Colors[strconv.Itoa(i)] = i + } + + Black = ColorCode("black") + Red = ColorCode("red") + Green = ColorCode("green") + Yellow = ColorCode("yellow") + Blue = ColorCode("blue") + Magenta = ColorCode("magenta") + Cyan = ColorCode("cyan") + White = ColorCode("white") + LightBlack = ColorCode("black+h") + LightRed = ColorCode("red+h") + LightGreen = ColorCode("green+h") + LightYellow = ColorCode("yellow+h") + LightBlue = ColorCode("blue+h") + LightMagenta = ColorCode("magenta+h") + LightCyan = ColorCode("cyan+h") + LightWhite = ColorCode("white+h") +} + +// ColorCode returns the ANSI color color code for style. +func ColorCode(style string) string { + return colorCode(style).String() +} + +// Gets the ANSI color code for a style. +func colorCode(style string) *bytes.Buffer { + buf := bytes.NewBufferString("") + if plain || style == "" { + return buf + } + if style == "reset" { + buf.WriteString(Reset) + return buf + } else if style == "off" { + return buf + } + + foregroundBackground := strings.Split(style, ":") + foreground := strings.Split(foregroundBackground[0], "+") + fgKey := foreground[0] + fg := Colors[fgKey] + fgStyle := "" + if len(foreground) > 1 { + fgStyle = foreground[1] + } + + bg, bgStyle := "", "" + + if len(foregroundBackground) > 1 { + background := strings.Split(foregroundBackground[1], "+") + bg = background[0] + if len(background) > 1 { + bgStyle = background[1] + } + } + + buf.WriteString(start) + base := normalIntensityFG + if len(fgStyle) > 0 { + if strings.Contains(fgStyle, "b") { + buf.WriteString(bold) + } + if strings.Contains(fgStyle, "B") { + buf.WriteString(blink) + } + if strings.Contains(fgStyle, "u") { + buf.WriteString(underline) + } + if strings.Contains(fgStyle, "i") { + buf.WriteString(inverse) + } + if strings.Contains(fgStyle, "s") { + buf.WriteString(strikethrough) + } + if strings.Contains(fgStyle, "h") { + base = highIntensityFG + } + } + + // if 256-color + n, err := strconv.Atoi(fgKey) + if err == nil { + fmt.Fprintf(buf, "38;5;%d;", n) + } else { + fmt.Fprintf(buf, "%d;", base+fg) + } + + base = normalIntensityBG + if len(bg) > 0 { + if strings.Contains(bgStyle, "h") { + base = highIntensityBG + } + // if 256-color + n, err := strconv.Atoi(bg) + if err == nil { + fmt.Fprintf(buf, "48;5;%d;", n) + } else { + fmt.Fprintf(buf, "%d;", base+Colors[bg]) + } + } + + // remove last ";" + buf.Truncate(buf.Len() - 1) + buf.WriteRune('m') + return buf +} + +// Color colors a string based on the ANSI color code for style. +func Color(s, style string) string { + if plain || len(style) < 1 { + return s + } + buf := colorCode(style) + buf.WriteString(s) + buf.WriteString(Reset) + return buf.String() +} + +// ColorFunc creates a closure to avoid computation ANSI color code. +func ColorFunc(style string) func(string) string { + if style == "" { + return func(s string) string { + return s + } + } + color := ColorCode(style) + return func(s string) string { + if plain || s == "" { + return s + } + buf := bytes.NewBufferString(color) + buf.WriteString(s) + buf.WriteString(Reset) + result := buf.String() + return result + } +} + +// DisableColors disables ANSI color codes. The default is false (colors are on). +func DisableColors(disable bool) { + plain = disable + if plain { + Black = "" + Red = "" + Green = "" + Yellow = "" + Blue = "" + Magenta = "" + Cyan = "" + White = "" + LightBlack = "" + LightRed = "" + LightGreen = "" + LightYellow = "" + LightBlue = "" + LightMagenta = "" + LightCyan = "" + LightWhite = "" + } else { + Black = ColorCode("black") + Red = ColorCode("red") + Green = ColorCode("green") + Yellow = ColorCode("yellow") + Blue = ColorCode("blue") + Magenta = ColorCode("magenta") + Cyan = ColorCode("cyan") + White = ColorCode("white") + LightBlack = ColorCode("black+h") + LightRed = ColorCode("red+h") + LightGreen = ColorCode("green+h") + LightYellow = ColorCode("yellow+h") + LightBlue = ColorCode("blue+h") + LightMagenta = ColorCode("magenta+h") + LightCyan = ColorCode("cyan+h") + LightWhite = ColorCode("white+h") + } +} diff --git a/vendor/github.com/mgutz/ansi/ansi_test.go b/vendor/github.com/mgutz/ansi/ansi_test.go new file mode 100644 index 00000000..1630dc57 --- /dev/null +++ b/vendor/github.com/mgutz/ansi/ansi_test.go @@ -0,0 +1,52 @@ +package ansi + +import ( + "strings" + "testing" +) + +func TestPlain(t *testing.T) { + DisableColors(true) + PrintStyles() +} + +func TestStyles(t *testing.T) { + DisableColors(false) + PrintStyles() +} + +func TestDisableColors(t *testing.T) { + fn := ColorFunc("red") + + buf := colorCode("off") + if buf.String() != "" { + t.Fail() + } + + DisableColors(true) + if Black != "" { + t.Fail() + } + code := ColorCode("red") + if code != "" { + t.Fail() + } + s := fn("foo") + if s != "foo" { + t.Fail() + } + + DisableColors(false) + if Black == "" { + t.Fail() + } + code = ColorCode("red") + if code == "" { + t.Fail() + } + // will have escape codes around it + index := strings.Index(fn("foo"), "foo") + if index <= 0 { + t.Fail() + } +} diff --git a/vendor/github.com/mgutz/ansi/cmd/ansi-mgutz/main.go b/vendor/github.com/mgutz/ansi/cmd/ansi-mgutz/main.go new file mode 100644 index 00000000..736b45dd --- /dev/null +++ b/vendor/github.com/mgutz/ansi/cmd/ansi-mgutz/main.go @@ -0,0 +1,135 @@ +package main + +import ( + "fmt" + "sort" + "strconv" + + "github.com/mattn/go-colorable" + "github.com/mgutz/ansi" +) + +func main() { + printColors() + print256Colors() + printConstants() +} + +func pad(s string, length int) string { + for len(s) < length { + s += " " + } + return s +} + +func padColor(s string, styles []string) string { + buffer := "" + for _, style := range styles { + buffer += ansi.Color(pad(s+style, 20), s+style) + } + return buffer +} + +func printPlain() { + ansi.DisableColors(true) + bgColors := []string{ + "", + ":black", + ":red", + ":green", + ":yellow", + ":blue", + ":magenta", + ":cyan", + ":white", + } + for fg := range ansi.Colors { + for _, bg := range bgColors { + println(padColor(fg, []string{"" + bg, "+b" + bg, "+bh" + bg, "+u" + bg})) + println(padColor(fg, []string{"+uh" + bg, "+B" + bg, "+Bb" + bg /* backgrounds */, "" + bg + "+h"})) + println(padColor(fg, []string{"+b" + bg + "+h", "+bh" + bg + "+h", "+u" + bg + "+h", "+uh" + bg + "+h"})) + } + } +} + +func printColors() { + ansi.DisableColors(false) + stdout := colorable.NewColorableStdout() + + bgColors := []string{ + "", + ":black", + ":red", + ":green", + ":yellow", + ":blue", + ":magenta", + ":cyan", + ":white", + } + + keys := []string{} + for fg := range ansi.Colors { + _, err := strconv.Atoi(fg) + if err != nil { + keys = append(keys, fg) + } + } + sort.Strings(keys) + + for _, fg := range keys { + for _, bg := range bgColors { + fmt.Fprintln(stdout, padColor(fg, []string{"" + bg, "+b" + bg, "+bh" + bg, "+u" + bg})) + fmt.Fprintln(stdout, padColor(fg, []string{"+uh" + bg, "+B" + bg, "+Bb" + bg /* backgrounds */, "" + bg + "+h", "+s" + bg})) + fmt.Fprintln(stdout, padColor(fg, []string{"+b" + bg + "+h", "+bh" + bg + "+h", "+u" + bg + "+h", "+uh" + bg + "+h"})) + } + } +} + +func print256Colors() { + ansi.DisableColors(false) + stdout := colorable.NewColorableStdout() + + bgColors := []string{""} + for i := 0; i < 256; i++ { + key := fmt.Sprintf(":%d", i) + bgColors = append(bgColors, key) + } + + keys := []string{} + for fg := range ansi.Colors { + n, err := strconv.Atoi(fg) + if err == nil { + keys = append(keys, fmt.Sprintf("%3d", n)) + } + } + sort.Strings(keys) + + for _, fg := range keys { + for _, bg := range bgColors { + fmt.Fprintln(stdout, padColor(fg, []string{"" + bg, "+b" + bg, "+u" + bg})) + fmt.Fprintln(stdout, padColor(fg, []string{"+B" + bg, "+Bb" + bg, "+s" + bg})) + } + } +} + +func printConstants() { + stdout := colorable.NewColorableStdout() + fmt.Fprintln(stdout, ansi.DefaultFG, "ansi.DefaultFG", ansi.Reset) + fmt.Fprintln(stdout, ansi.Black, "ansi.Black", ansi.Reset) + fmt.Fprintln(stdout, ansi.Red, "ansi.Red", ansi.Reset) + fmt.Fprintln(stdout, ansi.Green, "ansi.Green", ansi.Reset) + fmt.Fprintln(stdout, ansi.Yellow, "ansi.Yellow", ansi.Reset) + fmt.Fprintln(stdout, ansi.Blue, "ansi.Blue", ansi.Reset) + fmt.Fprintln(stdout, ansi.Magenta, "ansi.Magenta", ansi.Reset) + fmt.Fprintln(stdout, ansi.Cyan, "ansi.Cyan", ansi.Reset) + fmt.Fprintln(stdout, ansi.White, "ansi.White", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightBlack, "ansi.LightBlack", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightRed, "ansi.LightRed", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightGreen, "ansi.LightGreen", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightYellow, "ansi.LightYellow", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightBlue, "ansi.LightBlue", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightMagenta, "ansi.LightMagenta", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightCyan, "ansi.LightCyan", ansi.Reset) + fmt.Fprintln(stdout, ansi.LightWhite, "ansi.LightWhite", ansi.Reset) +} diff --git a/vendor/github.com/mgutz/ansi/doc.go b/vendor/github.com/mgutz/ansi/doc.go new file mode 100644 index 00000000..43c217e1 --- /dev/null +++ b/vendor/github.com/mgutz/ansi/doc.go @@ -0,0 +1,65 @@ +/* +Package ansi is a small, fast library to create ANSI colored strings and codes. + +Installation + + # this installs the color viewer and the package + go get -u github.com/mgutz/ansi/cmd/ansi-mgutz + +Example + + // colorize a string, SLOW + msg := ansi.Color("foo", "red+b:white") + + // create a closure to avoid recalculating ANSI code compilation + phosphorize := ansi.ColorFunc("green+h:black") + msg = phosphorize("Bring back the 80s!") + msg2 := phospohorize("Look, I'm a CRT!") + + // cache escape codes and build strings manually + lime := ansi.ColorCode("green+h:black") + reset := ansi.ColorCode("reset") + + fmt.Println(lime, "Bring back the 80s!", reset) + +Other examples + + Color(s, "red") // red + Color(s, "red+b") // red bold + Color(s, "red+B") // red blinking + Color(s, "red+u") // red underline + Color(s, "red+bh") // red bold bright + Color(s, "red:white") // red on white + Color(s, "red+b:white+h") // red bold on white bright + Color(s, "red+B:white+h") // red blink on white bright + +To view color combinations, from terminal + + ansi-mgutz + +Style format + + "foregroundColor+attributes:backgroundColor+attributes" + +Colors + + black + red + green + yellow + blue + magenta + cyan + white + +Attributes + + b = bold foreground + B = Blink foreground + u = underline foreground + h = high intensity (bright) foreground, background + i = inverse + +Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) +*/ +package ansi diff --git a/vendor/github.com/mgutz/ansi/print.go b/vendor/github.com/mgutz/ansi/print.go new file mode 100644 index 00000000..806f436b --- /dev/null +++ b/vendor/github.com/mgutz/ansi/print.go @@ -0,0 +1,57 @@ +package ansi + +import ( + "fmt" + "sort" + + colorable "github.com/mattn/go-colorable" +) + +// PrintStyles prints all style combinations to the terminal. +func PrintStyles() { + // for compatibility with Windows, not needed for *nix + stdout := colorable.NewColorableStdout() + + bgColors := []string{ + "", + ":black", + ":red", + ":green", + ":yellow", + ":blue", + ":magenta", + ":cyan", + ":white", + } + + keys := make([]string, 0, len(Colors)) + for k := range Colors { + keys = append(keys, k) + } + + sort.Sort(sort.StringSlice(keys)) + + for _, fg := range keys { + for _, bg := range bgColors { + fmt.Fprintln(stdout, padColor(fg, []string{"" + bg, "+b" + bg, "+bh" + bg, "+u" + bg})) + fmt.Fprintln(stdout, padColor(fg, []string{"+s" + bg, "+i" + bg})) + fmt.Fprintln(stdout, padColor(fg, []string{"+uh" + bg, "+B" + bg, "+Bb" + bg /* backgrounds */, "" + bg + "+h"})) + fmt.Fprintln(stdout, padColor(fg, []string{"+b" + bg + "+h", "+bh" + bg + "+h", "+u" + bg + "+h", "+uh" + bg + "+h"})) + } + } +} + +func pad(s string, length int) string { + for len(s) < length { + s += " " + } + return s +} + +func padColor(color string, styles []string) string { + buffer := "" + for _, style := range styles { + buffer += Color(pad(color+style, 20), color+style) + } + return buffer +} diff --git a/vendor/github.com/mgutz/logxi/.gitignore b/vendor/github.com/mgutz/logxi/.gitignore new file mode 100644 index 00000000..4d0ae5ba --- /dev/null +++ b/vendor/github.com/mgutz/logxi/.gitignore @@ -0,0 +1,4 @@ +godobin* +v1/cmd/demo/demo +v1/cmd/filter/filter +*.exe diff --git a/vendor/github.com/mgutz/logxi/Gododir/main.go b/vendor/github.com/mgutz/logxi/Gododir/main.go new file mode 100644 index 00000000..cf1d7341 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/Gododir/main.go @@ -0,0 +1,236 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" + "time" + + "github.com/mattn/go-colorable" + "github.com/mgutz/ansi" + do "gopkg.in/godo.v2" +) + +type pair struct { + description string + command string +} + +var stdout io.Writer + +var promptColor = ansi.ColorCode("cyan+h") +var commentColor = ansi.ColorCode("yellow+h") +var titleColor = ansi.ColorCode("green+h") +var subtitleColor = ansi.ColorCode("black+h") +var normal = ansi.DefaultFG +var wd string + +func init() { + wd, _ = os.Getwd() + stdout = colorable.NewColorableStdout() +} + +func clear() { + do.Bash("clear") + // leave a single line at top so the window + // overlay doesn't have to be exact + fmt.Fprintln(stdout, "") +} + +func pseudoType(s string, color string) { + if color != "" { + fmt.Fprint(stdout, color) + } + for _, r := range s { + fmt.Fprint(stdout, string(r)) + time.Sleep(50 * time.Millisecond) + } + if color != "" { + fmt.Fprint(stdout, ansi.Reset) + } +} + +func pseudoTypeln(s string, color string) { + pseudoType(s, color) + fmt.Fprint(stdout, "\n") +} + +func pseudoPrompt(prompt, s string) { + pseudoType(prompt, promptColor) + //fmt.Fprint(stdout, promptFn(prompt)) + pseudoType(s, normal) +} + +func intro(title, subtitle string, delay time.Duration) { + clear() + pseudoType("\n\n\t"+title+"\n\n", titleColor) + pseudoType("\t"+subtitle, subtitleColor) + time.Sleep(delay) +} + +func typeCommand(description, commandStr string) { + clear() + pseudoTypeln("# "+description, commentColor) + pseudoType("> ", promptColor) + pseudoType(commandStr, normal) + time.Sleep(200 * time.Millisecond) + fmt.Fprintln(stdout, "") +} + +var version = "v1" + +func relv(p string) string { + return filepath.Join(version, p) +} +func absv(p string) string { + return filepath.Join(wd, version, p) +} + +func tasks(p *do.Project) { + p.Task("bench", nil, func(c *do.Context) { + c.Run("LOGXI=* go test -bench . -benchmem", do.M{"$in": "v1/bench"}) + }) + + p.Task("build", nil, func(c *do.Context) { + c.Run("go build", do.M{"$in": "v1/cmd/demo"}) + }) + + p.Task("linux-build", nil, func(c *do.Context) { + c.Bash(` + set -e + GOOS=linux GOARCH=amd64 go build + scp -F ~/projects/provision/matcherino/ssh.vagrant.config demo devmaster1:~/. + `, do.M{"$in": "v1/cmd/demo"}) + }) + + p.Task("etcd-set", nil, func(c *do.Context) { + kv := c.Args.NonFlags() + if len(kv) != 2 { + do.Halt(fmt.Errorf("godo etcd-set -- KEY VALUE")) + } + + c.Run( + `curl -L http://127.0.0.1:4001/v2/keys/{{.key}} -XPUT -d value="{{.value}}"`, + do.M{"key": kv[0], "value": kv[1]}, + ) + }) + + p.Task("etcd-del", nil, func(c *do.Context) { + kv := c.Args.Leftover() + if len(kv) != 1 { + do.Halt(fmt.Errorf("godo etcd-del -- KEY")) + } + c.Run( + `curl -L http://127.0.0.1:4001/v2/keys/{{.key}} -XDELETE`, + do.M{"key": kv[0]}, + ) + }) + + p.Task("demo", nil, func(c *do.Context) { + c.Run("go run main.go", do.M{"$in": "v1/cmd/demo"}) + }) + + p.Task("demo2", nil, func(c *do.Context) { + c.Run("go run main.go", do.M{"$in": "v1/cmd/demo2"}) + }) + + p.Task("filter", do.S{"build"}, func(c *do.Context) { + c.Run("go build", do.M{"$in": "v1/cmd/filter"}) + c.Bash("LOGXI=* ../demo/demo | ./filter", do.M{"$in": "v1/cmd/filter"}) + }) + + p.Task("gifcast", do.S{"build"}, func(*do.Context) { + commands := []pair{ + { + `create a simple app demo`, + `cat main.ansi`, + }, + { + `running demo displays only warnings and errors with context`, + `demo`, + }, + { + `show all log levels`, + `LOGXI=* demo`, + }, + { + `enable/disable loggers with level`, + `LOGXI=*=ERR,models demo`, + }, + { + `create custom 256 colors colorscheme, pink==200`, + `LOGXI_COLORS=*=black+h,ERR=200+b,key=blue+h demo`, + }, + { + `put keys on newline, set time format, less context`, + `LOGXI=* LOGXI_FORMAT=pretty,maxcol=80,t=04:05.000,context=0 demo`, + }, + { + `logxi defaults to fast, unadorned JSON in production`, + `demo | cat`, + }, + } + + // setup time for ecorder, user presses enter when ready + clear() + do.Prompt("") + + intro( + "log XI", + "structured. faster. friendlier.\n\n\n\n\t::mgutz", + 1*time.Second, + ) + + for _, cmd := range commands { + typeCommand(cmd.description, cmd.command) + do.Bash(cmd.command, do.M{"$in": "v1/cmd/demo"}) + time.Sleep(3500 * time.Millisecond) + } + + clear() + do.Prompt("") + }) + + p.Task("demo-gif", nil, func(c *do.Context) { + c.Bash(`cp ~/Desktop/demo.gif images`) + }) + + p.Task("bench-allocs", nil, func(c *do.Context) { + c.Bash(`go test -bench . -benchmem -run=none | grep "allocs\|^Bench"`, do.M{"$in": "v1/bench"}) + }).Description("Runs benchmarks with allocs") + + p.Task("benchjson", nil, func(c *do.Context) { + c.Bash("go test -bench=BenchmarkLoggerJSON -benchmem", do.M{"$in": "v1/bench"}) + }) + + p.Task("test", nil, func(c *do.Context) { + c.Run("LOGXI=* go test", do.M{"$in": "v1"}) + //Run("LOGXI=* go test -run=TestColors", M{"$in": "v1"}) + }) + + p.Task("isolate", do.S{"build"}, func(c *do.Context) { + c.Bash("LOGXI=* LOGXI_FORMAT=fit,maxcol=80,t=04:05.000,context=2 demo", do.M{"$in": "v1/cmd/demo"}) + }) + + p.Task("install", nil, func(c *do.Context) { + packages := []string{ + "github.com/mattn/go-colorable", + "github.com/mattn/go-isatty", + "github.com/mgutz/ansi", + "github.com/stretchr/testify/assert", + + // needed for benchmarks in bench/ + "github.com/Sirupsen/logrus", + "gopkg.in/inconshreveable/log15.v2", + } + for _, pkg := range packages { + c.Run("go get -u " + pkg) + } + }).Description("Installs dependencies") + +} + +func main() { + do.Godo(tasks) +} diff --git a/vendor/github.com/mgutz/logxi/LICENSE b/vendor/github.com/mgutz/logxi/LICENSE new file mode 100644 index 00000000..7e601d4a --- /dev/null +++ b/vendor/github.com/mgutz/logxi/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2016 Mario Gutierrez + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mgutz/logxi/README.md b/vendor/github.com/mgutz/logxi/README.md new file mode 100644 index 00000000..c9981682 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/README.md @@ -0,0 +1,297 @@ + +![demo](https://github.com/mgutz/logxi/raw/master/images/demo.gif) + +# logxi + +log XI is a structured [12-factor app](http://12factor.net/logs) +logger built for speed and happy development. + +* Simpler. Sane no-configuration defaults out of the box. +* Faster. See benchmarks vs logrus and log15. +* Structured. Key-value pairs are enforced. Logs JSON in production. +* Configurable. Enable/disalbe Loggers and levels via env vars. +* Friendlier. Happy, colorful and developer friendly logger in terminal. +* Helpul. Traces, warnings and errors are emphasized with file, line + number and callstack. +* Efficient. Has level guards to avoid cost of building complex arguments. + + +### Requirements + + Go 1.3+ + +### Installation + + go get -u github.com/mgutz/logxi/v1 + +### Getting Started + +```go +import "github.com/mgutz/logxi/v1" + +// create package variable for Logger interface +var logger log.Logger + +func main() { + // use default logger + who := "mario" + log.Info("Hello", "who", who) + + // create a logger with a unique identifier which + // can be enabled from environment variables + logger = log.New("pkg") + + // specify a writer, use NewConcurrentWriter if it is not concurrent + // safe + modelLogger = log.NewLogger(log.NewConcurrentWriter(os.Stdout), "models") + + db, err := sql.Open("postgres", "dbname=testdb") + if err != nil { + modelLogger.Error("Could not open database", "err", err) + } + + fruit := "apple" + languages := []string{"go", "javascript"} + if log.IsDebug() { + // use key-value pairs after message + logger.Debug("OK", "fruit", fruit, "languages", languages) + } +} +``` + +logxi defaults to showing warnings and above. To view all logs + + LOGXI=* go run main.go + +## Highlights + +This logger package + +* Is fast in production environment + + A logger should be efficient and minimize performance tax. + logxi encodes JSON 2X faster than logrus and log15 with primitive types. + When diagnosing a problem in production, troubleshooting often means + enabling small trace data in `Debug` and `Info` statements for some + period of time. + + # primitive types + BenchmarkLogxi 100000 20021 ns/op 2477 B/op 66 allocs/op + BenchmarkLogrus 30000 46372 ns/op 8991 B/op 196 allocs/op + BenchmarkLog15 20000 62974 ns/op 9244 B/op 236 allocs/op + + # nested object + BenchmarkLogxiComplex 30000 44448 ns/op 6416 B/op 190 allocs/op + BenchmarkLogrusComplex 20000 65006 ns/op 12231 B/op 278 allocs/op + BenchmarkLog15Complex 20000 92880 ns/op 13172 B/op 311 allocs/op + +* Is developer friendly in the terminal. The HappyDevFormatter + is colorful, prints file and line numbers for traces, warnings + and errors. Arguments are printed in the order they are coded. + Errors print the call stack. + + `HappyDevFormatter` is not too concerned with performance + and delegates to JSONFormatter internally. + +* Logs machine parsable output in production environments. + The default formatter for non terminals is `JSONFormatter`. + + `TextFormatter` may also be used which is MUCH faster than + JSON but there is no guarantee it can be easily parsed. + +* Has level guards to avoid the cost of building arguments. Get in the + habit of using guards. + + if log.IsDebug() { + log.Debug("some ", "key1", expensive()) + } + +* Conforms to a logging interface so it can be replaced. + + type Logger interface { + Trace(msg string, args ...interface{}) + Debug(msg string, args ...interface{}) + Info(msg string, args ...interface{}) + Warn(msg string, args ...interface{}) error + Error(msg string, args ...interface{}) error + Fatal(msg string, args ...interface{}) + Log(level int, msg string, args []interface{}) + + SetLevel(int) + IsTrace() bool + IsDebug() bool + IsInfo() bool + IsWarn() bool + // Error, Fatal not needed, those SHOULD always be logged + } + +* Standardizes on key-value pair argument sequence + + ```go +log.Debug("inside Fn()", "key1", value1, "key2", value2) + +// instead of this +log.WithFields(logrus.Fields{"m": "pkg", "key1": value1, "key2": value2}).Debug("inside fn()") +``` + logxi logs `FIX_IMBALANCED_PAIRS =>` if key-value pairs are imbalanced + + `log.Warn and log.Error` are special cases and return error: + + ```go +return log.Error(msg) //=> fmt.Errorf(msg) +return log.Error(msg, "err", err) //=> err +``` + +* Supports Color Schemes (256 colors) + + `log.New` creates a logger that supports color schemes + + logger := log.New("mylog") + + To customize scheme + + # emphasize errors with white text on red background + LOGXI_COLORS="ERR=white:red" yourapp + + # emphasize errors with pink = 200 on 256 colors table + LOGXI_COLORS="ERR=200" yourapp + +* Is suppressable in unit tests + + ```go +func TestErrNotFound() { + log.Suppress(true) + defer log.Suppress(false) + ... +} +``` + + + +## Configuration + +### Enabling/Disabling Loggers + +By default logxi logs entries whose level is `LevelWarn` or above when +using a terminal. For non-terminals, entries with level `LevelError` and +above are logged. + +To quickly see all entries use short form + + # enable all, disable log named foo + LOGXI=*,-foo yourapp + +To better control logs in production, use long form which allows +for granular control of levels + + # the above statement is equivalent to this + LOGXI=*=DBG,foo=OFF yourapp + +`DBG` should obviously not be used in production unless for +troubleshooting. See `LevelAtoi` in `logger.go` for values. +For example, there is a problem in the data access layer +in production. + + # Set all to Error and set data related packages to Debug + LOGXI=*=ERR,models=DBG,dat*=DBG,api=DBG yourapp + +### Format + +The format may be set via `LOGXI_FORMAT` environment +variable. Valid values are `"happy", "text", "JSON", "LTSV"` + + # Use JSON in production with custom time + LOGXI_FORMAT=JSON,t=2006-01-02T15:04:05.000000-0700 yourapp + +The "happy" formatter has more options + +* pretty - puts each key-value pair indented on its own line + + "happy" default to fitting key-value pair onto the same line. If + result characters are longer than `maxcol` then the pair will be + put on the next line and indented + +* maxcol - maximum number of columns before forcing a key to be on its + own line. If you want everything on a single line, set this to high + value like 1000. Default is 80. + +* context - the number of context lines to print on source. Set to -1 + to see only file:lineno. Default is 2. + + +### Color Schemes + +The color scheme may be set with `LOGXI_COLORS` environment variable. For +example, the default dark scheme is emulated like this + + # on non-Windows, see Windows support below + export LOGXI_COLORS=key=cyan+h,value,misc=blue+h,source=magenta,TRC,DBG,WRN=yellow,INF=green,ERR=red+h + yourapp + + # color only errors + LOGXI_COLORS=ERR=red yourapp + +See [ansi](http://github.com/mgutz/ansi) package for styling. An empty +value, like "value" and "DBG" above means use default foreground and +background on terminal. + +Keys + +* \* - default color +* TRC - trace color +* DBG - debug color +* WRN - warn color +* INF - info color +* ERR - error color +* message - message color +* key - key color +* value - value color unless WRN or ERR +* misc - time and log name color +* source - source context color (excluding error line) + +#### Windows + +Use [ConEmu-Maximus5](https://github.com/Maximus5/ConEmu). +Read this page about [256 colors](https://code.google.com/p/conemu-maximus5/wiki/Xterm256Colors). + +Colors in PowerShell and Command Prompt _work_ but not very pretty. + +## Extending + +What about hooks? There are least two ways to do this + +* Implement your own `io.Writer` to write to external services. Be sure to set + the formatter to JSON to faciliate decoding with Go's built-in streaming + decoder. +* Create an external filter. See `v1/cmd/filter` as an example. + +What about log rotation? 12 factor apps only concern themselves with +STDOUT. Use shell redirection operators to write to a file. + +There are many utilities to rotate logs which accept STDIN as input. They can +do many things like send alerts, etc. The two obvious choices are Apache's `rotatelogs` +utility and `lograte`. + +```sh +yourapp | rotatelogs yourapp 86400 +``` + +## Testing + +``` +# install godo task runner +go get -u gopkg.in/godo.v2/cmd/godo + +# install dependencies +godo install -v + +# run test +godo test + +# run bench with allocs (requires manual cleanup of output) +godo bench-allocs +``` + +## License + +MIT License diff --git a/vendor/github.com/mgutz/logxi/images/demo.gif b/vendor/github.com/mgutz/logxi/images/demo.gif new file mode 100644 index 00000000..1f3e285c Binary files /dev/null and b/vendor/github.com/mgutz/logxi/images/demo.gif differ diff --git a/vendor/github.com/mgutz/logxi/v1/bench/bench_test.go b/vendor/github.com/mgutz/logxi/v1/bench/bench_test.go new file mode 100644 index 00000000..924650fe --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/bench/bench_test.go @@ -0,0 +1,169 @@ +package bench + +import ( + "encoding/json" + L "log" + "os" + "testing" + + "github.com/Sirupsen/logrus" + "github.com/mgutz/logxi/v1" + "gopkg.in/inconshreveable/log15.v2" +) + +type M map[string]interface{} + +var testObject = M{ + "foo": "bar", + "bah": M{ + "int": 1, + "float": -100.23, + "date": "06-01-01T15:04:05-0700", + "bool": true, + "nullable": nil, + }, +} + +var pid = os.Getpid() + +func toJSON(m map[string]interface{}) string { + b, _ := json.Marshal(m) + return string(b) +} + +// These tests write out all log levels with concurrency turned on and +// equivalent fields. + +func BenchmarkLog(b *testing.B) { + //fmt.Println("") + l := L.New(os.Stdout, "bench ", L.LstdFlags) + b.ResetTimer() + for i := 0; i < b.N; i++ { + debug := map[string]interface{}{"l": "debug", "key1": 1, "key2": "string", "key3": false} + l.Printf(toJSON(debug)) + + info := map[string]interface{}{"l": "info", "key1": 1, "key2": "string", "key3": false} + l.Printf(toJSON(info)) + + warn := map[string]interface{}{"l": "warn", "key1": 1, "key2": "string", "key3": false} + l.Printf(toJSON(warn)) + + err := map[string]interface{}{"l": "error", "key1": 1, "key2": "string", "key3": false} + l.Printf(toJSON(err)) + } + b.StopTimer() +} + +func BenchmarkLogComplex(b *testing.B) { + //fmt.Println("") + l := L.New(os.Stdout, "bench ", L.LstdFlags) + b.ResetTimer() + for i := 0; i < b.N; i++ { + debug := map[string]interface{}{"l": "debug", "key1": 1, "obj": testObject} + l.Printf(toJSON(debug)) + + info := map[string]interface{}{"l": "info", "key1": 1, "obj": testObject} + l.Printf(toJSON(info)) + + warn := map[string]interface{}{"l": "warn", "key1": 1, "obj": testObject} + l.Printf(toJSON(warn)) + + err := map[string]interface{}{"l": "error", "key1": 1, "obj": testObject} + l.Printf(toJSON(err)) + } + b.StopTimer() +} + +func BenchmarkLogxi(b *testing.B) { + //fmt.Println("") + stdout := log.NewConcurrentWriter(os.Stdout) + l := log.NewLogger3(stdout, "bench", log.NewJSONFormatter("bench")) + l.SetLevel(log.LevelDebug) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + l.Debug("debug", "key", 1, "key2", "string", "key3", false) + l.Info("info", "key", 1, "key2", "string", "key3", false) + l.Warn("warn", "key", 1, "key2", "string", "key3", false) + l.Error("error", "key", 1, "key2", "string", "key3", false) + } + b.StopTimer() +} + +func BenchmarkLogxiComplex(b *testing.B) { + //fmt.Println("") + stdout := log.NewConcurrentWriter(os.Stdout) + l := log.NewLogger3(stdout, "bench", log.NewJSONFormatter("bench")) + l.SetLevel(log.LevelDebug) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + l.Debug("debug", "key", 1, "obj", testObject) + l.Info("info", "key", 1, "obj", testObject) + l.Warn("warn", "key", 1, "obj", testObject) + l.Error("error", "key", 1, "obj", testObject) + } + b.StopTimer() + +} + +func BenchmarkLogrus(b *testing.B) { + //fmt.Println("") + l := logrus.New() + l.Formatter = &logrus.JSONFormatter{} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "key2": "string", "key3": false}).Debug("debug") + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "key2": "string", "key3": false}).Info("info") + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "key2": "string", "key3": false}).Warn("warn") + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "key2": "string", "key3": false}).Error("error") + } + b.StopTimer() +} + +func BenchmarkLogrusComplex(b *testing.B) { + //fmt.Println("") + l := logrus.New() + l.Formatter = &logrus.JSONFormatter{} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "obj": testObject}).Debug("debug") + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "obj": testObject}).Info("info") + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "obj": testObject}).Warn("warn") + l.WithFields(logrus.Fields{"_n": "bench", "_p": pid, "key": 1, "obj": testObject}).Error("error") + } + b.StopTimer() +} + +func BenchmarkLog15(b *testing.B) { + //fmt.Println("") + l := log15.New(log15.Ctx{"_n": "bench", "_p": pid}) + l.SetHandler(log15.SyncHandler(log15.StreamHandler(os.Stdout, log15.JsonFormat()))) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + l.Debug("debug", "key", 1, "key2", "string", "key3", false) + l.Info("info", "key", 1, "key2", "string", "key3", false) + l.Warn("warn", "key", 1, "key2", "string", "key3", false) + l.Error("error", "key", 1, "key2", "string", "key3", false) + } + b.StopTimer() + +} + +func BenchmarkLog15Complex(b *testing.B) { + //fmt.Println("") + l := log15.New(log15.Ctx{"_n": "bench", "_p": pid}) + l.SetHandler(log15.SyncHandler(log15.StreamHandler(os.Stdout, log15.JsonFormat()))) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + l.Debug("debug", "key", 1, "obj", testObject) + l.Info("info", "key", 1, "obj", testObject) + l.Warn("warn", "key", 1, "obj", testObject) + l.Error("error", "key", 1, "obj", testObject) + } + b.StopTimer() +} diff --git a/vendor/github.com/mgutz/logxi/v1/callstack.go b/vendor/github.com/mgutz/logxi/v1/callstack.go new file mode 100644 index 00000000..208eb405 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/callstack.go @@ -0,0 +1,261 @@ +package log + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/mgutz/ansi" +) + +type sourceLine struct { + lineno int + line string +} + +type frameInfo struct { + filename string + lineno int + method string + context []*sourceLine + contextLines int +} + +func (ci *frameInfo) readSource(contextLines int) error { + if ci.lineno == 0 || disableCallstack { + return nil + } + start := maxInt(1, ci.lineno-contextLines) + end := ci.lineno + contextLines + + f, err := os.Open(ci.filename) + if err != nil { + // if we can't read a file, it means user is running this in production + disableCallstack = true + return err + } + defer f.Close() + + lineno := 1 + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if start <= lineno && lineno <= end { + line := scanner.Text() + line = expandTabs(line, 4) + ci.context = append(ci.context, &sourceLine{lineno: lineno, line: line}) + } + lineno++ + } + + if err := scanner.Err(); err != nil { + InternalLog.Warn("scanner error", "file", ci.filename, "err", err) + } + return nil +} + +func (ci *frameInfo) String(color string, sourceColor string) string { + buf := pool.Get() + defer pool.Put(buf) + + if disableCallstack { + buf.WriteString(color) + buf.WriteString(Separator) + buf.WriteString(indent) + buf.WriteString(ci.filename) + buf.WriteRune(':') + buf.WriteString(strconv.Itoa(ci.lineno)) + return buf.String() + } + + // skip anything in the logxi package + if isLogxiCode(ci.filename) { + return "" + } + + // make path relative to current working directory or home + tildeFilename, err := filepath.Rel(wd, ci.filename) + if err != nil { + InternalLog.Warn("Could not make path relative", "path", ci.filename) + return "" + } + // ../../../ is too complex. Make path relative to home + if strings.HasPrefix(tildeFilename, strings.Repeat(".."+string(os.PathSeparator), 3)) { + tildeFilename = strings.Replace(tildeFilename, home, "~", 1) + } + + buf.WriteString(color) + buf.WriteString(Separator) + buf.WriteString(indent) + buf.WriteString("in ") + buf.WriteString(ci.method) + buf.WriteString("(") + buf.WriteString(tildeFilename) + buf.WriteRune(':') + buf.WriteString(strconv.Itoa(ci.lineno)) + buf.WriteString(")") + + if ci.contextLines == -1 { + return buf.String() + } + buf.WriteString("\n") + + // the width of the printed line number + var linenoWidth int + // trim spaces at start of source code based on common spaces + var skipSpaces = 1000 + + // calculate width of lineno and number of leading spaces that can be + // removed + for _, li := range ci.context { + linenoWidth = maxInt(linenoWidth, len(fmt.Sprintf("%d", li.lineno))) + index := indexOfNonSpace(li.line) + if index > -1 && index < skipSpaces { + skipSpaces = index + } + } + + for _, li := range ci.context { + var format string + format = fmt.Sprintf("%%s%%%dd: %%s\n", linenoWidth) + + if li.lineno == ci.lineno { + buf.WriteString(color) + if ci.contextLines > 2 { + format = fmt.Sprintf("%%s=> %%%dd: %%s\n", linenoWidth) + } + } else { + buf.WriteString(sourceColor) + if ci.contextLines > 2 { + // account for "=> " + format = fmt.Sprintf("%%s%%%dd: %%s\n", linenoWidth+3) + } + } + // trim spaces at start + idx := minInt(len(li.line), skipSpaces) + buf.WriteString(fmt.Sprintf(format, Separator+indent+indent, li.lineno, li.line[idx:])) + } + // get rid of last \n + buf.Truncate(buf.Len() - 1) + if !disableColors { + buf.WriteString(ansi.Reset) + } + return buf.String() +} + +// parseDebugStack parases a stack created by debug.Stack() +// +// This is what the string looks like +// /Users/mgutz/go/src/github.com/mgutz/logxi/v1/jsonFormatter.go:45 (0x5fa70) +// (*JSONFormatter).writeError: jf.writeString(buf, err.Error()+"\n"+string(debug.Stack())) +// /Users/mgutz/go/src/github.com/mgutz/logxi/v1/jsonFormatter.go:82 (0x5fdc3) +// (*JSONFormatter).appendValue: jf.writeError(buf, err) +// /Users/mgutz/go/src/github.com/mgutz/logxi/v1/jsonFormatter.go:109 (0x605ca) +// (*JSONFormatter).set: jf.appendValue(buf, val) +// ... +// /Users/mgutz/goroot/src/runtime/asm_amd64.s:2232 (0x38bf1) +// goexit: +func parseDebugStack(stack string, skip int, ignoreRuntime bool) []*frameInfo { + frames := []*frameInfo{} + // BUG temporarily disable since there is a bug with embedded newlines + if true { + return frames + } + + lines := strings.Split(stack, "\n") + + for i := skip * 2; i < len(lines); i += 2 { + ci := &frameInfo{} + sourceLine := lines[i] + if sourceLine == "" { + break + } + if ignoreRuntime && strings.Contains(sourceLine, filepath.Join("src", "runtime")) { + break + } + + colon := strings.Index(sourceLine, ":") + slash := strings.Index(sourceLine, "/") + if colon < slash { + // must be on Windows where paths look like c:/foo/bar.go:lineno + colon = strings.Index(sourceLine[slash:], ":") + slash + } + space := strings.Index(sourceLine, " ") + ci.filename = sourceLine[0:colon] + + // BUG with callstack where the error message has embedded newlines + // if colon > space { + // fmt.Println("lines", lines) + // } + // fmt.Println("SOURCELINE", sourceLine, "len", len(sourceLine), "COLON", colon, "SPACE", space) + numstr := sourceLine[colon+1 : space] + lineno, err := strconv.Atoi(numstr) + if err != nil { + InternalLog.Warn("Could not parse line number", "sourceLine", sourceLine, "numstr", numstr) + continue + } + ci.lineno = lineno + + methodLine := lines[i+1] + colon = strings.Index(methodLine, ":") + ci.method = strings.Trim(methodLine[0:colon], "\t ") + frames = append(frames, ci) + } + return frames +} + +// parseDebugStack parases a stack created by debug.Stack() +// +// This is what the string looks like +// /Users/mgutz/go/src/github.com/mgutz/logxi/v1/jsonFormatter.go:45 (0x5fa70) +// (*JSONFormatter).writeError: jf.writeString(buf, err.Error()+"\n"+string(debug.Stack())) +// /Users/mgutz/go/src/github.com/mgutz/logxi/v1/jsonFormatter.go:82 (0x5fdc3) +// (*JSONFormatter).appendValue: jf.writeError(buf, err) +// /Users/mgutz/go/src/github.com/mgutz/logxi/v1/jsonFormatter.go:109 (0x605ca) +// (*JSONFormatter).set: jf.appendValue(buf, val) +// ... +// /Users/mgutz/goroot/src/runtime/asm_amd64.s:2232 (0x38bf1) +// goexit: +func trimDebugStack(stack string) string { + buf := pool.Get() + defer pool.Put(buf) + lines := strings.Split(stack, "\n") + for i := 0; i < len(lines); i += 2 { + sourceLine := lines[i] + if sourceLine == "" { + break + } + + colon := strings.Index(sourceLine, ":") + slash := strings.Index(sourceLine, "/") + if colon < slash { + // must be on Windows where paths look like c:/foo/bar.go:lineno + colon = strings.Index(sourceLine[slash:], ":") + slash + } + filename := sourceLine[0:colon] + // skip anything in the logxi package + if isLogxiCode(filename) { + continue + } + buf.WriteString(sourceLine) + buf.WriteRune('\n') + buf.WriteString(lines[i+1]) + buf.WriteRune('\n') + } + return buf.String() +} + +func parseLogxiStack(entry map[string]interface{}, skip int, ignoreRuntime bool) []*frameInfo { + kv := entry[KeyMap.CallStack] + if kv == nil { + return nil + } + + var frames []*frameInfo + if stack, ok := kv.(string); ok { + frames = parseDebugStack(stack, skip, ignoreRuntime) + } + return frames +} diff --git a/vendor/github.com/mgutz/logxi/v1/cmd/demo/main.ansi b/vendor/github.com/mgutz/logxi/v1/cmd/demo/main.ansi new file mode 100644 index 00000000..f5dc1e46 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/cmd/demo/main.ansi @@ -0,0 +1,17 @@ +import  "github.com/mgutz/logxi/v1" + +func loadConfig() { + logger.Error("Could not read config file", "err", errConfig) +} + +func main() { + // create loggers + log.Trace("creating loggers") + logger = log.New("server") + modelsLogger := log.New("models") + + logger.Debug("Process", "hostname", hostname, "pid", os.Getpid()) + modelsLogger.Info("Connecting to database...") + modelsLogger.Warn("Could not connect, retrying ...", "dsn", dsn) + loadConfig() +} diff --git a/vendor/github.com/mgutz/logxi/v1/cmd/demo/main.go b/vendor/github.com/mgutz/logxi/v1/cmd/demo/main.go new file mode 100644 index 00000000..82ab062f --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/cmd/demo/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "os" + + "github.com/mgutz/logxi/v1" +) + +var errConfig = fmt.Errorf("file not found") +var dsn = "dbname=testdb" +var logger log.Logger +var hostname string +var configFile = "config.json" + +func init() { + hostname, _ = os.Hostname() +} + +func loadConfig() { + logger.Error("Could not read config file", "err", errConfig) +} + +func main() { + // create loggers + log.Trace("creating loggers") + logger = log.New("server") + modelsLogger := log.New("models") + + logger.Debug("Process", "hostname", hostname, "pid", os.Getpid()) + modelsLogger.Info("Connecting to database...") + modelsLogger.Warn("Could not connect, retrying ...", "dsn", dsn) + loadConfig() +} diff --git a/vendor/github.com/mgutz/logxi/v1/cmd/filter/README.md b/vendor/github.com/mgutz/logxi/v1/cmd/filter/README.md new file mode 100644 index 00000000..9f82b36b --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/cmd/filter/README.md @@ -0,0 +1,10 @@ +# filter + +Filter is an example of a how to process JSON log +entries using pipes in your shell. + +```sh +yourapp | filter +``` + +You can try see it in action with `godo filter` diff --git a/vendor/github.com/mgutz/logxi/v1/cmd/filter/main.go b/vendor/github.com/mgutz/logxi/v1/cmd/filter/main.go new file mode 100644 index 00000000..33492edf --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/cmd/filter/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + + "github.com/mgutz/logxi/v1" +) + +func sendExternal(obj map[string]interface{}) { + // normally you would send this to an external service like InfluxDB + // or some logging framework. Let's filter out some data. + fmt.Printf("Time: %s Level: %s Message: %s\n", + obj[log.KeyMap.Time], + obj[log.KeyMap.Level], + obj[log.KeyMap.Message], + ) +} + +func main() { + r := bufio.NewReader(os.Stdin) + dec := json.NewDecoder(r) + for { + var obj map[string]interface{} + if err := dec.Decode(&obj); err == io.EOF { + break + } else if err != nil { + log.InternalLog.Fatal("Could not decode", "err", err) + } + sendExternal(obj) + } +} diff --git a/vendor/github.com/mgutz/logxi/v1/cmd/reldir/README.md b/vendor/github.com/mgutz/logxi/v1/cmd/reldir/README.md new file mode 100644 index 00000000..221a7a29 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/cmd/reldir/README.md @@ -0,0 +1,3 @@ +# reldir + +Used to test relative paths when logging context. diff --git a/vendor/github.com/mgutz/logxi/v1/cmd/reldir/foo.go b/vendor/github.com/mgutz/logxi/v1/cmd/reldir/foo.go new file mode 100644 index 00000000..7729f2f6 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/cmd/reldir/foo.go @@ -0,0 +1,8 @@ +package reldir + +import "github.com/mgutz/logxi/v1" + +// Foo returns error +func Foo() { + log.Error("Oh bar!") +} diff --git a/vendor/github.com/mgutz/logxi/v1/concurrentWriter.go b/vendor/github.com/mgutz/logxi/v1/concurrentWriter.go new file mode 100644 index 00000000..960f97e7 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/concurrentWriter.go @@ -0,0 +1,25 @@ +package log + +import ( + "io" + "sync" +) + +// ConcurrentWriter is a concurrent safe wrapper around io.Writer +type ConcurrentWriter struct { + writer io.Writer + sync.Mutex +} + +// NewConcurrentWriter crates a new concurrent writer wrapper around existing writer. +func NewConcurrentWriter(writer io.Writer) io.Writer { + return &ConcurrentWriter{writer: writer} +} + +func (cw *ConcurrentWriter) Write(p []byte) (n int, err error) { + cw.Lock() + defer cw.Unlock() + // This is basically the same logic as in go's log.Output() which + // doesn't look at the returned number of bytes returned + return cw.writer.Write(p) +} diff --git a/vendor/github.com/mgutz/logxi/v1/defaultLogger.go b/vendor/github.com/mgutz/logxi/v1/defaultLogger.go new file mode 100644 index 00000000..40fb5132 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/defaultLogger.go @@ -0,0 +1,149 @@ +package log + +import ( + "fmt" + "io" +) + +// DefaultLogger is the default logger for this package. +type DefaultLogger struct { + writer io.Writer + name string + level int + formatter Formatter +} + +// NewLogger creates a new default logger. If writer is not concurrent +// safe, wrap it with NewConcurrentWriter. +func NewLogger(writer io.Writer, name string) Logger { + formatter, err := createFormatter(name, logxiFormat) + if err != nil { + panic("Could not create formatter") + } + return NewLogger3(writer, name, formatter) +} + +// NewLogger3 creates a new logger with a writer, name and formatter. If writer is not concurrent +// safe, wrap it with NewConcurrentWriter. +func NewLogger3(writer io.Writer, name string, formatter Formatter) Logger { + var level int + if name != "__logxi" { + // if err is returned, then it means the log is disabled + level = getLogLevel(name) + if level == LevelOff { + return NullLog + } + } + + log := &DefaultLogger{ + formatter: formatter, + writer: writer, + name: name, + level: level, + } + + // TODO loggers will be used when watching changes to configuration such + // as in consul, etcd + loggers.Lock() + loggers.loggers[name] = log + loggers.Unlock() + return log +} + +// New creates a colorable default logger. +func New(name string) Logger { + return NewLogger(colorableStdout, name) +} + +// Trace logs a debug entry. +func (l *DefaultLogger) Trace(msg string, args ...interface{}) { + l.Log(LevelTrace, msg, args) +} + +// Debug logs a debug entry. +func (l *DefaultLogger) Debug(msg string, args ...interface{}) { + l.Log(LevelDebug, msg, args) +} + +// Info logs an info entry. +func (l *DefaultLogger) Info(msg string, args ...interface{}) { + l.Log(LevelInfo, msg, args) +} + +// Warn logs a warn entry. +func (l *DefaultLogger) Warn(msg string, args ...interface{}) error { + if l.IsWarn() { + defer l.Log(LevelWarn, msg, args) + + for _, arg := range args { + if err, ok := arg.(error); ok { + return err + } + } + + return nil + } + return nil +} + +func (l *DefaultLogger) extractLogError(level int, msg string, args []interface{}) error { + defer l.Log(level, msg, args) + + for _, arg := range args { + if err, ok := arg.(error); ok { + return err + } + } + return fmt.Errorf(msg) +} + +// Error logs an error entry. +func (l *DefaultLogger) Error(msg string, args ...interface{}) error { + return l.extractLogError(LevelError, msg, args) +} + +// Fatal logs a fatal entry then panics. +func (l *DefaultLogger) Fatal(msg string, args ...interface{}) { + l.extractLogError(LevelFatal, msg, args) + defer panic("Exit due to fatal error: ") +} + +// Log logs a leveled entry. +func (l *DefaultLogger) Log(level int, msg string, args []interface{}) { + // log if the log level (warn=4) >= level of message (err=3) + if l.level < level || silent { + return + } + l.formatter.Format(l.writer, level, msg, args) +} + +// IsTrace determines if this logger logs a debug statement. +func (l *DefaultLogger) IsTrace() bool { + // DEBUG(7) >= TRACE(10) + return l.level >= LevelTrace +} + +// IsDebug determines if this logger logs a debug statement. +func (l *DefaultLogger) IsDebug() bool { + return l.level >= LevelDebug +} + +// IsInfo determines if this logger logs an info statement. +func (l *DefaultLogger) IsInfo() bool { + return l.level >= LevelInfo +} + +// IsWarn determines if this logger logs a warning statement. +func (l *DefaultLogger) IsWarn() bool { + return l.level >= LevelWarn +} + +// SetLevel sets the level of this logger. +func (l *DefaultLogger) SetLevel(level int) { + l.level = level +} + +// SetFormatter set the formatter for this logger. +func (l *DefaultLogger) SetFormatter(formatter Formatter) { + l.formatter = formatter +} diff --git a/vendor/github.com/mgutz/logxi/v1/env.go b/vendor/github.com/mgutz/logxi/v1/env.go new file mode 100644 index 00000000..c61c452a --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/env.go @@ -0,0 +1,166 @@ +package log + +import ( + "os" + "strconv" + "strings" +) + +var contextLines int + +// Configuration comes from environment or external services like +// consul, etcd. +type Configuration struct { + Format string `json:"format"` + Colors string `json:"colors"` + Levels string `json:"levels"` +} + +func readFromEnviron() *Configuration { + conf := &Configuration{} + + var envOrDefault = func(name, val string) string { + result := os.Getenv(name) + if result == "" { + result = val + } + return result + } + + conf.Levels = envOrDefault("LOGXI", defaultLogxiEnv) + conf.Format = envOrDefault("LOGXI_FORMAT", defaultLogxiFormatEnv) + conf.Colors = envOrDefault("LOGXI_COLORS", defaultLogxiColorsEnv) + return conf +} + +// ProcessEnv (re)processes environment. +func ProcessEnv(env *Configuration) { + // TODO: allow reading from etcd + + ProcessLogxiEnv(env.Levels) + ProcessLogxiColorsEnv(env.Colors) + ProcessLogxiFormatEnv(env.Format) +} + +// ProcessLogxiFormatEnv parses LOGXI_FORMAT +func ProcessLogxiFormatEnv(env string) { + logxiFormat = env + m := parseKVList(logxiFormat, ",") + formatterFormat := "" + tFormat := "" + for key, value := range m { + switch key { + default: + formatterFormat = key + case "t": + tFormat = value + case "pretty": + isPretty = value != "false" && value != "0" + case "maxcol": + col, err := strconv.Atoi(value) + if err == nil { + maxCol = col + } else { + maxCol = defaultMaxCol + } + case "context": + lines, err := strconv.Atoi(value) + if err == nil { + contextLines = lines + } else { + contextLines = defaultContextLines + } + case "LTSV": + formatterFormat = "text" + AssignmentChar = ltsvAssignmentChar + Separator = ltsvSeparator + } + } + if formatterFormat == "" || formatterCreators[formatterFormat] == nil { + formatterFormat = defaultFormat + } + logxiFormat = formatterFormat + if tFormat == "" { + tFormat = defaultTimeFormat + } + timeFormat = tFormat +} + +// ProcessLogxiEnv parses LOGXI variable +func ProcessLogxiEnv(env string) { + logxiEnable := env + if logxiEnable == "" { + logxiEnable = defaultLogxiEnv + } + + logxiNameLevelMap = map[string]int{} + m := parseKVList(logxiEnable, ",") + if m == nil { + logxiNameLevelMap["*"] = defaultLevel + } + for key, value := range m { + if strings.HasPrefix(key, "-") { + // LOGXI=*,-foo => disable foo + logxiNameLevelMap[key[1:]] = LevelOff + } else if value == "" { + // LOGXI=* => default to all + logxiNameLevelMap[key] = LevelAll + } else { + // LOGXI=*=ERR => use user-specified level + level := LevelAtoi[value] + if level == 0 { + InternalLog.Error("Unknown level in LOGXI environment variable", "key", key, "value", value, "LOGXI", env) + level = defaultLevel + } + logxiNameLevelMap[key] = level + } + } + + // must always have global default, otherwise errs may get eaten up + if _, ok := logxiNameLevelMap["*"]; !ok { + logxiNameLevelMap["*"] = LevelError + } +} + +func getLogLevel(name string) int { + var wildcardLevel int + var result int + + for k, v := range logxiNameLevelMap { + if k == name { + result = v + } else if k == "*" { + wildcardLevel = v + } else if strings.HasPrefix(k, "*") && strings.HasSuffix(name, k[1:]) { + result = v + } else if strings.HasSuffix(k, "*") && strings.HasPrefix(name, k[:len(k)-1]) { + result = v + } + } + + if result == LevelOff { + return LevelOff + } + + if result > 0 { + return result + } + + if wildcardLevel > 0 { + return wildcardLevel + } + + return LevelOff +} + +// ProcessLogxiColorsEnv parases LOGXI_COLORS +func ProcessLogxiColorsEnv(env string) { + colors := env + if colors == "" { + colors = defaultLogxiColorsEnv + } else if colors == "*=off" { + // disable all colors + disableColors = true + } + theme = parseTheme(colors) +} diff --git a/vendor/github.com/mgutz/logxi/v1/formatter.go b/vendor/github.com/mgutz/logxi/v1/formatter.go new file mode 100644 index 00000000..93573948 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/formatter.go @@ -0,0 +1,61 @@ +package log + +var formatterCreators = map[string]CreateFormatterFunc{} + +// CreateFormatterFunc is a function which creates a new instance +// of a Formatter. +type CreateFormatterFunc func(name, kind string) (Formatter, error) + +// createFormatter creates formatters. It accepts a kind in {"text", "JSON"} +// which correspond to TextFormatter and JSONFormatter, and the name of the +// logger. +func createFormatter(name string, kind string) (Formatter, error) { + if kind == FormatEnv { + kind = logxiFormat + } + if kind == "" { + kind = FormatText + } + + fn := formatterCreators[kind] + if fn == nil { + fn = formatterCreators[FormatText] + } + + formatter, err := fn(name, kind) + if err != nil { + return nil, err + } + // custom formatter may have not returned a formatter + if formatter == nil { + formatter, err = formatFactory(name, FormatText) + } + return formatter, err +} + +func formatFactory(name string, kind string) (Formatter, error) { + var formatter Formatter + var err error + switch kind { + default: + formatter = NewTextFormatter(name) + case FormatHappy: + formatter = NewHappyDevFormatter(name) + case FormatText: + formatter = NewTextFormatter(name) + case FormatJSON: + formatter = NewJSONFormatter(name) + } + return formatter, err +} + +// RegisterFormatFactory registers a format factory function. +func RegisterFormatFactory(kind string, fn CreateFormatterFunc) { + if kind == "" { + panic("kind is empty string") + } + if fn == nil { + panic("creator is nil") + } + formatterCreators[kind] = fn +} diff --git a/vendor/github.com/mgutz/logxi/v1/happyDevFormatter.go b/vendor/github.com/mgutz/logxi/v1/happyDevFormatter.go new file mode 100644 index 00000000..3931b369 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/happyDevFormatter.go @@ -0,0 +1,373 @@ +package log + +import ( + "encoding/json" + "fmt" + "io" + "runtime/debug" + "strings" + + "github.com/mgutz/ansi" +) + +// colorScheme defines a color theme for HappyDevFormatter +type colorScheme struct { + Key string + Message string + Value string + Misc string + Source string + + Trace string + Debug string + Info string + Warn string + Error string +} + +var indent = " " +var maxCol = defaultMaxCol +var theme *colorScheme + +func parseKVList(s, separator string) map[string]string { + pairs := strings.Split(s, separator) + if len(pairs) == 0 { + return nil + } + m := map[string]string{} + for _, pair := range pairs { + if pair == "" { + continue + } + parts := strings.Split(pair, "=") + switch len(parts) { + case 1: + m[parts[0]] = "" + case 2: + m[parts[0]] = parts[1] + } + } + return m +} + +func parseTheme(theme string) *colorScheme { + m := parseKVList(theme, ",") + cs := &colorScheme{} + var wildcard string + + var color = func(key string) string { + if disableColors { + return "" + } + style := m[key] + c := ansi.ColorCode(style) + if c == "" { + c = wildcard + } + //fmt.Printf("plain=%b [%s] %s=%q\n", ansi.DefaultFG, key, style, c) + + return c + } + wildcard = color("*") + + if wildcard != ansi.Reset { + cs.Key = wildcard + cs.Value = wildcard + cs.Misc = wildcard + cs.Source = wildcard + cs.Message = wildcard + + cs.Trace = wildcard + cs.Debug = wildcard + cs.Warn = wildcard + cs.Info = wildcard + cs.Error = wildcard + } + + cs.Key = color("key") + cs.Value = color("value") + cs.Misc = color("misc") + cs.Source = color("source") + cs.Message = color("message") + + cs.Trace = color("TRC") + cs.Debug = color("DBG") + cs.Warn = color("WRN") + cs.Info = color("INF") + cs.Error = color("ERR") + return cs +} + +// HappyDevFormatter is the formatter used for terminals. It is +// colorful, dev friendly and provides meaningful logs when +// warnings and errors occur. +// +// HappyDevFormatter does not worry about performance. It's at least 3-4X +// slower than JSONFormatter since it delegates to JSONFormatter to marshal +// then unmarshal JSON. Then it does other stuff like read source files, sort +// keys all to give a developer more information. +// +// SHOULD NOT be used in production for extended period of time. However, it +// works fine in SSH terminals and binary deployments. +type HappyDevFormatter struct { + name string + col int + // always use the production formatter + jsonFormatter *JSONFormatter +} + +// NewHappyDevFormatter returns a new instance of HappyDevFormatter. +func NewHappyDevFormatter(name string) *HappyDevFormatter { + jf := NewJSONFormatter(name) + return &HappyDevFormatter{ + name: name, + jsonFormatter: jf, + } +} + +func (hd *HappyDevFormatter) writeKey(buf bufferWriter, key string) { + // assumes this is not the first key + hd.writeString(buf, Separator) + if key == "" { + return + } + buf.WriteString(theme.Key) + hd.writeString(buf, key) + hd.writeString(buf, AssignmentChar) + if !disableColors { + buf.WriteString(ansi.Reset) + } +} + +func (hd *HappyDevFormatter) set(buf bufferWriter, key string, value interface{}, color string) { + var str string + if s, ok := value.(string); ok { + str = s + } else if s, ok := value.(fmt.Stringer); ok { + str = s.String() + } else { + str = fmt.Sprintf("%v", value) + } + val := strings.Trim(str, "\n ") + if (isPretty && key != "") || hd.col+len(key)+2+len(val) >= maxCol { + buf.WriteString("\n") + hd.col = 0 + hd.writeString(buf, indent) + } + hd.writeKey(buf, key) + if color != "" { + buf.WriteString(color) + } + hd.writeString(buf, val) + if color != "" && !disableColors { + buf.WriteString(ansi.Reset) + } +} + +// Write a string and tracks the position of the string so we can break lines +// cleanly. Do not send ANSI escape sequences, just raw strings +func (hd *HappyDevFormatter) writeString(buf bufferWriter, s string) { + buf.WriteString(s) + hd.col += len(s) +} + +func (hd *HappyDevFormatter) getContext(color string) string { + if disableCallstack { + return "" + } + frames := parseDebugStack(string(debug.Stack()), 5, true) + if len(frames) == 0 { + return "" + } + for _, frame := range frames { + context := frame.String(color, theme.Source) + if context != "" { + return context + } + } + return "" +} + +func (hd *HappyDevFormatter) getLevelContext(level int, entry map[string]interface{}) (message string, context string, color string) { + + switch level { + case LevelTrace: + color = theme.Trace + context = hd.getContext(color) + context += "\n" + case LevelDebug: + color = theme.Debug + case LevelInfo: + color = theme.Info + // case LevelWarn: + // color = theme.Warn + // context = hd.getContext(color) + // context += "\n" + case LevelWarn, LevelError, LevelFatal: + + // warnings return an error but if it does not have an error + // then print line info only + if level == LevelWarn { + color = theme.Warn + kv := entry[KeyMap.CallStack] + if kv == nil { + context = hd.getContext(color) + context += "\n" + break + } + } else { + color = theme.Error + } + + if disableCallstack || contextLines == -1 { + context = trimDebugStack(string(debug.Stack())) + break + } + frames := parseLogxiStack(entry, 4, true) + if frames == nil { + frames = parseDebugStack(string(debug.Stack()), 4, true) + } + + if len(frames) == 0 { + break + } + errbuf := pool.Get() + defer pool.Put(errbuf) + lines := 0 + for _, frame := range frames { + err := frame.readSource(contextLines) + if err != nil { + // by setting to empty, the original stack is used + errbuf.Reset() + break + } + ctx := frame.String(color, theme.Source) + if ctx == "" { + continue + } + errbuf.WriteString(ctx) + errbuf.WriteRune('\n') + lines++ + } + context = errbuf.String() + default: + panic("should never get here") + } + return message, context, color +} + +// Format a log entry. +func (hd *HappyDevFormatter) Format(writer io.Writer, level int, msg string, args []interface{}) { + buf := pool.Get() + defer pool.Put(buf) + + if len(args) == 1 { + args = append(args, 0) + copy(args[1:], args[0:]) + args[0] = singleArgKey + } + + // warn about reserved, bad and complex keys + for i := 0; i < len(args); i += 2 { + isReserved, err := isReservedKey(args[i]) + if err != nil { + InternalLog.Error("Key is not a string.", "err", fmt.Errorf("args[%d]=%v", i, args[i])) + } else if isReserved { + InternalLog.Fatal("Key conflicts with reserved key. Avoiding using single rune keys.", "key", args[i].(string)) + } else { + // Ensure keys are simple strings. The JSONFormatter doesn't escape + // keys as a performance tradeoff. This panics if the JSON key + // value has a different value than a simple quoted string. + key := args[i].(string) + b, err := json.Marshal(key) + if err != nil { + panic("Key is invalid. " + err.Error()) + } + if string(b) != `"`+key+`"` { + panic("Key is complex. Use simpler key for: " + fmt.Sprintf("%q", key)) + } + } + } + + // use the production JSON formatter to format the log first. This + // ensures JSON will marshal/unmarshal correctly in production. + entry := hd.jsonFormatter.LogEntry(level, msg, args) + + // reset the column tracker used for fancy formatting + hd.col = 0 + + // timestamp + buf.WriteString(theme.Misc) + hd.writeString(buf, entry[KeyMap.Time].(string)) + if !disableColors { + buf.WriteString(ansi.Reset) + } + + // emphasize warnings and errors + message, context, color := hd.getLevelContext(level, entry) + if message == "" { + message = entry[KeyMap.Message].(string) + } + + // DBG, INF ... + hd.set(buf, "", entry[KeyMap.Level].(string), color) + // logger name + hd.set(buf, "", entry[KeyMap.Name], theme.Misc) + // message from user + hd.set(buf, "", message, theme.Message) + + // Preserve key order in the sequencethey were added by developer.This + // makes it easier for developers to follow the log. + order := []string{} + lenArgs := len(args) + for i := 0; i < len(args); i += 2 { + if i+1 >= lenArgs { + continue + } + if key, ok := args[i].(string); ok { + order = append(order, key) + } else { + order = append(order, badKeyAtIndex(i)) + } + } + + for _, key := range order { + // skip reserved keys which were already added to buffer above + isReserved, err := isReservedKey(key) + if err != nil { + panic("key is invalid. Should never get here. " + err.Error()) + } else if isReserved { + continue + } + hd.set(buf, key, entry[key], theme.Value) + } + + addLF := true + hasCallStack := entry[KeyMap.CallStack] != nil + // WRN,ERR file, line number context + + if context != "" { + // warnings and traces are single line, space can be optimized + if level == LevelTrace || (level == LevelWarn && !hasCallStack) { + // gets rid of "in " + idx := strings.IndexRune(context, 'n') + hd.set(buf, "in", context[idx+2:], color) + } else { + buf.WriteRune('\n') + if !disableColors { + buf.WriteString(color) + } + addLF = context[len(context)-1:len(context)] != "\n" + buf.WriteString(context) + if !disableColors { + buf.WriteString(ansi.Reset) + } + } + } else if hasCallStack { + hd.set(buf, "", entry[KeyMap.CallStack], color) + } + if addLF { + buf.WriteRune('\n') + } + buf.WriteTo(writer) +} diff --git a/vendor/github.com/mgutz/logxi/v1/init.go b/vendor/github.com/mgutz/logxi/v1/init.go new file mode 100644 index 00000000..57c91404 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/init.go @@ -0,0 +1,200 @@ +package log + +import ( + "fmt" + "io" + "os" + "runtime" + "strconv" + "sync" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +// scream so user fixes it +const warnImbalancedKey = "FIX_IMBALANCED_PAIRS" +const warnImbalancedPairs = warnImbalancedKey + " => " +const singleArgKey = "_" + +func badKeyAtIndex(i int) string { + return "BAD_KEY_AT_INDEX_" + strconv.Itoa(i) +} + +// DefaultLogLog is the default log for this package. +var DefaultLog Logger + +// Suppress supresses logging and is useful to supress output in +// in unit tests. +// +// Example +// log.Suppress(true) +// defer log.suppress(false) +func Suppress(quiet bool) { + silent = quiet +} + +var silent bool + +// internalLog is the logger used by logxi itself +var InternalLog Logger + +type loggerMap struct { + sync.Mutex + loggers map[string]Logger +} + +var loggers = &loggerMap{ + loggers: map[string]Logger{}, +} + +func (lm *loggerMap) set(name string, logger Logger) { + lm.loggers[name] = logger +} + +// The assignment character between key-value pairs +var AssignmentChar = ": " + +// Separator is the separator to use between key value pairs +//var Separator = "{~}" +var Separator = " " + +const ltsvAssignmentChar = ":" +const ltsvSeparator = "\t" + +// logxiEnabledMap maps log name patterns to levels +var logxiNameLevelMap map[string]int + +// logxiFormat is the formatter kind to create +var logxiFormat string + +var colorableStdout io.Writer +var defaultContextLines = 2 +var defaultFormat string +var defaultLevel int +var defaultLogxiEnv string +var defaultLogxiFormatEnv string +var defaultMaxCol = 80 +var defaultPretty = false +var defaultLogxiColorsEnv string +var defaultTimeFormat string +var disableCallstack bool +var disableCheckKeys bool +var disableColors bool +var home string +var isPretty bool +var isTerminal bool +var isWindows = runtime.GOOS == "windows" +var pkgMutex sync.Mutex +var pool = NewBufferPool() +var timeFormat string +var wd string +var pid = os.Getpid() +var pidStr = strconv.Itoa(os.Getpid()) + +// KeyMapping is the key map used to print built-in log entry fields. +type KeyMapping struct { + Level string + Message string + Name string + PID string + Time string + CallStack string +} + +// KeyMap is the key map to use when printing log statements. +var KeyMap = &KeyMapping{ + Level: "_l", + Message: "_m", + Name: "_n", + PID: "_p", + Time: "_t", + CallStack: "_c", +} + +var logxiKeys []string + +func setDefaults(isTerminal bool) { + var err error + contextLines = defaultContextLines + wd, err = os.Getwd() + if err != nil { + InternalLog.Error("Could not get working directory") + } + + logxiKeys = []string{KeyMap.Level, KeyMap.Message, KeyMap.Name, KeyMap.Time, KeyMap.CallStack, KeyMap.PID} + + if isTerminal { + defaultLogxiEnv = "*=WRN" + defaultLogxiFormatEnv = "happy,fit,maxcol=80,t=15:04:05.000000,context=-1" + defaultFormat = FormatHappy + defaultLevel = LevelWarn + defaultTimeFormat = "15:04:05.000000" + } else { + defaultLogxiEnv = "*=ERR" + defaultLogxiFormatEnv = "JSON,t=2006-01-02T15:04:05-0700" + defaultFormat = FormatJSON + defaultLevel = LevelError + defaultTimeFormat = "2006-01-02T15:04:05-0700" + disableColors = true + } + + if isWindows { + home = os.Getenv("HOMEPATH") + if os.Getenv("ConEmuANSI") == "ON" { + defaultLogxiColorsEnv = "key=cyan+h,value,misc=blue+h,source=yellow,TRC,DBG,WRN=yellow+h,INF=green+h,ERR=red+h" + } else { + colorableStdout = NewConcurrentWriter(colorable.NewColorableStdout()) + defaultLogxiColorsEnv = "ERR=red,misc=cyan,key=cyan" + } + // DefaultScheme is a color scheme optimized for dark background + // but works well with light backgrounds + } else { + home = os.Getenv("HOME") + term := os.Getenv("TERM") + if term == "xterm-256color" { + defaultLogxiColorsEnv = "key=cyan+h,value,misc=blue,source=88,TRC,DBG,WRN=yellow,INF=green+h,ERR=red+h,message=magenta+h" + } else { + defaultLogxiColorsEnv = "key=cyan+h,value,misc=blue,source=magenta,TRC,DBG,WRN=yellow,INF=green,ERR=red+h" + } + } +} + +func isReservedKey(k interface{}) (bool, error) { + key, ok := k.(string) + if !ok { + return false, fmt.Errorf("Key is not a string") + } + + // check if reserved + for _, key2 := range logxiKeys { + if key == key2 { + return true, nil + } + } + return false, nil +} + +func init() { + colorableStdout = NewConcurrentWriter(os.Stdout) + + isTerminal = isatty.IsTerminal(os.Stdout.Fd()) + + // the internal logger to report errors + if isTerminal { + InternalLog = NewLogger3(NewConcurrentWriter(os.Stdout), "__logxi", NewTextFormatter("__logxi")) + } else { + InternalLog = NewLogger3(NewConcurrentWriter(os.Stdout), "__logxi", NewJSONFormatter("__logxi")) + } + InternalLog.SetLevel(LevelError) + + setDefaults(isTerminal) + + RegisterFormatFactory(FormatHappy, formatFactory) + RegisterFormatFactory(FormatText, formatFactory) + RegisterFormatFactory(FormatJSON, formatFactory) + ProcessEnv(readFromEnviron()) + + // package logger for users + DefaultLog = New("~") +} diff --git a/vendor/github.com/mgutz/logxi/v1/init_test.go b/vendor/github.com/mgutz/logxi/v1/init_test.go new file mode 100644 index 00000000..568c9b60 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/init_test.go @@ -0,0 +1,26 @@ +package log + +import ( + "bytes" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +var testBuf bytes.Buffer + +var testInternalLog Logger + +func init() { + testInternalLog = NewLogger3(&testBuf, "__logxi", NewTextFormatter("__logxi")) + testInternalLog.SetLevel(LevelError) +} + +func TestUnknownLevel(t *testing.T) { + testResetEnv() + os.Setenv("LOGXI", "*=oy") + processEnv() + buffer := testBuf.String() + assert.Contains(t, buffer, "Unknown level", "should error on unknown level") +} diff --git a/vendor/github.com/mgutz/logxi/v1/jsonFormatter.go b/vendor/github.com/mgutz/logxi/v1/jsonFormatter.go new file mode 100644 index 00000000..b21dd08c --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/jsonFormatter.go @@ -0,0 +1,205 @@ +package log + +import ( + "encoding/json" + "fmt" + "io" + "reflect" + "runtime/debug" + "strconv" + "time" +) + +type bufferWriter interface { + Write(p []byte) (nn int, err error) + WriteRune(r rune) (n int, err error) + WriteString(s string) (n int, err error) +} + +// JSONFormatter is a fast, efficient JSON formatter optimized for logging. +// +// * log entry keys are not escaped +// Who uses complex keys when coding? Checked by HappyDevFormatter in case user does. +// Nested object keys are escaped by json.Marshal(). +// * Primitive types uses strconv +// * Logger reserved key values (time, log name, level) require no conversion +// * sync.Pool buffer for bytes.Buffer +type JSONFormatter struct { + name string +} + +// NewJSONFormatter creates a new instance of JSONFormatter. +func NewJSONFormatter(name string) *JSONFormatter { + return &JSONFormatter{name: name} +} + +func (jf *JSONFormatter) writeString(buf bufferWriter, s string) { + b, err := json.Marshal(s) + if err != nil { + InternalLog.Error("Could not json.Marshal string.", "str", s) + buf.WriteString(`"Could not marshal this key's string"`) + return + } + buf.Write(b) +} + +func (jf *JSONFormatter) writeError(buf bufferWriter, err error) { + jf.writeString(buf, err.Error()) + jf.set(buf, KeyMap.CallStack, string(debug.Stack())) + return +} + +func (jf *JSONFormatter) appendValue(buf bufferWriter, val interface{}) { + if val == nil { + buf.WriteString("null") + return + } + + // always show error stack even at cost of some performance. there's + // nothing worse than looking at production logs without a clue + if err, ok := val.(error); ok { + jf.writeError(buf, err) + return + } + + value := reflect.ValueOf(val) + kind := value.Kind() + if kind == reflect.Ptr { + if value.IsNil() { + buf.WriteString("null") + return + } + value = value.Elem() + kind = value.Kind() + } + switch kind { + case reflect.Bool: + if value.Bool() { + buf.WriteString("true") + } else { + buf.WriteString("false") + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + buf.WriteString(strconv.FormatInt(value.Int(), 10)) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + buf.WriteString(strconv.FormatUint(value.Uint(), 10)) + + case reflect.Float32: + buf.WriteString(strconv.FormatFloat(value.Float(), 'g', -1, 32)) + + case reflect.Float64: + buf.WriteString(strconv.FormatFloat(value.Float(), 'g', -1, 64)) + + default: + var err error + var b []byte + if stringer, ok := val.(fmt.Stringer); ok { + b, err = json.Marshal(stringer.String()) + } else { + b, err = json.Marshal(val) + } + + if err != nil { + InternalLog.Error("Could not json.Marshal value: ", "formatter", "JSONFormatter", "err", err.Error()) + if s, ok := val.(string); ok { + b, err = json.Marshal(s) + } else if s, ok := val.(fmt.Stringer); ok { + b, err = json.Marshal(s.String()) + } else { + b, err = json.Marshal(fmt.Sprintf("%#v", val)) + } + + if err != nil { + // should never get here, but JSONFormatter should never panic + msg := "Could not Sprintf value" + InternalLog.Error(msg) + buf.WriteString(`"` + msg + `"`) + return + } + } + buf.Write(b) + } +} + +func (jf *JSONFormatter) set(buf bufferWriter, key string, val interface{}) { + // WARNING: assumes this is not first key + buf.WriteString(`, "`) + buf.WriteString(key) + buf.WriteString(`":`) + jf.appendValue(buf, val) +} + +// Format formats log entry as JSON. +func (jf *JSONFormatter) Format(writer io.Writer, level int, msg string, args []interface{}) { + buf := pool.Get() + defer pool.Put(buf) + + const lead = `", "` + const colon = `":"` + + buf.WriteString(`{"`) + buf.WriteString(KeyMap.Time) + buf.WriteString(`":"`) + buf.WriteString(time.Now().Format(timeFormat)) + + buf.WriteString(`", "`) + buf.WriteString(KeyMap.PID) + buf.WriteString(`":"`) + buf.WriteString(pidStr) + + buf.WriteString(`", "`) + buf.WriteString(KeyMap.Level) + buf.WriteString(`":"`) + buf.WriteString(LevelMap[level]) + + buf.WriteString(`", "`) + buf.WriteString(KeyMap.Name) + buf.WriteString(`":"`) + buf.WriteString(jf.name) + + buf.WriteString(`", "`) + buf.WriteString(KeyMap.Message) + buf.WriteString(`":`) + jf.appendValue(buf, msg) + + var lenArgs = len(args) + if lenArgs > 0 { + if lenArgs == 1 { + jf.set(buf, singleArgKey, args[0]) + } else if lenArgs%2 == 0 { + for i := 0; i < lenArgs; i += 2 { + if key, ok := args[i].(string); ok { + if key == "" { + // show key is invalid + jf.set(buf, badKeyAtIndex(i), args[i+1]) + } else { + jf.set(buf, key, args[i+1]) + } + } else { + // show key is invalid + jf.set(buf, badKeyAtIndex(i), args[i+1]) + } + } + } else { + jf.set(buf, warnImbalancedKey, args) + } + } + buf.WriteString("}\n") + buf.WriteTo(writer) +} + +// LogEntry returns the JSON log entry object built by Format(). Used by +// HappyDevFormatter to ensure any data logged while developing properly +// logs in production. +func (jf *JSONFormatter) LogEntry(level int, msg string, args []interface{}) map[string]interface{} { + buf := pool.Get() + defer pool.Put(buf) + jf.Format(buf, level, msg, args) + var entry map[string]interface{} + err := json.Unmarshal(buf.Bytes(), &entry) + if err != nil { + panic("Unable to unmarhsal entry from JSONFormatter: " + err.Error() + " \"" + string(buf.Bytes()) + "\"") + } + return entry +} diff --git a/vendor/github.com/mgutz/logxi/v1/logger.go b/vendor/github.com/mgutz/logxi/v1/logger.go new file mode 100644 index 00000000..113a38ac --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/logger.go @@ -0,0 +1,153 @@ +package log + +/* +http://en.wikipedia.org/wiki/Syslog + +Code Severity Keyword +0 Emergency emerg (panic) System is unusable. + + A "panic" condition usually affecting multiple apps/servers/sites. At this + level it would usually notify all tech staff on call. + +1 Alert alert Action must be taken immediately. + + Should be corrected immediately, therefore notify staff who can fix the + problem. An example would be the loss of a primary ISP connection. + +2 Critical crit Critical conditions. + + Should be corrected immediately, but indicates failure in a secondary + system, an example is a loss of a backup ISP connection. + +3 Error err (error) Error conditions. + + Non-urgent failures, these should be relayed to developers or admins; each + item must be resolved within a given time. + +4 Warning warning (warn) Warning conditions. + + Warning messages, not an error, but indication that an error will occur if + action is not taken, e.g. file system 85% full - each item must be resolved + within a given time. + +5 Notice notice Normal but significant condition. + + Events that are unusual but not error conditions - might be summarized in + an email to developers or admins to spot potential problems - no immediate + action required. + +6 Informational info Informational messages. + + Normal operational messages - may be harvested for reporting, measuring + throughput, etc. - no action required. + +7 Debug debug Debug-level messages. + + Info useful to developers for debugging the application, not useful during operations. +*/ + +const ( + // LevelEnv chooses level from LOGXI environment variable or defaults + // to LevelInfo + LevelEnv = -10000 + + // LevelOff means logging is disabled for logger. This should always + // be first + LevelOff = -1000 + + // LevelEmergency is usually 0 but that is also the "zero" value + // for Go, which means whenever we do any lookup in string -> int + // map 0 is returned (not good). + LevelEmergency = -1 + + // LevelAlert means action must be taken immediately. + LevelAlert = 1 + + // LevelFatal means it should be corrected immediately, eg cannot connect to database. + LevelFatal = 2 + + // LevelCritical is alias for LevelFatal + LevelCritical = 2 + + // LevelError is a non-urgen failure to notify devlopers or admins + LevelError = 3 + + // LevelWarn indiates an error will occur if action is not taken, eg file system 85% full + LevelWarn = 4 + + // LevelNotice is normal but significant condition. + LevelNotice = 5 + + // LevelInfo is info level + LevelInfo = 6 + + // LevelDebug is debug level + LevelDebug = 7 + + // LevelTrace is trace level and displays file and line in terminal + LevelTrace = 10 + + // LevelAll is all levels + LevelAll = 1000 +) + +// FormatHappy uses HappyDevFormatter +const FormatHappy = "happy" + +// FormatText uses TextFormatter +const FormatText = "text" + +// FormatJSON uses JSONFormatter +const FormatJSON = "JSON" + +// FormatEnv selects formatter based on LOGXI_FORMAT environment variable +const FormatEnv = "" + +// LevelMap maps int enums to string level. +var LevelMap = map[int]string{ + LevelFatal: "FTL", + LevelError: "ERR", + LevelWarn: "WRN", + LevelInfo: "INF", + LevelDebug: "DBG", + LevelTrace: "TRC", +} + +// LevelMap maps int enums to string level. +var LevelAtoi = map[string]int{ + "OFF": LevelOff, + "FTL": LevelFatal, + "ERR": LevelError, + "WRN": LevelWarn, + "INF": LevelInfo, + "DBG": LevelDebug, + "TRC": LevelTrace, + "ALL": LevelAll, + + "off": LevelOff, + "fatal": LevelFatal, + "error": LevelError, + "warn": LevelWarn, + "info": LevelInfo, + "debug": LevelDebug, + "trace": LevelTrace, + "all": LevelAll, +} + +// Logger is the interface for logging. +type Logger interface { + Trace(msg string, args ...interface{}) + Debug(msg string, args ...interface{}) + Info(msg string, args ...interface{}) + Warn(msg string, args ...interface{}) error + Error(msg string, args ...interface{}) error + Fatal(msg string, args ...interface{}) + Log(level int, msg string, args []interface{}) + + SetLevel(int) + IsTrace() bool + IsDebug() bool + IsInfo() bool + IsWarn() bool + // Error, Fatal not needed, those SHOULD always be logged +} diff --git a/vendor/github.com/mgutz/logxi/v1/logger_test.go b/vendor/github.com/mgutz/logxi/v1/logger_test.go new file mode 100644 index 00000000..d8bf4e4f --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/logger_test.go @@ -0,0 +1,359 @@ +package log + +import ( + "bytes" + "encoding/json" + "errors" + "os" + "regexp" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func processEnv() { + ProcessEnv(readFromEnviron()) +} + +func testResetEnv() { + disableColors = false + testBuf.Reset() + os.Clearenv() + processEnv() + InternalLog = testInternalLog +} + +func TestEnvLOGXI(t *testing.T) { + assert := assert.New(t) + + os.Setenv("LOGXI", "") + processEnv() + assert.Equal(LevelWarn, logxiNameLevelMap["*"], "Unset LOGXI defaults to *:WRN with TTY") + + // default all to ERR + os.Setenv("LOGXI", "*=ERR") + processEnv() + level := getLogLevel("mylog") + assert.Equal(LevelError, level) + level = getLogLevel("mylog2") + assert.Equal(LevelError, level) + + // unrecognized defaults to LevelDebug on TTY + os.Setenv("LOGXI", "mylog=badlevel") + processEnv() + level = getLogLevel("mylog") + assert.Equal(LevelWarn, level) + + // wildcard should not override exact match + os.Setenv("LOGXI", "*=WRN,mylog=ERR,other=OFF") + processEnv() + level = getLogLevel("mylog") + assert.Equal(LevelError, level) + level = getLogLevel("other") + assert.Equal(LevelOff, level) + + // wildcard pattern should match + os.Setenv("LOGXI", "*log=ERR") + processEnv() + level = getLogLevel("mylog") + assert.Equal(LevelError, level, "wildcat prefix should match") + + os.Setenv("LOGXI", "myx*=ERR") + processEnv() + level = getLogLevel("mylog") + assert.Equal(LevelError, level, "no match should return LevelError") + + os.Setenv("LOGXI", "myl*,-foo") + processEnv() + level = getLogLevel("mylog") + assert.Equal(LevelAll, level) + level = getLogLevel("foo") + assert.Equal(LevelOff, level) +} + +func TestEnvLOGXI_FORMAT(t *testing.T) { + assert := assert.New(t) + oldIsTerminal := isTerminal + + os.Setenv("LOGXI_FORMAT", "") + setDefaults(true) + processEnv() + assert.Equal(FormatHappy, logxiFormat, "terminal defaults to FormatHappy") + setDefaults(false) + processEnv() + assert.Equal(FormatJSON, logxiFormat, "non terminal defaults to FormatJSON") + + os.Setenv("LOGXI_FORMAT", "JSON") + processEnv() + assert.Equal(FormatJSON, logxiFormat) + + os.Setenv("LOGXI_FORMAT", "json") + setDefaults(true) + processEnv() + assert.Equal(FormatHappy, logxiFormat, "Mismatches defaults to FormatHappy") + setDefaults(false) + processEnv() + assert.Equal(FormatJSON, logxiFormat, "Mismatches defaults to FormatJSON non terminal") + + isTerminal = oldIsTerminal + setDefaults(isTerminal) +} + +func TestEnvLOGXI_COLORS(t *testing.T) { + oldIsTerminal := isTerminal + + os.Setenv("LOGXI_COLORS", "*=off") + setDefaults(true) + processEnv() + + var buf bytes.Buffer + l := NewLogger3(&buf, "telc", NewHappyDevFormatter("logxi-colors")) + l.SetLevel(LevelDebug) + l.Info("info") + + r := regexp.MustCompile(`^\d{2}:\d{2}:\d{2}\.\d{6} INF logxi-colors info`) + assert.True(t, r.Match(buf.Bytes())) + + setDefaults(true) + + isTerminal = oldIsTerminal + setDefaults(isTerminal) +} + +func TestComplexKeys(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger(&buf, "bench") + assert.Panics(t, func() { + l.Error("complex", "foo\n", 1) + }) + + assert.Panics(t, func() { + l.Error("complex", "foo\"s", 1) + }) + + l.Error("apos is ok", "foo's", 1) +} + +func TestJSON(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "bench", NewJSONFormatter("bench")) + l.SetLevel(LevelDebug) + l.Error("hello", "foo", "bar") + + var obj map[string]interface{} + err := json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Equal(t, "bar", obj["foo"].(string)) + assert.Equal(t, "hello", obj[KeyMap.Message].(string)) +} + +func TestJSONImbalanced(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "bench", NewJSONFormatter("bench")) + l.SetLevel(LevelDebug) + l.Error("hello", "foo", "bar", "bah") + + var obj map[string]interface{} + err := json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Exactly(t, []interface{}{"foo", "bar", "bah"}, obj[warnImbalancedKey]) + assert.Equal(t, "hello", obj[KeyMap.Message].(string)) +} + +func TestJSONNoArgs(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "bench", NewJSONFormatter("bench")) + l.SetLevel(LevelDebug) + l.Error("hello") + + var obj map[string]interface{} + err := json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Equal(t, "hello", obj[KeyMap.Message].(string)) +} + +func TestJSONNested(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "bench", NewJSONFormatter("bench")) + l.SetLevel(LevelDebug) + l.Error("hello", "obj", map[string]string{"fruit": "apple"}) + + var obj map[string]interface{} + err := json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Equal(t, "hello", obj[KeyMap.Message].(string)) + o := obj["obj"] + assert.Equal(t, "apple", o.(map[string]interface{})["fruit"].(string)) +} + +func TestJSONEscapeSequences(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "bench", NewJSONFormatter("bench")) + l.SetLevel(LevelDebug) + esc := "I said, \"a's \\ \\\b\f\n\r\t\x1a\"你好'; DELETE FROM people" + + var obj map[string]interface{} + // test as message + l.Error(esc) + err := json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Equal(t, esc, obj[KeyMap.Message].(string)) + + // test as key + buf.Reset() + key := "你好" + l.Error("as key", key, "esc") + err = json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Equal(t, "as key", obj[KeyMap.Message].(string)) + assert.Equal(t, "esc", obj[key].(string)) +} + +func TestKeyNotString(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "badkey", NewHappyDevFormatter("badkey")) + l.SetLevel(LevelDebug) + l.Debug("foo", 1) + assert.Panics(t, func() { + l.Debug("reserved key", "_t", "trying to use time") + }) +} + +func TestWarningErrorContext(t *testing.T) { + testResetEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "wrnerr", NewHappyDevFormatter("wrnerr")) + l.Warn("no keys") + l.Warn("has eys", "key1", 2) + l.Error("no keys") + l.Error("has keys", "key1", 2) +} + +func TestLevels(t *testing.T) { + var buf bytes.Buffer + l := NewLogger3(&buf, "bench", NewJSONFormatter("bench")) + + l.SetLevel(LevelFatal) + assert.False(t, l.IsWarn()) + assert.False(t, l.IsInfo()) + assert.False(t, l.IsTrace()) + assert.False(t, l.IsDebug()) + + l.SetLevel(LevelError) + assert.False(t, l.IsWarn()) + + l.SetLevel(LevelWarn) + assert.True(t, l.IsWarn()) + assert.False(t, l.IsDebug()) + + l.SetLevel(LevelInfo) + assert.True(t, l.IsInfo()) + assert.True(t, l.IsWarn()) + assert.False(t, l.IsDebug()) + + l.SetLevel(LevelDebug) + assert.True(t, l.IsDebug()) + assert.True(t, l.IsInfo()) + assert.False(t, l.IsTrace()) + + l.SetLevel(LevelTrace) + assert.True(t, l.IsTrace()) + assert.True(t, l.IsDebug()) +} + +func TestAllowSingleParam(t *testing.T) { + var buf bytes.Buffer + l := NewLogger3(&buf, "wrnerr", NewTextFormatter("wrnerr")) + l.SetLevel(LevelDebug) + l.Info("info", 1) + assert.True(t, strings.HasSuffix(buf.String(), singleArgKey+": 1\n")) + + buf.Reset() + l = NewLogger3(&buf, "wrnerr", NewHappyDevFormatter("wrnerr")) + l.SetLevel(LevelDebug) + l.Info("info", 1) + assert.True(t, strings.HasSuffix(buf.String(), "_: \x1b[0m1\n")) + + var obj map[string]interface{} + buf.Reset() + l = NewLogger3(&buf, "wrnerr", NewJSONFormatter("wrnerr")) + l.SetLevel(LevelDebug) + l.Info("info", 1) + err := json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Equal(t, float64(1), obj["_"]) +} + +func TestErrorOnWarn(t *testing.T) { + testResetEnv() + // os.Setenv("LOGXI_FORMAT", "context=2") + // processEnv() + var buf bytes.Buffer + l := NewLogger3(&buf, "wrnerr", NewHappyDevFormatter("wrnerr")) + l.SetLevel(LevelWarn) + + ErrorDummy := errors.New("dummy error") + + err := l.Warn("warn with error", "err", ErrorDummy) + assert.Error(t, err) + assert.Equal(t, "dummy error", err.Error()) + err = l.Warn("warn with no error", "one", 1) + assert.NoError(t, err) + //l.Error("error with err", "err", ErrorDummy) +} + +type CheckStringer struct { + s string +} + +func (cs CheckStringer) String() string { + return "bbb" +} + +func TestStringer(t *testing.T) { + f := CheckStringer{s: "aaa"} + + var buf bytes.Buffer + l := NewLogger3(&buf, "cs1", NewTextFormatter("stringer-text")) + l.SetLevel(LevelDebug) + l.Info("info", "f", f) + assert.True(t, strings.Contains(buf.String(), "bbb")) + + buf.Reset() + l = NewLogger3(&buf, "cs2", NewHappyDevFormatter("stringer-happy")) + l.SetLevel(LevelDebug) + l.Info("info", "f", f) + assert.True(t, strings.Contains(buf.String(), "bbb")) + + var obj map[string]interface{} + buf.Reset() + l = NewLogger3(&buf, "cs3", NewJSONFormatter("stringer-json")) + l.SetLevel(LevelDebug) + l.Info("info", "f", f) + err := json.Unmarshal(buf.Bytes(), &obj) + assert.NoError(t, err) + assert.Equal(t, "bbb", obj["f"]) +} + +// When log functions cast pointers to interface{}. +// Say p is a pointer set to nil: +// +// interface{}(p) == nil // this is false +// +// Casting it to interface{} makes it trickier to test whether its nil. +func TestStringerNullPointers(t *testing.T) { + var f *CheckStringer + var buf bytes.Buffer + l := NewLogger3(&buf, "cs1", NewJSONFormatter("stringer-json")) + l.SetLevel(LevelDebug) + l.Info("info", "f", f) + assert.Contains(t, buf.String(), "null") +} \ No newline at end of file diff --git a/vendor/github.com/mgutz/logxi/v1/methods.go b/vendor/github.com/mgutz/logxi/v1/methods.go new file mode 100644 index 00000000..7297b90c --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/methods.go @@ -0,0 +1,51 @@ +package log + +// Trace logs a trace statement. On terminals file and line number are logged. +func Trace(msg string, args ...interface{}) { + DefaultLog.Trace(msg, args...) +} + +// Debug logs a debug statement. +func Debug(msg string, args ...interface{}) { + DefaultLog.Debug(msg, args...) +} + +// Info logs an info statement. +func Info(msg string, args ...interface{}) { + DefaultLog.Info(msg, args...) +} + +// Warn logs a warning statement. On terminals it logs file and line number. +func Warn(msg string, args ...interface{}) { + DefaultLog.Warn(msg, args...) +} + +// Error logs an error statement with callstack. +func Error(msg string, args ...interface{}) { + DefaultLog.Error(msg, args...) +} + +// Fatal logs a fatal statement. +func Fatal(msg string, args ...interface{}) { + DefaultLog.Fatal(msg, args...) +} + +// IsTrace determines if this logger logs a trace statement. +func IsTrace() bool { + return DefaultLog.IsTrace() +} + +// IsDebug determines if this logger logs a debug statement. +func IsDebug() bool { + return DefaultLog.IsDebug() +} + +// IsInfo determines if this logger logs an info statement. +func IsInfo() bool { + return DefaultLog.IsInfo() +} + +// IsWarn determines if this logger logs a warning statement. +func IsWarn() bool { + return DefaultLog.IsWarn() +} diff --git a/vendor/github.com/mgutz/logxi/v1/nullLogger.go b/vendor/github.com/mgutz/logxi/v1/nullLogger.go new file mode 100644 index 00000000..8da91875 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/nullLogger.go @@ -0,0 +1,66 @@ +package log + +// NullLog is a noop logger. Think of it as /dev/null. +var NullLog = &NullLogger{} + +// NullLogger is the default logger for this package. +type NullLogger struct{} + +// Trace logs a debug entry. +func (l *NullLogger) Trace(msg string, args ...interface{}) { +} + +// Debug logs a debug entry. +func (l *NullLogger) Debug(msg string, args ...interface{}) { +} + +// Info logs an info entry. +func (l *NullLogger) Info(msg string, args ...interface{}) { +} + +// Warn logs a warn entry. +func (l *NullLogger) Warn(msg string, args ...interface{}) error { + return nil +} + +// Error logs an error entry. +func (l *NullLogger) Error(msg string, args ...interface{}) error { + return nil +} + +// Fatal logs a fatal entry then panics. +func (l *NullLogger) Fatal(msg string, args ...interface{}) { + panic("exit due to fatal error") +} + +// Log logs a leveled entry. +func (l *NullLogger) Log(level int, msg string, args []interface{}) { +} + +// IsTrace determines if this logger logs a trace statement. +func (l *NullLogger) IsTrace() bool { + return false +} + +// IsDebug determines if this logger logs a debug statement. +func (l *NullLogger) IsDebug() bool { + return false +} + +// IsInfo determines if this logger logs an info statement. +func (l *NullLogger) IsInfo() bool { + return false +} + +// IsWarn determines if this logger logs a warning statement. +func (l *NullLogger) IsWarn() bool { + return false +} + +// SetLevel sets the level of this logger. +func (l *NullLogger) SetLevel(level int) { +} + +// SetFormatter set the formatter for this logger. +func (l *NullLogger) SetFormatter(formatter Formatter) { +} diff --git a/vendor/github.com/mgutz/logxi/v1/pool.go b/vendor/github.com/mgutz/logxi/v1/pool.go new file mode 100644 index 00000000..3f06bfed --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/pool.go @@ -0,0 +1,29 @@ +package log + +import ( + "bytes" + "sync" +) + +type BufferPool struct { + sync.Pool +} + +func NewBufferPool() *BufferPool { + return &BufferPool{ + Pool: sync.Pool{New: func() interface{} { + b := bytes.NewBuffer(make([]byte, 128)) + b.Reset() + return b + }}, + } +} + +func (bp *BufferPool) Get() *bytes.Buffer { + return bp.Pool.Get().(*bytes.Buffer) +} + +func (bp *BufferPool) Put(b *bytes.Buffer) { + b.Reset() + bp.Pool.Put(b) +} diff --git a/vendor/github.com/mgutz/logxi/v1/textFormatter.go b/vendor/github.com/mgutz/logxi/v1/textFormatter.go new file mode 100644 index 00000000..f5be9ad4 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/textFormatter.go @@ -0,0 +1,107 @@ +package log + +import ( + "fmt" + "io" + "runtime/debug" + "time" +) + +// Formatter records log entries. +type Formatter interface { + Format(writer io.Writer, level int, msg string, args []interface{}) +} + +// TextFormatter is the default recorder used if one is unspecified when +// creating a new Logger. +type TextFormatter struct { + name string + itoaLevelMap map[int]string + timeLabel string +} + +// NewTextFormatter returns a new instance of TextFormatter. SetName +// must be called befored using it. +func NewTextFormatter(name string) *TextFormatter { + timeLabel := KeyMap.Time + AssignmentChar + levelLabel := Separator + KeyMap.Level + AssignmentChar + messageLabel := Separator + KeyMap.Message + AssignmentChar + nameLabel := Separator + KeyMap.Name + AssignmentChar + pidLabel := Separator + KeyMap.PID + AssignmentChar + + var buildKV = func(level string) string { + buf := pool.Get() + defer pool.Put(buf) + + buf.WriteString(pidLabel) + buf.WriteString(pidStr) + + //buf.WriteString(Separator) + buf.WriteString(nameLabel) + buf.WriteString(name) + + //buf.WriteString(Separator) + buf.WriteString(levelLabel) + buf.WriteString(level) + + //buf.WriteString(Separator) + buf.WriteString(messageLabel) + + return buf.String() + } + itoaLevelMap := map[int]string{ + LevelDebug: buildKV(LevelMap[LevelDebug]), + LevelWarn: buildKV(LevelMap[LevelWarn]), + LevelInfo: buildKV(LevelMap[LevelInfo]), + LevelError: buildKV(LevelMap[LevelError]), + LevelFatal: buildKV(LevelMap[LevelFatal]), + } + return &TextFormatter{itoaLevelMap: itoaLevelMap, name: name, timeLabel: timeLabel} +} + +func (tf *TextFormatter) set(buf bufferWriter, key string, val interface{}) { + buf.WriteString(Separator) + buf.WriteString(key) + buf.WriteString(AssignmentChar) + if err, ok := val.(error); ok { + buf.WriteString(err.Error()) + buf.WriteRune('\n') + buf.WriteString(string(debug.Stack())) + return + } + buf.WriteString(fmt.Sprintf("%v", val)) +} + +// Format records a log entry. +func (tf *TextFormatter) Format(writer io.Writer, level int, msg string, args []interface{}) { + buf := pool.Get() + defer pool.Put(buf) + buf.WriteString(tf.timeLabel) + buf.WriteString(time.Now().Format(timeFormat)) + buf.WriteString(tf.itoaLevelMap[level]) + buf.WriteString(msg) + var lenArgs = len(args) + if lenArgs > 0 { + if lenArgs == 1 { + tf.set(buf, singleArgKey, args[0]) + } else if lenArgs%2 == 0 { + for i := 0; i < lenArgs; i += 2 { + if key, ok := args[i].(string); ok { + if key == "" { + // show key is invalid + tf.set(buf, badKeyAtIndex(i), args[i+1]) + } else { + tf.set(buf, key, args[i+1]) + } + } else { + // show key is invalid + tf.set(buf, badKeyAtIndex(i), args[i+1]) + } + } + } else { + tf.set(buf, warnImbalancedKey, args) + } + } + buf.WriteRune('\n') + buf.WriteTo(writer) +} diff --git a/vendor/github.com/mgutz/logxi/v1/util.go b/vendor/github.com/mgutz/logxi/v1/util.go new file mode 100644 index 00000000..22f31302 --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/util.go @@ -0,0 +1,53 @@ +package log + +import ( + "path/filepath" + "strings" +) + +func expandTabs(s string, tabLen int) string { + if s == "" { + return s + } + parts := strings.Split(s, "\t") + buf := pool.Get() + defer pool.Put(buf) + for _, part := range parts { + buf.WriteString(part) + buf.WriteString(strings.Repeat(" ", tabLen-len(part)%tabLen)) + } + return buf.String() +} + +func maxInt(a, b int) int { + if a > b { + return a + } + return b +} +func minInt(a, b int) int { + if a < b { + return a + } + return b +} + +func indexOfNonSpace(s string) int { + if s == "" { + return -1 + } + for i, r := range s { + if r != ' ' { + return i + } + } + return -1 +} + +var inLogxiPath = filepath.Join("mgutz", "logxi", "v"+strings.Split(Version, ".")[0]) + +func isLogxiCode(filename string) bool { + // need to see errors in tests + return strings.HasSuffix(filepath.Dir(filename), inLogxiPath) && + !strings.HasSuffix(filename, "_test.go") +} diff --git a/vendor/github.com/mgutz/logxi/v1/version.go b/vendor/github.com/mgutz/logxi/v1/version.go new file mode 100644 index 00000000..a7ec7b0e --- /dev/null +++ b/vendor/github.com/mgutz/logxi/v1/version.go @@ -0,0 +1,4 @@ +package log + +// Version is the version of this package +const Version = "1.0.0-pre" diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 00000000..15e5a192 --- /dev/null +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,14 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 00000000..835ba3e7 --- /dev/null +++ b/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md new file mode 100644 index 00000000..6483ba2a --- /dev/null +++ b/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## License + +BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 00000000..a932eade --- /dev/null +++ b/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/vendor/github.com/pkg/errors/bench_test.go b/vendor/github.com/pkg/errors/bench_test.go new file mode 100644 index 00000000..903b5f2d --- /dev/null +++ b/vendor/github.com/pkg/errors/bench_test.go @@ -0,0 +1,63 @@ +// +build go1.7 + +package errors + +import ( + "fmt" + "testing" + + stderrors "errors" +) + +func noErrors(at, depth int) error { + if at >= depth { + return stderrors.New("no error") + } + return noErrors(at+1, depth) +} + +func yesErrors(at, depth int) error { + if at >= depth { + return New("ye error") + } + return yesErrors(at+1, depth) +} + +// GlobalE is an exported global to store the result of benchmark results, +// preventing the compiler from optimising the benchmark functions away. +var GlobalE error + +func BenchmarkErrors(b *testing.B) { + type run struct { + stack int + std bool + } + runs := []run{ + {10, false}, + {10, true}, + {100, false}, + {100, true}, + {1000, false}, + {1000, true}, + } + for _, r := range runs { + part := "pkg/errors" + if r.std { + part = "errors" + } + name := fmt.Sprintf("%s-stack-%d", part, r.stack) + b.Run(name, func(b *testing.B) { + var err error + f := yesErrors + if r.std { + f = noErrors + } + b.ReportAllocs() + for i := 0; i < b.N; i++ { + err = f(0, r.stack) + } + b.StopTimer() + GlobalE = err + }) + } +} diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 00000000..842ee804 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,269 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required the errors.WithStack and errors.WithMessage +// functions destructure errors.Wrap into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface. +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// stackTracer interface is not exported by this package, but is considered a part +// of stable public API. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/vendor/github.com/pkg/errors/errors_test.go b/vendor/github.com/pkg/errors/errors_test.go new file mode 100644 index 00000000..c4e6eef6 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors_test.go @@ -0,0 +1,225 @@ +package errors + +import ( + "errors" + "fmt" + "io" + "reflect" + "testing" +) + +func TestNew(t *testing.T) { + tests := []struct { + err string + want error + }{ + {"", fmt.Errorf("")}, + {"foo", fmt.Errorf("foo")}, + {"foo", New("foo")}, + {"string with format specifiers: %v", errors.New("string with format specifiers: %v")}, + } + + for _, tt := range tests { + got := New(tt.err) + if got.Error() != tt.want.Error() { + t.Errorf("New.Error(): got: %q, want %q", got, tt.want) + } + } +} + +func TestWrapNil(t *testing.T) { + got := Wrap(nil, "no error") + if got != nil { + t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWrap(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"}, + } + + for _, tt := range tests { + got := Wrap(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) + } + } +} + +type nilError struct{} + +func (nilError) Error() string { return "nil error" } + +func TestCause(t *testing.T) { + x := New("error") + tests := []struct { + err error + want error + }{{ + // nil error is nil + err: nil, + want: nil, + }, { + // explicit nil error is nil + err: (error)(nil), + want: nil, + }, { + // typed nil is nil + err: (*nilError)(nil), + want: (*nilError)(nil), + }, { + // uncaused error is unaffected + err: io.EOF, + want: io.EOF, + }, { + // caused error returns cause + err: Wrap(io.EOF, "ignored"), + want: io.EOF, + }, { + err: x, // return from errors.New + want: x, + }, { + WithMessage(nil, "whoops"), + nil, + }, { + WithMessage(io.EOF, "whoops"), + io.EOF, + }, { + WithStack(nil), + nil, + }, { + WithStack(io.EOF), + io.EOF, + }} + + for i, tt := range tests { + got := Cause(tt.err) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) + } + } +} + +func TestWrapfNil(t *testing.T) { + got := Wrapf(nil, "no error") + if got != nil { + t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWrapf(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"}, + {Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"}, + } + + for _, tt := range tests { + got := Wrapf(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) + } + } +} + +func TestErrorf(t *testing.T) { + tests := []struct { + err error + want string + }{ + {Errorf("read error without format specifiers"), "read error without format specifiers"}, + {Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"}, + } + + for _, tt := range tests { + got := tt.err.Error() + if got != tt.want { + t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want) + } + } +} + +func TestWithStackNil(t *testing.T) { + got := WithStack(nil) + if got != nil { + t.Errorf("WithStack(nil): got %#v, expected nil", got) + } +} + +func TestWithStack(t *testing.T) { + tests := []struct { + err error + want string + }{ + {io.EOF, "EOF"}, + {WithStack(io.EOF), "EOF"}, + } + + for _, tt := range tests { + got := WithStack(tt.err).Error() + if got != tt.want { + t.Errorf("WithStack(%v): got: %v, want %v", tt.err, got, tt.want) + } + } +} + +func TestWithMessageNil(t *testing.T) { + got := WithMessage(nil, "no error") + if got != nil { + t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got) + } +} + +func TestWithMessage(t *testing.T) { + tests := []struct { + err error + message string + want string + }{ + {io.EOF, "read error", "read error: EOF"}, + {WithMessage(io.EOF, "read error"), "client error", "client error: read error: EOF"}, + } + + for _, tt := range tests { + got := WithMessage(tt.err, tt.message).Error() + if got != tt.want { + t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want) + } + } +} + +// errors.New, etc values are not expected to be compared by value +// but the change in errors#27 made them incomparable. Assert that +// various kinds of errors have a functional equality operator, even +// if the result of that equality is always false. +func TestErrorEquality(t *testing.T) { + vals := []error{ + nil, + io.EOF, + errors.New("EOF"), + New("EOF"), + Errorf("EOF"), + Wrap(io.EOF, "EOF"), + Wrapf(io.EOF, "EOF%d", 2), + WithMessage(nil, "whoops"), + WithMessage(io.EOF, "whoops"), + WithStack(io.EOF), + WithStack(nil), + } + + for i := range vals { + for j := range vals { + _ = vals[i] == vals[j] // mustn't panic + } + } +} diff --git a/vendor/github.com/pkg/errors/example_test.go b/vendor/github.com/pkg/errors/example_test.go new file mode 100644 index 00000000..c1fc13e3 --- /dev/null +++ b/vendor/github.com/pkg/errors/example_test.go @@ -0,0 +1,205 @@ +package errors_test + +import ( + "fmt" + + "github.com/pkg/errors" +) + +func ExampleNew() { + err := errors.New("whoops") + fmt.Println(err) + + // Output: whoops +} + +func ExampleNew_printf() { + err := errors.New("whoops") + fmt.Printf("%+v", err) + + // Example output: + // whoops + // github.com/pkg/errors_test.ExampleNew_printf + // /home/dfc/src/github.com/pkg/errors/example_test.go:17 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 +} + +func ExampleWithMessage() { + cause := errors.New("whoops") + err := errors.WithMessage(cause, "oh noes") + fmt.Println(err) + + // Output: oh noes: whoops +} + +func ExampleWithStack() { + cause := errors.New("whoops") + err := errors.WithStack(cause) + fmt.Println(err) + + // Output: whoops +} + +func ExampleWithStack_printf() { + cause := errors.New("whoops") + err := errors.WithStack(cause) + fmt.Printf("%+v", err) + + // Example Output: + // whoops + // github.com/pkg/errors_test.ExampleWithStack_printf + // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:55 + // testing.runExample + // /usr/lib/go/src/testing/example.go:114 + // testing.RunExamples + // /usr/lib/go/src/testing/example.go:38 + // testing.(*M).Run + // /usr/lib/go/src/testing/testing.go:744 + // main.main + // github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /usr/lib/go/src/runtime/proc.go:183 + // runtime.goexit + // /usr/lib/go/src/runtime/asm_amd64.s:2086 + // github.com/pkg/errors_test.ExampleWithStack_printf + // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:56 + // testing.runExample + // /usr/lib/go/src/testing/example.go:114 + // testing.RunExamples + // /usr/lib/go/src/testing/example.go:38 + // testing.(*M).Run + // /usr/lib/go/src/testing/testing.go:744 + // main.main + // github.com/pkg/errors/_test/_testmain.go:106 + // runtime.main + // /usr/lib/go/src/runtime/proc.go:183 + // runtime.goexit + // /usr/lib/go/src/runtime/asm_amd64.s:2086 +} + +func ExampleWrap() { + cause := errors.New("whoops") + err := errors.Wrap(cause, "oh noes") + fmt.Println(err) + + // Output: oh noes: whoops +} + +func fn() error { + e1 := errors.New("error") + e2 := errors.Wrap(e1, "inner") + e3 := errors.Wrap(e2, "middle") + return errors.Wrap(e3, "outer") +} + +func ExampleCause() { + err := fn() + fmt.Println(err) + fmt.Println(errors.Cause(err)) + + // Output: outer: middle: inner: error + // error +} + +func ExampleWrap_extended() { + err := fn() + fmt.Printf("%+v\n", err) + + // Example output: + // error + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:47 + // github.com/pkg/errors_test.ExampleCause_printf + // /home/dfc/src/github.com/pkg/errors/example_test.go:63 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:104 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer +} + +func ExampleWrapf() { + cause := errors.New("whoops") + err := errors.Wrapf(cause, "oh noes #%d", 2) + fmt.Println(err) + + // Output: oh noes #2: whoops +} + +func ExampleErrorf_extended() { + err := errors.Errorf("whoops: %s", "foo") + fmt.Printf("%+v", err) + + // Example output: + // whoops: foo + // github.com/pkg/errors_test.ExampleErrorf + // /home/dfc/src/github.com/pkg/errors/example_test.go:101 + // testing.runExample + // /home/dfc/go/src/testing/example.go:114 + // testing.RunExamples + // /home/dfc/go/src/testing/example.go:38 + // testing.(*M).Run + // /home/dfc/go/src/testing/testing.go:744 + // main.main + // /github.com/pkg/errors/_test/_testmain.go:102 + // runtime.main + // /home/dfc/go/src/runtime/proc.go:183 + // runtime.goexit + // /home/dfc/go/src/runtime/asm_amd64.s:2059 +} + +func Example_stackTrace() { + type stackTracer interface { + StackTrace() errors.StackTrace + } + + err, ok := errors.Cause(fn()).(stackTracer) + if !ok { + panic("oops, err does not implement stackTracer") + } + + st := err.StackTrace() + fmt.Printf("%+v", st[0:2]) // top two frames + + // Example output: + // github.com/pkg/errors_test.fn + // /home/dfc/src/github.com/pkg/errors/example_test.go:47 + // github.com/pkg/errors_test.Example_stackTrace + // /home/dfc/src/github.com/pkg/errors/example_test.go:127 +} + +func ExampleCause_printf() { + err := errors.Wrap(func() error { + return func() error { + return errors.Errorf("hello %s", fmt.Sprintf("world")) + }() + }(), "failed") + + fmt.Printf("%v", err) + + // Output: failed: hello world +} diff --git a/vendor/github.com/pkg/errors/format_test.go b/vendor/github.com/pkg/errors/format_test.go new file mode 100644 index 00000000..c2eef5f0 --- /dev/null +++ b/vendor/github.com/pkg/errors/format_test.go @@ -0,0 +1,535 @@ +package errors + +import ( + "errors" + "fmt" + "io" + "regexp" + "strings" + "testing" +) + +func TestFormatNew(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + New("error"), + "%s", + "error", + }, { + New("error"), + "%v", + "error", + }, { + New("error"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatNew\n" + + "\t.+/github.com/pkg/errors/format_test.go:26", + }, { + New("error"), + "%q", + `"error"`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatErrorf(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Errorf("%s", "error"), + "%s", + "error", + }, { + Errorf("%s", "error"), + "%v", + "error", + }, { + Errorf("%s", "error"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatErrorf\n" + + "\t.+/github.com/pkg/errors/format_test.go:56", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWrap(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Wrap(New("error"), "error2"), + "%s", + "error2: error", + }, { + Wrap(New("error"), "error2"), + "%v", + "error2: error", + }, { + Wrap(New("error"), "error2"), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:82", + }, { + Wrap(io.EOF, "error"), + "%s", + "error: EOF", + }, { + Wrap(io.EOF, "error"), + "%v", + "error: EOF", + }, { + Wrap(io.EOF, "error"), + "%+v", + "EOF\n" + + "error\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:96", + }, { + Wrap(Wrap(io.EOF, "error1"), "error2"), + "%+v", + "EOF\n" + + "error1\n" + + "github.com/pkg/errors.TestFormatWrap\n" + + "\t.+/github.com/pkg/errors/format_test.go:103\n", + }, { + Wrap(New("error with space"), "context"), + "%q", + `"context: error with space"`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWrapf(t *testing.T) { + tests := []struct { + error + format string + want string + }{{ + Wrapf(io.EOF, "error%d", 2), + "%s", + "error2: EOF", + }, { + Wrapf(io.EOF, "error%d", 2), + "%v", + "error2: EOF", + }, { + Wrapf(io.EOF, "error%d", 2), + "%+v", + "EOF\n" + + "error2\n" + + "github.com/pkg/errors.TestFormatWrapf\n" + + "\t.+/github.com/pkg/errors/format_test.go:134", + }, { + Wrapf(New("error"), "error%d", 2), + "%s", + "error2: error", + }, { + Wrapf(New("error"), "error%d", 2), + "%v", + "error2: error", + }, { + Wrapf(New("error"), "error%d", 2), + "%+v", + "error\n" + + "github.com/pkg/errors.TestFormatWrapf\n" + + "\t.+/github.com/pkg/errors/format_test.go:149", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.error, tt.format, tt.want) + } +} + +func TestFormatWithStack(t *testing.T) { + tests := []struct { + error + format string + want []string + }{{ + WithStack(io.EOF), + "%s", + []string{"EOF"}, + }, { + WithStack(io.EOF), + "%v", + []string{"EOF"}, + }, { + WithStack(io.EOF), + "%+v", + []string{"EOF", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:175"}, + }, { + WithStack(New("error")), + "%s", + []string{"error"}, + }, { + WithStack(New("error")), + "%v", + []string{"error"}, + }, { + WithStack(New("error")), + "%+v", + []string{"error", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:189", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:189"}, + }, { + WithStack(WithStack(io.EOF)), + "%+v", + []string{"EOF", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:197", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:197"}, + }, { + WithStack(WithStack(Wrapf(io.EOF, "message"))), + "%+v", + []string{"EOF", + "message", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:205"}, + }, { + WithStack(Errorf("error%d", 1)), + "%+v", + []string{"error1", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:216", + "github.com/pkg/errors.TestFormatWithStack\n" + + "\t.+/github.com/pkg/errors/format_test.go:216"}, + }} + + for i, tt := range tests { + testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) + } +} + +func TestFormatWithMessage(t *testing.T) { + tests := []struct { + error + format string + want []string + }{{ + WithMessage(New("error"), "error2"), + "%s", + []string{"error2: error"}, + }, { + WithMessage(New("error"), "error2"), + "%v", + []string{"error2: error"}, + }, { + WithMessage(New("error"), "error2"), + "%+v", + []string{ + "error", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:244", + "error2"}, + }, { + WithMessage(io.EOF, "addition1"), + "%s", + []string{"addition1: EOF"}, + }, { + WithMessage(io.EOF, "addition1"), + "%v", + []string{"addition1: EOF"}, + }, { + WithMessage(io.EOF, "addition1"), + "%+v", + []string{"EOF", "addition1"}, + }, { + WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), + "%v", + []string{"addition2: addition1: EOF"}, + }, { + WithMessage(WithMessage(io.EOF, "addition1"), "addition2"), + "%+v", + []string{"EOF", "addition1", "addition2"}, + }, { + Wrap(WithMessage(io.EOF, "error1"), "error2"), + "%+v", + []string{"EOF", "error1", "error2", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:272"}, + }, { + WithMessage(Errorf("error%d", 1), "error2"), + "%+v", + []string{"error1", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:278", + "error2"}, + }, { + WithMessage(WithStack(io.EOF), "error"), + "%+v", + []string{ + "EOF", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:285", + "error"}, + }, { + WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"), + "%+v", + []string{ + "EOF", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:293", + "inside-error", + "github.com/pkg/errors.TestFormatWithMessage\n" + + "\t.+/github.com/pkg/errors/format_test.go:293", + "outside-error"}, + }} + + for i, tt := range tests { + testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true) + } +} + +func TestFormatGeneric(t *testing.T) { + starts := []struct { + err error + want []string + }{ + {New("new-error"), []string{ + "new-error", + "github.com/pkg/errors.TestFormatGeneric\n" + + "\t.+/github.com/pkg/errors/format_test.go:315"}, + }, {Errorf("errorf-error"), []string{ + "errorf-error", + "github.com/pkg/errors.TestFormatGeneric\n" + + "\t.+/github.com/pkg/errors/format_test.go:319"}, + }, {errors.New("errors-new-error"), []string{ + "errors-new-error"}, + }, + } + + wrappers := []wrapper{ + { + func(err error) error { return WithMessage(err, "with-message") }, + []string{"with-message"}, + }, { + func(err error) error { return WithStack(err) }, + []string{ + "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" + + ".+/github.com/pkg/errors/format_test.go:333", + }, + }, { + func(err error) error { return Wrap(err, "wrap-error") }, + []string{ + "wrap-error", + "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" + + ".+/github.com/pkg/errors/format_test.go:339", + }, + }, { + func(err error) error { return Wrapf(err, "wrapf-error%d", 1) }, + []string{ + "wrapf-error1", + "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" + + ".+/github.com/pkg/errors/format_test.go:346", + }, + }, + } + + for s := range starts { + err := starts[s].err + want := starts[s].want + testFormatCompleteCompare(t, s, err, "%+v", want, false) + testGenericRecursive(t, err, want, wrappers, 3) + } +} + +func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) { + got := fmt.Sprintf(format, arg) + gotLines := strings.SplitN(got, "\n", -1) + wantLines := strings.SplitN(want, "\n", -1) + + if len(wantLines) > len(gotLines) { + t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want) + return + } + + for i, w := range wantLines { + match, err := regexp.MatchString(w, gotLines[i]) + if err != nil { + t.Fatal(err) + } + if !match { + t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want) + } + } +} + +var stackLineR = regexp.MustCompile(`\.`) + +// parseBlocks parses input into a slice, where: +// - incase entry contains a newline, its a stacktrace +// - incase entry contains no newline, its a solo line. +// +// Detecting stack boundaries only works incase the WithStack-calls are +// to be found on the same line, thats why it is optionally here. +// +// Example use: +// +// for _, e := range blocks { +// if strings.ContainsAny(e, "\n") { +// // Match as stack +// } else { +// // Match as line +// } +// } +// +func parseBlocks(input string, detectStackboundaries bool) ([]string, error) { + var blocks []string + + stack := "" + wasStack := false + lines := map[string]bool{} // already found lines + + for _, l := range strings.Split(input, "\n") { + isStackLine := stackLineR.MatchString(l) + + switch { + case !isStackLine && wasStack: + blocks = append(blocks, stack, l) + stack = "" + lines = map[string]bool{} + case isStackLine: + if wasStack { + // Detecting two stacks after another, possible cause lines match in + // our tests due to WithStack(WithStack(io.EOF)) on same line. + if detectStackboundaries { + if lines[l] { + if len(stack) == 0 { + return nil, errors.New("len of block must not be zero here") + } + + blocks = append(blocks, stack) + stack = l + lines = map[string]bool{l: true} + continue + } + } + + stack = stack + "\n" + l + } else { + stack = l + } + lines[l] = true + case !isStackLine && !wasStack: + blocks = append(blocks, l) + default: + return nil, errors.New("must not happen") + } + + wasStack = isStackLine + } + + // Use up stack + if stack != "" { + blocks = append(blocks, stack) + } + return blocks, nil +} + +func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) { + gotStr := fmt.Sprintf(format, arg) + + got, err := parseBlocks(gotStr, detectStackBoundaries) + if err != nil { + t.Fatal(err) + } + + if len(got) != len(want) { + t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s\nwant: %s\ngotStr: %q", + n+1, format, len(got), len(want), prettyBlocks(got), prettyBlocks(want), gotStr) + } + + for i := range got { + if strings.ContainsAny(want[i], "\n") { + // Match as stack + match, err := regexp.MatchString(want[i], got[i]) + if err != nil { + t.Fatal(err) + } + if !match { + t.Fatalf("test %d: block %d: fmt.Sprintf(%q, err):\ngot:\n%q\nwant:\n%q\nall-got:\n%s\nall-want:\n%s\n", + n+1, i+1, format, got[i], want[i], prettyBlocks(got), prettyBlocks(want)) + } + } else { + // Match as message + if got[i] != want[i] { + t.Fatalf("test %d: fmt.Sprintf(%s, err) at block %d got != want:\n got: %q\nwant: %q", n+1, format, i+1, got[i], want[i]) + } + } + } +} + +type wrapper struct { + wrap func(err error) error + want []string +} + +func prettyBlocks(blocks []string) string { + var out []string + + for _, b := range blocks { + out = append(out, fmt.Sprintf("%v", b)) + } + + return " " + strings.Join(out, "\n ") +} + +func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) { + if len(beforeWant) == 0 { + panic("beforeWant must not be empty") + } + for _, w := range list { + if len(w.want) == 0 { + panic("want must not be empty") + } + + err := w.wrap(beforeErr) + + // Copy required cause append(beforeWant, ..) modified beforeWant subtly. + beforeCopy := make([]string, len(beforeWant)) + copy(beforeCopy, beforeWant) + + beforeWant := beforeCopy + last := len(beforeWant) - 1 + var want []string + + // Merge two stacks behind each other. + if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") { + want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...) + } else { + want = append(beforeWant, w.want...) + } + + testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false) + if maxDepth > 0 { + testGenericRecursive(t, err, want, list, maxDepth-1) + } + } +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 00000000..2874a048 --- /dev/null +++ b/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,147 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/vendor/github.com/pkg/errors/stack_test.go b/vendor/github.com/pkg/errors/stack_test.go new file mode 100644 index 00000000..85fc4195 --- /dev/null +++ b/vendor/github.com/pkg/errors/stack_test.go @@ -0,0 +1,274 @@ +package errors + +import ( + "fmt" + "runtime" + "testing" +) + +var initpc, _, _, _ = runtime.Caller(0) + +func TestFrameLine(t *testing.T) { + var tests = []struct { + Frame + want int + }{{ + Frame(initpc), + 9, + }, { + func() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) + }(), + 20, + }, { + func() Frame { + var pc, _, _, _ = runtime.Caller(1) + return Frame(pc) + }(), + 28, + }, { + Frame(0), // invalid PC + 0, + }} + + for _, tt := range tests { + got := tt.Frame.line() + want := tt.want + if want != got { + t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got) + } + } +} + +type X struct{} + +func (x X) val() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) +} + +func (x *X) ptr() Frame { + var pc, _, _, _ = runtime.Caller(0) + return Frame(pc) +} + +func TestFrameFormat(t *testing.T) { + var tests = []struct { + Frame + format string + want string + }{{ + Frame(initpc), + "%s", + "stack_test.go", + }, { + Frame(initpc), + "%+s", + "github.com/pkg/errors.init\n" + + "\t.+/github.com/pkg/errors/stack_test.go", + }, { + Frame(0), + "%s", + "unknown", + }, { + Frame(0), + "%+s", + "unknown", + }, { + Frame(initpc), + "%d", + "9", + }, { + Frame(0), + "%d", + "0", + }, { + Frame(initpc), + "%n", + "init", + }, { + func() Frame { + var x X + return x.ptr() + }(), + "%n", + `\(\*X\).ptr`, + }, { + func() Frame { + var x X + return x.val() + }(), + "%n", + "X.val", + }, { + Frame(0), + "%n", + "", + }, { + Frame(initpc), + "%v", + "stack_test.go:9", + }, { + Frame(initpc), + "%+v", + "github.com/pkg/errors.init\n" + + "\t.+/github.com/pkg/errors/stack_test.go:9", + }, { + Frame(0), + "%v", + "unknown:0", + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.Frame, tt.format, tt.want) + } +} + +func TestFuncname(t *testing.T) { + tests := []struct { + name, want string + }{ + {"", ""}, + {"runtime.main", "main"}, + {"github.com/pkg/errors.funcname", "funcname"}, + {"funcname", "funcname"}, + {"io.copyBuffer", "copyBuffer"}, + {"main.(*R).Write", "(*R).Write"}, + } + + for _, tt := range tests { + got := funcname(tt.name) + want := tt.want + if got != want { + t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) + } + } +} + +func TestStackTrace(t *testing.T) { + tests := []struct { + err error + want []string + }{{ + New("ooh"), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:154", + }, + }, { + Wrap(New("ooh"), "ahh"), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:159", // this is the stack of Wrap, not New + }, + }, { + Cause(Wrap(New("ooh"), "ahh")), []string{ + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:164", // this is the stack of New + }, + }, { + func() error { return New("ooh") }(), []string{ + `github.com/pkg/errors.(func·009|TestStackTrace.func1)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New's caller + }, + }, { + Cause(func() error { + return func() error { + return Errorf("hello %s", fmt.Sprintf("world")) + }() + }()), []string{ + `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:178", // this is the stack of Errorf + `github.com/pkg/errors.(func·011|TestStackTrace.func2)` + + "\n\t.+/github.com/pkg/errors/stack_test.go:179", // this is the stack of Errorf's caller + "github.com/pkg/errors.TestStackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:180", // this is the stack of Errorf's caller's caller + }, + }} + for i, tt := range tests { + x, ok := tt.err.(interface { + StackTrace() StackTrace + }) + if !ok { + t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err) + continue + } + st := x.StackTrace() + for j, want := range tt.want { + testFormatRegexp(t, i, st[j], "%+v", want) + } + } +} + +func stackTrace() StackTrace { + const depth = 8 + var pcs [depth]uintptr + n := runtime.Callers(1, pcs[:]) + var st stack = pcs[0:n] + return st.StackTrace() +} + +func TestStackTraceFormat(t *testing.T) { + tests := []struct { + StackTrace + format string + want string + }{{ + nil, + "%s", + `\[\]`, + }, { + nil, + "%v", + `\[\]`, + }, { + nil, + "%+v", + "", + }, { + nil, + "%#v", + `\[\]errors.Frame\(nil\)`, + }, { + make(StackTrace, 0), + "%s", + `\[\]`, + }, { + make(StackTrace, 0), + "%v", + `\[\]`, + }, { + make(StackTrace, 0), + "%+v", + "", + }, { + make(StackTrace, 0), + "%#v", + `\[\]errors.Frame{}`, + }, { + stackTrace()[:2], + "%s", + `\[stack_test.go stack_test.go\]`, + }, { + stackTrace()[:2], + "%v", + `\[stack_test.go:207 stack_test.go:254\]`, + }, { + stackTrace()[:2], + "%+v", + "\n" + + "github.com/pkg/errors.stackTrace\n" + + "\t.+/github.com/pkg/errors/stack_test.go:207\n" + + "github.com/pkg/errors.TestStackTraceFormat\n" + + "\t.+/github.com/pkg/errors/stack_test.go:258", + }, { + stackTrace()[:2], + "%#v", + `\[\]errors.Frame{stack_test.go:207, stack_test.go:266}`, + }} + + for i, tt := range tests { + testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) + } +} diff --git a/vendor/github.com/robertkrimen/otto/.gitignore b/vendor/github.com/robertkrimen/otto/.gitignore new file mode 100644 index 00000000..8c2a1694 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/.gitignore @@ -0,0 +1,5 @@ +/.test +/otto/otto +/otto/otto-* +/test/test-*.js +/test/tester diff --git a/vendor/github.com/robertkrimen/otto/DESIGN.markdown b/vendor/github.com/robertkrimen/otto/DESIGN.markdown new file mode 100644 index 00000000..28875298 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/DESIGN.markdown @@ -0,0 +1 @@ +* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.) diff --git a/vendor/github.com/robertkrimen/otto/LICENSE b/vendor/github.com/robertkrimen/otto/LICENSE new file mode 100644 index 00000000..b6179fe3 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2012 Robert Krimen + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/robertkrimen/otto/Makefile b/vendor/github.com/robertkrimen/otto/Makefile new file mode 100644 index 00000000..9868db3c --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/Makefile @@ -0,0 +1,63 @@ +.PHONY: test test-race test-release release release-check test-262 +.PHONY: parser +.PHONY: otto assets underscore + +TESTS := \ + ~ + +TEST := -v --run +TEST := -v +TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\) +TEST := . + +test: parser inline.go + go test -i + go test $(TEST) + @echo PASS + +parser: + $(MAKE) -C parser + +inline.go: inline.pl + ./$< > $@ + +################# +# release, test # +################# + +release: test-race test-release + for package in . parser token ast file underscore registry; do (cd $$package && godocdown --signature > README.markdown); done + @echo \*\*\* make release-check + @echo PASS + +release-check: .test + $(MAKE) -C test build test + $(MAKE) -C .test/test262 build test + @echo PASS + +test-262: .test + $(MAKE) -C .test/test262 build test + @echo PASS + +test-release: + go test -i + go test + +test-race: + go test -race -i + go test -race + +################################# +# otto, assets, underscore, ... # +################################# + +otto: + $(MAKE) -C otto + +assets: + mkdir -p .assets + for file in underscore/test/*.js; do tr "\`" "_" < $$file > .assets/`basename $$file`; done + +underscore: + $(MAKE) -C $@ + diff --git a/vendor/github.com/robertkrimen/otto/README.markdown b/vendor/github.com/robertkrimen/otto/README.markdown new file mode 100644 index 00000000..40584d32 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/README.markdown @@ -0,0 +1,871 @@ +# otto +-- +```go +import "github.com/robertkrimen/otto" +``` + +Package otto is a JavaScript parser and interpreter written natively in Go. + +http://godoc.org/github.com/robertkrimen/otto + +```go +import ( + "github.com/robertkrimen/otto" +) +``` + +Run something in the VM + +```go +vm := otto.New() +vm.Run(` + abc = 2 + 2; + console.log("The value of abc is " + abc); // 4 +`) +``` + +Get a value out of the VM + +```go +if value, err := vm.Get("abc"); err == nil { + if value_int, err := value.ToInteger(); err == nil { + fmt.Printf("", value_int, err) + } +} +``` + +Set a number + +```go +vm.Set("def", 11) +vm.Run(` + console.log("The value of def is " + def); + // The value of def is 11 +`) +``` + +Set a string + +```go +vm.Set("xyzzy", "Nothing happens.") +vm.Run(` + console.log(xyzzy.length); // 16 +`) +``` + +Get the value of an expression + +```go +value, _ = vm.Run("xyzzy.length") +{ + // value is an int64 with a value of 16 + value, _ := value.ToInteger() +} +``` + +An error happens + +```go +value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length") +if err != nil { + // err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined + // If there is an error, then value.IsUndefined() is true + ... +} +``` + +Set a Go function + +```go +vm.Set("sayHello", func(call otto.FunctionCall) otto.Value { + fmt.Printf("Hello, %s.\n", call.Argument(0).String()) + return otto.Value{} +}) +``` + +Set a Go function that returns something useful + +```go +vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value { + right, _ := call.Argument(0).ToInteger() + result, _ := vm.ToValue(2 + right) + return result +}) +``` + +Use the functions in JavaScript + +```go +result, _ = vm.Run(` + sayHello("Xyzzy"); // Hello, Xyzzy. + sayHello(); // Hello, undefined + + result = twoPlus(2.0); // 4 +`) +``` + +### Parser + +A separate parser is available in the parser package if you're just interested +in building an AST. + +http://godoc.org/github.com/robertkrimen/otto/parser + +Parse and return an AST + +```go +filename := "" // A filename is optional +src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); +` + +// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList +program, err := parser.ParseFile(nil, filename, src, 0) +``` + +### otto + +You can run (Go) JavaScript from the commandline with: +http://github.com/robertkrimen/otto/tree/master/otto + + $ go get -v github.com/robertkrimen/otto/otto + +Run JavaScript by entering some source on stdin or by giving otto a filename: + + $ otto example.js + +### underscore + +Optionally include the JavaScript utility-belt library, underscore, with this +import: + +```go +import ( + "github.com/robertkrimen/otto" + _ "github.com/robertkrimen/otto/underscore" +) + +// Now every otto runtime will come loaded with underscore +``` + +For more information: http://github.com/robertkrimen/otto/tree/master/underscore + + +### Caveat Emptor + +The following are some limitations with otto: + + * "use strict" will parse, but does nothing. + * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. + * Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. + + +### Regular Expression Incompatibility + +Go translates JavaScript-style regular expressions into something that is +"regexp" compatible via `parser.TransformRegExp`. Unfortunately, RegExp requires +backtracking for some patterns, and backtracking is not supported by the +standard Go engine: https://code.google.com/p/re2/wiki/Syntax + +Therefore, the following syntax is incompatible: + + (?=) // Lookahead (positive), currently a parsing error + (?!) // Lookahead (backhead), currently a parsing error + \1 // Backreference (\1, \2, \3, ...), currently a parsing error + +A brief discussion of these limitations: "Regexp (?!re)" +https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E + +More information about re2: https://code.google.com/p/re2/ + +In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r +]. The JavaScript definition, on the other hand, also includes \v, Unicode +"Separator, Space", etc. + + +### Halting Problem + +If you want to stop long running executions (like third-party code), you can use +the interrupt channel to do this: + +```go +package main + +import ( + "errors" + "fmt" + "os" + "time" + + "github.com/robertkrimen/otto" +) + +var halt = errors.New("Stahp") + +func main() { + runUnsafe(`var abc = [];`) + runUnsafe(` + while (true) { + // Loop forever + }`) +} + +func runUnsafe(unsafe string) { + start := time.Now() + defer func() { + duration := time.Since(start) + if caught := recover(); caught != nil { + if caught == halt { + fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration) + return + } + panic(caught) // Something else happened, repanic! + } + fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration) + }() + + vm := otto.New() + vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking + + go func() { + time.Sleep(2 * time.Second) // Stop after two seconds + vm.Interrupt <- func() { + panic(halt) + } + }() + + vm.Run(unsafe) // Here be dragons (risky code) +} +``` + +Where is setTimeout/setInterval? + +These timing functions are not actually part of the ECMA-262 specification. +Typically, they belong to the `window` object (in the browser). It would not be +difficult to provide something like these via Go, but you probably want to wrap +otto in an event loop in that case. + +For an example of how this could be done in Go with otto, see natto: + +http://github.com/robertkrimen/natto + +Here is some more discussion of the issue: + +* http://book.mixu.net/node/ch2.html + +* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 + +* http://aaroncrane.co.uk/2009/02/perl_safe_signals/ + +## Usage + +```go +var ErrVersion = errors.New("version mismatch") +``` + +#### type Error + +```go +type Error struct { +} +``` + +An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc. + +#### func (Error) Error + +```go +func (err Error) Error() string +``` +Error returns a description of the error + + TypeError: 'def' is not a function + +#### func (Error) String + +```go +func (err Error) String() string +``` +String returns a description of the error and a trace of where the error +occurred. + + TypeError: 'def' is not a function + at xyz (:3:9) + at :7:1/ + +#### type FunctionCall + +```go +type FunctionCall struct { + This Value + ArgumentList []Value + Otto *Otto +} +``` + +FunctionCall is an encapsulation of a JavaScript function call. + +#### func (FunctionCall) Argument + +```go +func (self FunctionCall) Argument(index int) Value +``` +Argument will return the value of the argument at the given index. + +If no such argument exists, undefined is returned. + +#### type Object + +```go +type Object struct { +} +``` + +Object is the representation of a JavaScript object. + +#### func (Object) Call + +```go +func (self Object) Call(name string, argumentList ...interface{}) (Value, error) +``` +Call a method on the object. + +It is essentially equivalent to: + + var method, _ := object.Get(name) + method.Call(object, argumentList...) + +An undefined value and an error will result if: + + 1. There is an error during conversion of the argument list + 2. The property is not actually a function + 3. An (uncaught) exception is thrown + +#### func (Object) Class + +```go +func (self Object) Class() string +``` +Class will return the class string of the object. + +The return value will (generally) be one of: + + Object + Function + Array + String + Number + Boolean + Date + RegExp + +#### func (Object) Get + +```go +func (self Object) Get(name string) (Value, error) +``` +Get the value of the property with the given name. + +#### func (Object) Keys + +```go +func (self Object) Keys() []string +``` +Get the keys for the object + +Equivalent to calling Object.keys on the object + +#### func (Object) Set + +```go +func (self Object) Set(name string, value interface{}) error +``` +Set the property of the given name to the given value. + +An error will result if the setting the property triggers an exception (i.e. +read-only), or there is an error during conversion of the given value. + +#### func (Object) Value + +```go +func (self Object) Value() Value +``` +Value will return self as a value. + +#### type Otto + +```go +type Otto struct { + // Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example. + // See "Halting Problem" for more information. + Interrupt chan func() +} +``` + +Otto is the representation of the JavaScript runtime. Each instance of Otto has +a self-contained namespace. + +#### func New + +```go +func New() *Otto +``` +New will allocate a new JavaScript runtime + +#### func Run + +```go +func Run(src interface{}) (*Otto, Value, error) +``` +Run will allocate a new JavaScript runtime, run the given source on the +allocated runtime, and return the runtime, resulting value, and error (if any). + +src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST +always be in UTF-8. + +src may also be a Script. + +src may also be a Program, but if the AST has been modified, then runtime +behavior is undefined. + +#### func (Otto) Call + +```go +func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) +``` +Call the given JavaScript with a given this and arguments. + +If this is nil, then some special handling takes place to determine the proper +this value, falling back to a "standard" invocation if necessary (where this is +undefined). + +If source begins with "new " (A lowercase new followed by a space), then Call +will invoke the function constructor rather than performing a function call. In +this case, the this argument has no effect. + +```go +// value is a String object +value, _ := vm.Call("Object", nil, "Hello, World.") + +// Likewise... +value, _ := vm.Call("new Object", nil, "Hello, World.") + +// This will perform a concat on the given array and return the result +// value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ] +value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") +``` + +#### func (*Otto) Compile + +```go +func (self *Otto) Compile(filename string, src interface{}) (*Script, error) +``` +Compile will parse the given source and return a Script value or nil and an +error if there was a problem during compilation. + +```go +script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) +vm.Run(script) +``` + +#### func (*Otto) Copy + +```go +func (in *Otto) Copy() *Otto +``` +Copy will create a copy/clone of the runtime. + +Copy is useful for saving some time when creating many similar runtimes. + +This method works by walking the original runtime and cloning each object, +scope, stash, etc. into a new runtime. + +Be on the lookout for memory leaks or inadvertent sharing of resources. + +#### func (Otto) Get + +```go +func (self Otto) Get(name string) (Value, error) +``` +Get the value of the top-level binding of the given name. + +If there is an error (like the binding does not exist), then the value will be +undefined. + +#### func (Otto) Object + +```go +func (self Otto) Object(source string) (*Object, error) +``` +Object will run the given source and return the result as an object. + +For example, accessing an existing object: + +```go +object, _ := vm.Object(`Number`) +``` + +Or, creating a new object: + +```go +object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`) +``` + +Or, creating and assigning an object: + +```go +object, _ := vm.Object(`xyzzy = {}`) +object.Set("volume", 11) +``` + +If there is an error (like the source does not result in an object), then nil +and an error is returned. + +#### func (Otto) Run + +```go +func (self Otto) Run(src interface{}) (Value, error) +``` +Run will run the given source (parsing it first if necessary), returning the +resulting value and error (if any) + +src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST +always be in UTF-8. + +If the runtime is unable to parse source, then this function will return +undefined and the parse error (nothing will be evaluated in this case). + +src may also be a Script. + +src may also be a Program, but if the AST has been modified, then runtime +behavior is undefined. + +#### func (Otto) Set + +```go +func (self Otto) Set(name string, value interface{}) error +``` +Set the top-level binding of the given name to the given value. + +Set will automatically apply ToValue to the given value in order to convert it +to a JavaScript value (type Value). + +If there is an error (like the binding is read-only, or the ToValue conversion +fails), then an error is returned. + +If the top-level binding does not exist, it will be created. + +#### func (Otto) ToValue + +```go +func (self Otto) ToValue(value interface{}) (Value, error) +``` +ToValue will convert an interface{} value to a value digestible by +otto/JavaScript. + +#### type Script + +```go +type Script struct { +} +``` + +Script is a handle for some (reusable) JavaScript. Passing a Script value to a +run method will evaluate the JavaScript. + +#### func (*Script) String + +```go +func (self *Script) String() string +``` + +#### type Value + +```go +type Value struct { +} +``` + +Value is the representation of a JavaScript value. + +#### func FalseValue + +```go +func FalseValue() Value +``` +FalseValue will return a value representing false. + +It is equivalent to: + +```go +ToValue(false) +``` + +#### func NaNValue + +```go +func NaNValue() Value +``` +NaNValue will return a value representing NaN. + +It is equivalent to: + +```go +ToValue(math.NaN()) +``` + +#### func NullValue + +```go +func NullValue() Value +``` +NullValue will return a Value representing null. + +#### func ToValue + +```go +func ToValue(value interface{}) (Value, error) +``` +ToValue will convert an interface{} value to a value digestible by +otto/JavaScript + +This function will not work for advanced types (struct, map, slice/array, etc.) +and you should use Otto.ToValue instead. + +#### func TrueValue + +```go +func TrueValue() Value +``` +TrueValue will return a value representing true. + +It is equivalent to: + +```go +ToValue(true) +``` + +#### func UndefinedValue + +```go +func UndefinedValue() Value +``` +UndefinedValue will return a Value representing undefined. + +#### func (Value) Call + +```go +func (value Value) Call(this Value, argumentList ...interface{}) (Value, error) +``` +Call the value as a function with the given this value and argument list and +return the result of invocation. It is essentially equivalent to: + + value.apply(thisValue, argumentList) + +An undefined value and an error will result if: + + 1. There is an error during conversion of the argument list + 2. The value is not actually a function + 3. An (uncaught) exception is thrown + +#### func (Value) Class + +```go +func (value Value) Class() string +``` +Class will return the class string of the value or the empty string if value is +not an object. + +The return value will (generally) be one of: + + Object + Function + Array + String + Number + Boolean + Date + RegExp + +#### func (Value) Export + +```go +func (self Value) Export() (interface{}, error) +``` +Export will attempt to convert the value to a Go representation and return it +via an interface{} kind. + +Export returns an error, but it will always be nil. It is present for backwards +compatibility. + +If a reasonable conversion is not possible, then the original value is returned. + + undefined -> nil (FIXME?: Should be Value{}) + null -> nil + boolean -> bool + number -> A number type (int, float32, uint64, ...) + string -> string + Array -> []interface{} + Object -> map[string]interface{} + +#### func (Value) IsBoolean + +```go +func (value Value) IsBoolean() bool +``` +IsBoolean will return true if value is a boolean (primitive). + +#### func (Value) IsDefined + +```go +func (value Value) IsDefined() bool +``` +IsDefined will return false if the value is undefined, and true otherwise. + +#### func (Value) IsFunction + +```go +func (value Value) IsFunction() bool +``` +IsFunction will return true if value is a function. + +#### func (Value) IsNaN + +```go +func (value Value) IsNaN() bool +``` +IsNaN will return true if value is NaN (or would convert to NaN). + +#### func (Value) IsNull + +```go +func (value Value) IsNull() bool +``` +IsNull will return true if the value is null, and false otherwise. + +#### func (Value) IsNumber + +```go +func (value Value) IsNumber() bool +``` +IsNumber will return true if value is a number (primitive). + +#### func (Value) IsObject + +```go +func (value Value) IsObject() bool +``` +IsObject will return true if value is an object. + +#### func (Value) IsPrimitive + +```go +func (value Value) IsPrimitive() bool +``` +IsPrimitive will return true if value is a primitive (any kind of primitive). + +#### func (Value) IsString + +```go +func (value Value) IsString() bool +``` +IsString will return true if value is a string (primitive). + +#### func (Value) IsUndefined + +```go +func (value Value) IsUndefined() bool +``` +IsUndefined will return true if the value is undefined, and false otherwise. + +#### func (Value) Object + +```go +func (value Value) Object() *Object +``` +Object will return the object of the value, or nil if value is not an object. + +This method will not do any implicit conversion. For example, calling this +method on a string primitive value will not return a String object. + +#### func (Value) String + +```go +func (value Value) String() string +``` +String will return the value as a string. + +This method will make return the empty string if there is an error. + +#### func (Value) ToBoolean + +```go +func (value Value) ToBoolean() (bool, error) +``` +ToBoolean will convert the value to a boolean (bool). + + ToValue(0).ToBoolean() => false + ToValue("").ToBoolean() => false + ToValue(true).ToBoolean() => true + ToValue(1).ToBoolean() => true + ToValue("Nothing happens").ToBoolean() => true + +If there is an error during the conversion process (like an uncaught exception), +then the result will be false and an error. + +#### func (Value) ToFloat + +```go +func (value Value) ToFloat() (float64, error) +``` +ToFloat will convert the value to a number (float64). + + ToValue(0).ToFloat() => 0. + ToValue(1.1).ToFloat() => 1.1 + ToValue("11").ToFloat() => 11. + +If there is an error during the conversion process (like an uncaught exception), +then the result will be 0 and an error. + +#### func (Value) ToInteger + +```go +func (value Value) ToInteger() (int64, error) +``` +ToInteger will convert the value to a number (int64). + + ToValue(0).ToInteger() => 0 + ToValue(1.1).ToInteger() => 1 + ToValue("11").ToInteger() => 11 + +If there is an error during the conversion process (like an uncaught exception), +then the result will be 0 and an error. + +#### func (Value) ToString + +```go +func (value Value) ToString() (string, error) +``` +ToString will convert the value to a string (string). + + ToValue(0).ToString() => "0" + ToValue(false).ToString() => "false" + ToValue(1.1).ToString() => "1.1" + ToValue("11").ToString() => "11" + ToValue('Nothing happens.').ToString() => "Nothing happens." + +If there is an error during the conversion process (like an uncaught exception), +then the result will be the empty string ("") and an error. + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/robertkrimen/otto/array_test.go b/vendor/github.com/robertkrimen/otto/array_test.go new file mode 100644 index 00000000..358be794 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/array_test.go @@ -0,0 +1,720 @@ +package otto + +import ( + "testing" +) + +func TestArray(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = [ undefined, "Nothing happens." ]; + abc.length; + `, 2) + + test(` + abc = ""+[0, 1, 2, 3]; + def = [].toString(); + ghi = [null, 4, "null"].toString(); + [ abc, def, ghi ]; + `, "0,1,2,3,,,4,null") + + test(`new Array(0).length`, 0) + + test(`new Array(11).length`, 11) + + test(`new Array(11, 1).length`, 2) + + test(` + abc = [0, 1, 2, 3]; + abc.xyzzy = "Nothing happens."; + delete abc[1]; + var xyzzy = delete abc.xyzzy; + [ abc, xyzzy, abc.xyzzy ]; + `, "0,,2,3,true,") + + test(` + var abc = [0, 1, 2, 3, 4]; + abc.length = 2; + abc; + `, "0,1") + + test(`raise: + [].length = 3.14159; + `, "RangeError") + + test(`raise: + new Array(3.14159); + `, "RangeError") + + test(` + Object.defineProperty(Array.prototype, "0", { + value: 100, + writable: false, + configurable: true + }); + abc = [101]; + abc.hasOwnProperty("0") && abc[0] === 101; + `, true) + + test(` + abc = [,,undefined]; + [ abc.hasOwnProperty(0), abc.hasOwnProperty(1), abc.hasOwnProperty(2) ]; + `, "false,false,true") + + test(` + abc = Object.getOwnPropertyDescriptor(Array, "prototype"); + [ [ typeof Array.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestArray_toString(t *testing.T) { + tt(t, func() { + { + test(` + Array.prototype.toString = function() { + return "Nothing happens."; + } + abc = Array.prototype.toString(); + def = [].toString(); + ghi = [null, 4, "null"].toString(); + + [ abc, def, ghi ].join(","); + `, "Nothing happens.,Nothing happens.,Nothing happens.") + } + + { + test(` + Array.prototype.join = undefined + abc = Array.prototype.toString() + def = [].toString() + ghi = [null, 4, "null"].toString() + + abc + "," + def + "," + ghi; + `, "[object Array],[object Array],[object Array]") + } + }) +} + +func TestArray_toLocaleString(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + [ 3.14159, "abc", undefined, new Date(0) ].toLocaleString(); + `, "3.14159,abc,,1970-01-01 00:00:00") + + test(`raise: + [ { toLocaleString: undefined } ].toLocaleString(); + `, "TypeError") + }) +} + +func TestArray_concat(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0, 1, 2]; + def = [-1, -2, -3]; + ghi = abc.concat(def); + jkl = abc.concat(def, 3, 4, 5); + mno = def.concat(-4, -5, abc); + + [ ghi, jkl, mno ].join(";"); + `, "0,1,2,-1,-2,-3;0,1,2,-1,-2,-3,3,4,5;-1,-2,-3,-4,-5,0,1,2") + + test(` + var abc = [,1]; + var def = abc.concat([], [,]); + + def.getClass = Object.prototype.toString; + + [ def.getClass(), typeof def[0], def[1], typeof def[2], def.length ]; + `, "[object Array],undefined,1,undefined,3") + + test(` + Object.defineProperty(Array.prototype, "0", { + value: 100, + writable: false, + configurable: true + }); + + var abc = Array.prototype.concat.call(101); + + var hasProperty = abc.hasOwnProperty("0"); + var instanceOfVerify = typeof abc[0] === "object"; + var verifyValue = false; + verifyValue = abc[0] == 101; + + var verifyEnumerable = false; + for (var property in abc) { + if (property === "0" && abc.hasOwnProperty("0")) { + verifyEnumerable = true; + } + } + + var verifyWritable = false; + abc[0] = 12; + verifyWritable = abc[0] === 12; + + var verifyConfigurable = false; + delete abc[0]; + verifyConfigurable = abc.hasOwnProperty("0"); + + [ hasProperty, instanceOfVerify, verifyValue, !verifyConfigurable, verifyEnumerable, verifyWritable ]; + `, "true,true,true,true,true,true") + }) +} + +func TestArray_splice(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0, 1, 2]; + def = abc.splice(1, 2, 3, 4, 5); + ghi = [].concat(abc); + jkl = ghi.splice(17, 21, 7, 8, 9); + mno = [].concat(abc); + pqr = mno.splice(2); + [ abc, def, ghi, jkl, mno, pqr ].join(";"); + `, "0,3,4,5;1,2;0,3,4,5,7,8,9;;0,3;4,5") + }) +} + +func TestArray_shift(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0, 1, 2]; + def = abc.shift(); + ghi = [].concat(abc); + jkl = abc.shift(); + mno = [].concat(abc); + pqr = abc.shift(); + stu = [].concat(abc); + vwx = abc.shift(); + + [ abc, def, ghi, jkl, mno, pqr, stu, vwx ].join(";"); + `, ";0;1,2;1;2;2;;") + }) +} + +func TestArray_push(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0]; + def = abc.push(1); + ghi = [].concat(abc); + jkl = abc.push(2,3,4); + + [ abc, def, ghi, jkl ].join(";"); + `, "0,1,2,3,4;2;0,1;5") + }) +} + +func TestArray_pop(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1]; + def = abc.pop(); + ghi = [].concat(abc); + jkl = abc.pop(); + mno = [].concat(abc); + pqr = abc.pop(); + + [ abc, def, ghi, jkl, mno, pqr ].join(";"); + `, ";1;0;0;;") + }) +} + +func TestArray_slice(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1,2,3]; + def = abc.slice(); + ghi = abc.slice(1); + jkl = abc.slice(3,-1); + mno = abc.slice(2,-1); + pqr = abc.slice(-1, -10); + + [ abc, def, ghi, jkl, mno, pqr ].join(";"); + `, "0,1,2,3;0,1,2,3;1,2,3;;2;") + + // Array.protoype.slice is generic + test(` + abc = { 0: 0, 1: 1, 2: 2, 3: 3 }; + abc.length = 4; + def = Array.prototype.slice.call(abc); + ghi = Array.prototype.slice.call(abc,1); + jkl = Array.prototype.slice.call(abc,3,-1); + mno = Array.prototype.slice.call(abc,2,-1); + pqr = Array.prototype.slice.call(abc,-1,-10); + + [ abc, def, ghi, jkl, pqr ].join(";"); + `, "[object Object];0,1,2,3;1,2,3;;") + }) +} + +func TestArray_sliceArguments(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + (function(){ + return Array.prototype.slice.call(arguments, 1) + })({}, 1, 2, 3); + `, "1,2,3") + }) +} + +func TestArray_unshift(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = []; + def = abc.unshift(0); + ghi = [].concat(abc); + jkl = abc.unshift(1,2,3,4); + + [ abc, def, ghi, jkl ].join(";"); + `, "1,2,3,4,0;1;0;5") + }) +} + +func TestArray_reverse(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1,2,3].reverse(); + def = [0,1,2].reverse(); + + [ abc, def ]; + `, "3,2,1,0,2,1,0") + }) +} + +func TestArray_sort(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [0,1,2,3].sort(); + def = [3,2,1,0].sort(); + ghi = [].sort(); + jkl = [0].sort(); + mno = [1,0].sort(); + pqr = [1,5,-10, 100, 8, 72, 401, 0.05].sort(); + stu = [1,5,-10, 100, 8, 72, 401, 0.05].sort(function(x, y){ + return x == y ? 0 : x < y ? -1 : 1 + }); + vwx = [1,2,3,1,2,3].sort(); + yza = [1,2,3,1,0,1,-1,0].sort(); + + [ abc, def, ghi, jkl, mno, pqr, stu, vwx, yza ].join(";"); + `, "0,1,2,3;0,1,2,3;;0;0,1;-10,0.05,1,100,401,5,72,8;-10,0.05,1,5,8,72,100,401;1,1,2,2,3,3;-1,0,0,1,1,1,2,3") + + test(`Array.prototype.sort.length`, 1) + }) +} + +func TestArray_isArray(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ Array.isArray.length, Array.isArray(), Array.isArray([]), Array.isArray({}) ]; + `, "1,false,true,false") + + test(`Array.isArray(Math)`, false) + }) +} + +func TestArray_indexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`['a', 'b', 'c', 'b'].indexOf('b')`, 1) + + test(`['a', 'b', 'c', 'b'].indexOf('b', 2)`, 3) + + test(`['a', 'b', 'c', 'b'].indexOf('b', -2)`, 3) + + test(` + Object.prototype.indexOf = Array.prototype.indexOf; + var abc = {0: 'a', 1: 'b', 2: 'c', length: 3}; + abc.indexOf('c'); + `, 2) + + test(`[true].indexOf(true, "-Infinity")`, 0) + + test(` + var target = {}; + Math[3] = target; + Math.length = 5; + Array.prototype.indexOf.call(Math, target) === 3; + `, true) + + test(` + var _NaN = NaN; + var abc = new Array("NaN", undefined, 0, false, null, {toString:function(){return NaN}}, "false", _NaN, NaN); + abc.indexOf(NaN); + `, -1) + + test(` + var abc = {toString:function (){return 0}}; + var def = 1; + var ghi = -(4/3); + var jkl = new Array(false, undefined, null, "0", abc, -1.3333333333333, "string", -0, true, +0, def, 1, 0, false, ghi, -(4/3)); + [ jkl.indexOf(-(4/3)), jkl.indexOf(0), jkl.indexOf(-0), jkl.indexOf(1) ]; + `, "14,7,7,10") + }) +} + +func TestArray_lastIndexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`['a', 'b', 'c', 'b'].lastIndexOf('b')`, 3) + + test(`['a', 'b', 'c', 'b'].lastIndexOf('b', 2)`, 1) + + test(`['a', 'b', 'c', 'b'].lastIndexOf('b', -2)`, 1) + + test(` + Object.prototype.lastIndexOf = Array.prototype.lastIndexOf; + var abc = {0: 'a', 1: 'b', 2: 'c', 3: 'b', length: 4}; + abc.lastIndexOf('b'); + `, 3) + + test(` + var target = {}; + Math[3] = target; + Math.length = 5; + [ Array.prototype.lastIndexOf.call(Math, target) === 3 ]; + `, "true") + + test(` + var _NaN = NaN; + var abc = new Array("NaN", undefined, 0, false, null, {toString:function(){return NaN}}, "false", _NaN, NaN); + abc.lastIndexOf(NaN); + `, -1) + + test(` + var abc = {toString:function (){return 0}}; + var def = 1; + var ghi = -(4/3); + var jkl = new Array(false, undefined, null, "0", abc, -1.3333333333333, "string", -0, true, +0, def, 1, 0, false, ghi, -(4/3)); + [ jkl.lastIndexOf(-(4/3)), jkl.indexOf(0), jkl.indexOf(-0), jkl.indexOf(1) ]; + `, "15,7,7,10") + }) +} + +func TestArray_every(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].every()`, "TypeError") + + test(`raise: [].every("abc")`, "TypeError") + + test(`[].every(function() { return false })`, true) + + test(`[1,2,3].every(function() { return false })`, false) + + test(`[1,2,3].every(function() { return true })`, true) + + test(`[1,2,3].every(function(_, index) { if (index === 1) return true })`, false) + + test(` + var abc = function(value, index, object) { + return ('[object Math]' !== Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + !Array.prototype.every.call(Math, abc); + `, true) + + test(` + var def = false; + + var abc = function(value, index, object) { + def = true; + return this === Math; + }; + + [11].every(abc, Math) && def; + `, true) + + test(` + var def = false; + + var abc = function(value, index, object) { + def = true; + return Math; + }; + + [11].every(abc) && def; + `, true) + }) +} + +func TestArray_some(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].some("abc")`, "TypeError") + + test(`[].some(function() { return true })`, false) + + test(`[1,2,3].some(function() { return false })`, false) + + test(`[1,2,3].some(function() { return true })`, true) + + test(`[1,2,3].some(function(_, index) { if (index === 1) return true })`, true) + + test(` + var abc = function(value, index, object) { + return ('[object Math]' !== Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + !Array.prototype.some.call(Math, abc); + `, true) + + test(` + var abc = function(value, index, object) { + return this === Math; + }; + + [11].some(abc, Math); + `, true) + + test(` + var abc = function(value, index, object) { + return Math; + }; + + [11].some(abc); + `, true) + }) +} + +func TestArray_forEach(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].forEach("abc")`, "TypeError") + + test(` + var abc = 0; + [].forEach(function(value) { + abc += value; + }); + abc; + `, 0) + + test(` + abc = 0; + var def = []; + [1,2,3].forEach(function(value, index) { + abc += value; + def.push(index); + }); + [ abc, def ]; + `, "6,0,1,2") + + test(` + var def = false; + var abc = function(value, index, object) { + def = ('[object Math]' === Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + Array.prototype.forEach.call(Math, abc); + def; + `, true) + + test(` + var def = false; + var abc = function(value, index, object) { + def = this === Math; + }; + + [11].forEach(abc, Math); + def; + `, true) + }) +} + +func TestArray_indexing(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = new Array(0, 1); + var def = abc.length; + abc[4294967296] = 10; // 2^32 => 0 + abc[4294967297] = 11; // 2^32+1 => 1 + [ def, abc.length, abc[0], abc[1], abc[4294967296] ]; + `, "2,2,0,1,10") + + test(` + abc = new Array(0, 1); + def = abc.length; + abc[4294967295] = 10; + var ghi = abc.length; + abc[4294967299] = 12; + var jkl = abc.length; + abc[4294967294] = 11; + [ def, ghi, jkl, abc.length, abc[4294967295], abc[4294967299] ]; + `, "2,2,2,4294967295,10,12") + }) +} + +func TestArray_map(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].map("abc")`, "TypeError") + + test(`[].map(function() { return 1 }).length`, 0) + + test(`[1,2,3].map(function(value) { return value * value })`, "1,4,9") + + test(`[1,2,3].map(function(value) { return 1 })`, "1,1,1") + + test(` + var abc = function(value, index, object) { + return ('[object Math]' === Object.prototype.toString.call(object)); + }; + + Math.length = 1; + Math[0] = 1; + Array.prototype.map.call(Math, abc)[0]; + `, true) + + test(` + var abc = function(value, index, object) { + return this === Math; + }; + + [11].map(abc, Math)[0]; + `, true) + }) +} + +func TestArray_filter(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].filter("abc")`, "TypeError") + + test(`[].filter(function() { return 1 }).length`, 0) + + test(`[1,2,3].filter(function() { return false }).length`, 0) + + test(`[1,2,3].filter(function() { return true })`, "1,2,3") + }) +} + +func TestArray_reduce(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].reduce("abc")`, "TypeError") + + test(`raise: [].reduce(function() {})`, "TypeError") + + test(`[].reduce(function() {}, 0)`, 0) + + test(`[].reduce(function() {}, undefined)`, "undefined") + + test(`['a','b','c'].reduce(function(result, value) { return result+', '+value })`, "a, b, c") + + test(`[1,2,3].reduce(function(result, value) { return result + value }, 4)`, 10) + + test(`[1,2,3].reduce(function(result, value) { return result + value })`, 6) + }) +} + +func TestArray_reduceRight(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: [].reduceRight("abc")`, "TypeError") + + test(`raise: [].reduceRight(function() {})`, "TypeError") + + test(`[].reduceRight(function() {}, 0)`, 0) + + test(`[].reduceRight(function() {}, undefined)`, "undefined") + + test(`['a','b','c'].reduceRight(function(result, value) { return result+', '+value })`, "c, b, a") + + test(`[1,2,3].reduceRight(function(result, value) { return result + value }, 4)`, 10) + + test(`[1,2,3].reduceRight(function(result, value) { return result + value })`, 6) + }) +} + +func TestArray_defineOwnProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = []; + Object.defineProperty(abc, "length", { + writable: false + }); + abc.length; + `, 0) + + test(`raise: + var abc = []; + var exception; + Object.defineProperty(abc, "length", { + writable: false + }); + Object.defineProperty(abc, "length", { + writable: true + }); + `, "TypeError") + }) +} + +func TestArray_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = new Array(null); + var def = new Array(undefined); + [ abc.length, abc[0] === null, def.length, def[0] === undefined ] + `, "1,true,1,true") + + test(` + var abc = new Array(new Number(0)); + var def = new Array(new Number(4294967295)); + [ abc.length, typeof abc[0], abc[0] == 0, def.length, typeof def[0], def[0] == 4294967295 ] + `, "1,object,true,1,object,true") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/ast/README.markdown b/vendor/github.com/robertkrimen/otto/ast/README.markdown new file mode 100644 index 00000000..a785da91 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/README.markdown @@ -0,0 +1,1068 @@ +# ast +-- + import "github.com/robertkrimen/otto/ast" + +Package ast declares types representing a JavaScript AST. + + +### Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +## Usage + +#### type ArrayLiteral + +```go +type ArrayLiteral struct { + LeftBracket file.Idx + RightBracket file.Idx + Value []Expression +} +``` + + +#### func (*ArrayLiteral) Idx0 + +```go +func (self *ArrayLiteral) Idx0() file.Idx +``` + +#### func (*ArrayLiteral) Idx1 + +```go +func (self *ArrayLiteral) Idx1() file.Idx +``` + +#### type AssignExpression + +```go +type AssignExpression struct { + Operator token.Token + Left Expression + Right Expression +} +``` + + +#### func (*AssignExpression) Idx0 + +```go +func (self *AssignExpression) Idx0() file.Idx +``` + +#### func (*AssignExpression) Idx1 + +```go +func (self *AssignExpression) Idx1() file.Idx +``` + +#### type BadExpression + +```go +type BadExpression struct { + From file.Idx + To file.Idx +} +``` + + +#### func (*BadExpression) Idx0 + +```go +func (self *BadExpression) Idx0() file.Idx +``` + +#### func (*BadExpression) Idx1 + +```go +func (self *BadExpression) Idx1() file.Idx +``` + +#### type BadStatement + +```go +type BadStatement struct { + From file.Idx + To file.Idx +} +``` + + +#### func (*BadStatement) Idx0 + +```go +func (self *BadStatement) Idx0() file.Idx +``` + +#### func (*BadStatement) Idx1 + +```go +func (self *BadStatement) Idx1() file.Idx +``` + +#### type BinaryExpression + +```go +type BinaryExpression struct { + Operator token.Token + Left Expression + Right Expression + Comparison bool +} +``` + + +#### func (*BinaryExpression) Idx0 + +```go +func (self *BinaryExpression) Idx0() file.Idx +``` + +#### func (*BinaryExpression) Idx1 + +```go +func (self *BinaryExpression) Idx1() file.Idx +``` + +#### type BlockStatement + +```go +type BlockStatement struct { + LeftBrace file.Idx + List []Statement + RightBrace file.Idx +} +``` + + +#### func (*BlockStatement) Idx0 + +```go +func (self *BlockStatement) Idx0() file.Idx +``` + +#### func (*BlockStatement) Idx1 + +```go +func (self *BlockStatement) Idx1() file.Idx +``` + +#### type BooleanLiteral + +```go +type BooleanLiteral struct { + Idx file.Idx + Literal string + Value bool +} +``` + + +#### func (*BooleanLiteral) Idx0 + +```go +func (self *BooleanLiteral) Idx0() file.Idx +``` + +#### func (*BooleanLiteral) Idx1 + +```go +func (self *BooleanLiteral) Idx1() file.Idx +``` + +#### type BracketExpression + +```go +type BracketExpression struct { + Left Expression + Member Expression + LeftBracket file.Idx + RightBracket file.Idx +} +``` + + +#### func (*BracketExpression) Idx0 + +```go +func (self *BracketExpression) Idx0() file.Idx +``` + +#### func (*BracketExpression) Idx1 + +```go +func (self *BracketExpression) Idx1() file.Idx +``` + +#### type BranchStatement + +```go +type BranchStatement struct { + Idx file.Idx + Token token.Token + Label *Identifier +} +``` + + +#### func (*BranchStatement) Idx0 + +```go +func (self *BranchStatement) Idx0() file.Idx +``` + +#### func (*BranchStatement) Idx1 + +```go +func (self *BranchStatement) Idx1() file.Idx +``` + +#### type CallExpression + +```go +type CallExpression struct { + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx +} +``` + + +#### func (*CallExpression) Idx0 + +```go +func (self *CallExpression) Idx0() file.Idx +``` + +#### func (*CallExpression) Idx1 + +```go +func (self *CallExpression) Idx1() file.Idx +``` + +#### type CaseStatement + +```go +type CaseStatement struct { + Case file.Idx + Test Expression + Consequent []Statement +} +``` + + +#### func (*CaseStatement) Idx0 + +```go +func (self *CaseStatement) Idx0() file.Idx +``` + +#### func (*CaseStatement) Idx1 + +```go +func (self *CaseStatement) Idx1() file.Idx +``` + +#### type CatchStatement + +```go +type CatchStatement struct { + Catch file.Idx + Parameter *Identifier + Body Statement +} +``` + + +#### func (*CatchStatement) Idx0 + +```go +func (self *CatchStatement) Idx0() file.Idx +``` + +#### func (*CatchStatement) Idx1 + +```go +func (self *CatchStatement) Idx1() file.Idx +``` + +#### type ConditionalExpression + +```go +type ConditionalExpression struct { + Test Expression + Consequent Expression + Alternate Expression +} +``` + + +#### func (*ConditionalExpression) Idx0 + +```go +func (self *ConditionalExpression) Idx0() file.Idx +``` + +#### func (*ConditionalExpression) Idx1 + +```go +func (self *ConditionalExpression) Idx1() file.Idx +``` + +#### type DebuggerStatement + +```go +type DebuggerStatement struct { + Debugger file.Idx +} +``` + + +#### func (*DebuggerStatement) Idx0 + +```go +func (self *DebuggerStatement) Idx0() file.Idx +``` + +#### func (*DebuggerStatement) Idx1 + +```go +func (self *DebuggerStatement) Idx1() file.Idx +``` + +#### type Declaration + +```go +type Declaration interface { + // contains filtered or unexported methods +} +``` + +All declaration nodes implement the Declaration interface. + +#### type DoWhileStatement + +```go +type DoWhileStatement struct { + Do file.Idx + Test Expression + Body Statement +} +``` + + +#### func (*DoWhileStatement) Idx0 + +```go +func (self *DoWhileStatement) Idx0() file.Idx +``` + +#### func (*DoWhileStatement) Idx1 + +```go +func (self *DoWhileStatement) Idx1() file.Idx +``` + +#### type DotExpression + +```go +type DotExpression struct { + Left Expression + Identifier Identifier +} +``` + + +#### func (*DotExpression) Idx0 + +```go +func (self *DotExpression) Idx0() file.Idx +``` + +#### func (*DotExpression) Idx1 + +```go +func (self *DotExpression) Idx1() file.Idx +``` + +#### type EmptyStatement + +```go +type EmptyStatement struct { + Semicolon file.Idx +} +``` + + +#### func (*EmptyStatement) Idx0 + +```go +func (self *EmptyStatement) Idx0() file.Idx +``` + +#### func (*EmptyStatement) Idx1 + +```go +func (self *EmptyStatement) Idx1() file.Idx +``` + +#### type Expression + +```go +type Expression interface { + Node + // contains filtered or unexported methods +} +``` + +All expression nodes implement the Expression interface. + +#### type ExpressionStatement + +```go +type ExpressionStatement struct { + Expression Expression +} +``` + + +#### func (*ExpressionStatement) Idx0 + +```go +func (self *ExpressionStatement) Idx0() file.Idx +``` + +#### func (*ExpressionStatement) Idx1 + +```go +func (self *ExpressionStatement) Idx1() file.Idx +``` + +#### type ForInStatement + +```go +type ForInStatement struct { + For file.Idx + Into Expression + Source Expression + Body Statement +} +``` + + +#### func (*ForInStatement) Idx0 + +```go +func (self *ForInStatement) Idx0() file.Idx +``` + +#### func (*ForInStatement) Idx1 + +```go +func (self *ForInStatement) Idx1() file.Idx +``` + +#### type ForStatement + +```go +type ForStatement struct { + For file.Idx + Initializer Expression + Update Expression + Test Expression + Body Statement +} +``` + + +#### func (*ForStatement) Idx0 + +```go +func (self *ForStatement) Idx0() file.Idx +``` + +#### func (*ForStatement) Idx1 + +```go +func (self *ForStatement) Idx1() file.Idx +``` + +#### type FunctionDeclaration + +```go +type FunctionDeclaration struct { + Function *FunctionLiteral +} +``` + + +#### type FunctionLiteral + +```go +type FunctionLiteral struct { + Function file.Idx + Name *Identifier + ParameterList *ParameterList + Body Statement + Source string + + DeclarationList []Declaration +} +``` + + +#### func (*FunctionLiteral) Idx0 + +```go +func (self *FunctionLiteral) Idx0() file.Idx +``` + +#### func (*FunctionLiteral) Idx1 + +```go +func (self *FunctionLiteral) Idx1() file.Idx +``` + +#### type Identifier + +```go +type Identifier struct { + Name string + Idx file.Idx +} +``` + + +#### func (*Identifier) Idx0 + +```go +func (self *Identifier) Idx0() file.Idx +``` + +#### func (*Identifier) Idx1 + +```go +func (self *Identifier) Idx1() file.Idx +``` + +#### type IfStatement + +```go +type IfStatement struct { + If file.Idx + Test Expression + Consequent Statement + Alternate Statement +} +``` + + +#### func (*IfStatement) Idx0 + +```go +func (self *IfStatement) Idx0() file.Idx +``` + +#### func (*IfStatement) Idx1 + +```go +func (self *IfStatement) Idx1() file.Idx +``` + +#### type LabelledStatement + +```go +type LabelledStatement struct { + Label *Identifier + Colon file.Idx + Statement Statement +} +``` + + +#### func (*LabelledStatement) Idx0 + +```go +func (self *LabelledStatement) Idx0() file.Idx +``` + +#### func (*LabelledStatement) Idx1 + +```go +func (self *LabelledStatement) Idx1() file.Idx +``` + +#### type NewExpression + +```go +type NewExpression struct { + New file.Idx + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx +} +``` + + +#### func (*NewExpression) Idx0 + +```go +func (self *NewExpression) Idx0() file.Idx +``` + +#### func (*NewExpression) Idx1 + +```go +func (self *NewExpression) Idx1() file.Idx +``` + +#### type Node + +```go +type Node interface { + Idx0() file.Idx // The index of the first character belonging to the node + Idx1() file.Idx // The index of the first character immediately after the node +} +``` + +All nodes implement the Node interface. + +#### type NullLiteral + +```go +type NullLiteral struct { + Idx file.Idx + Literal string +} +``` + + +#### func (*NullLiteral) Idx0 + +```go +func (self *NullLiteral) Idx0() file.Idx +``` + +#### func (*NullLiteral) Idx1 + +```go +func (self *NullLiteral) Idx1() file.Idx +``` + +#### type NumberLiteral + +```go +type NumberLiteral struct { + Idx file.Idx + Literal string + Value interface{} +} +``` + + +#### func (*NumberLiteral) Idx0 + +```go +func (self *NumberLiteral) Idx0() file.Idx +``` + +#### func (*NumberLiteral) Idx1 + +```go +func (self *NumberLiteral) Idx1() file.Idx +``` + +#### type ObjectLiteral + +```go +type ObjectLiteral struct { + LeftBrace file.Idx + RightBrace file.Idx + Value []Property +} +``` + + +#### func (*ObjectLiteral) Idx0 + +```go +func (self *ObjectLiteral) Idx0() file.Idx +``` + +#### func (*ObjectLiteral) Idx1 + +```go +func (self *ObjectLiteral) Idx1() file.Idx +``` + +#### type ParameterList + +```go +type ParameterList struct { + Opening file.Idx + List []*Identifier + Closing file.Idx +} +``` + + +#### type Program + +```go +type Program struct { + Body []Statement + + DeclarationList []Declaration + + File *file.File +} +``` + + +#### func (*Program) Idx0 + +```go +func (self *Program) Idx0() file.Idx +``` + +#### func (*Program) Idx1 + +```go +func (self *Program) Idx1() file.Idx +``` + +#### type Property + +```go +type Property struct { + Key string + Kind string + Value Expression +} +``` + + +#### type RegExpLiteral + +```go +type RegExpLiteral struct { + Idx file.Idx + Literal string + Pattern string + Flags string + Value string +} +``` + + +#### func (*RegExpLiteral) Idx0 + +```go +func (self *RegExpLiteral) Idx0() file.Idx +``` + +#### func (*RegExpLiteral) Idx1 + +```go +func (self *RegExpLiteral) Idx1() file.Idx +``` + +#### type ReturnStatement + +```go +type ReturnStatement struct { + Return file.Idx + Argument Expression +} +``` + + +#### func (*ReturnStatement) Idx0 + +```go +func (self *ReturnStatement) Idx0() file.Idx +``` + +#### func (*ReturnStatement) Idx1 + +```go +func (self *ReturnStatement) Idx1() file.Idx +``` + +#### type SequenceExpression + +```go +type SequenceExpression struct { + Sequence []Expression +} +``` + + +#### func (*SequenceExpression) Idx0 + +```go +func (self *SequenceExpression) Idx0() file.Idx +``` + +#### func (*SequenceExpression) Idx1 + +```go +func (self *SequenceExpression) Idx1() file.Idx +``` + +#### type Statement + +```go +type Statement interface { + Node + // contains filtered or unexported methods +} +``` + +All statement nodes implement the Statement interface. + +#### type StringLiteral + +```go +type StringLiteral struct { + Idx file.Idx + Literal string + Value string +} +``` + + +#### func (*StringLiteral) Idx0 + +```go +func (self *StringLiteral) Idx0() file.Idx +``` + +#### func (*StringLiteral) Idx1 + +```go +func (self *StringLiteral) Idx1() file.Idx +``` + +#### type SwitchStatement + +```go +type SwitchStatement struct { + Switch file.Idx + Discriminant Expression + Default int + Body []*CaseStatement +} +``` + + +#### func (*SwitchStatement) Idx0 + +```go +func (self *SwitchStatement) Idx0() file.Idx +``` + +#### func (*SwitchStatement) Idx1 + +```go +func (self *SwitchStatement) Idx1() file.Idx +``` + +#### type ThisExpression + +```go +type ThisExpression struct { + Idx file.Idx +} +``` + + +#### func (*ThisExpression) Idx0 + +```go +func (self *ThisExpression) Idx0() file.Idx +``` + +#### func (*ThisExpression) Idx1 + +```go +func (self *ThisExpression) Idx1() file.Idx +``` + +#### type ThrowStatement + +```go +type ThrowStatement struct { + Throw file.Idx + Argument Expression +} +``` + + +#### func (*ThrowStatement) Idx0 + +```go +func (self *ThrowStatement) Idx0() file.Idx +``` + +#### func (*ThrowStatement) Idx1 + +```go +func (self *ThrowStatement) Idx1() file.Idx +``` + +#### type TryStatement + +```go +type TryStatement struct { + Try file.Idx + Body Statement + Catch *CatchStatement + Finally Statement +} +``` + + +#### func (*TryStatement) Idx0 + +```go +func (self *TryStatement) Idx0() file.Idx +``` + +#### func (*TryStatement) Idx1 + +```go +func (self *TryStatement) Idx1() file.Idx +``` + +#### type UnaryExpression + +```go +type UnaryExpression struct { + Operator token.Token + Idx file.Idx // If a prefix operation + Operand Expression + Postfix bool +} +``` + + +#### func (*UnaryExpression) Idx0 + +```go +func (self *UnaryExpression) Idx0() file.Idx +``` + +#### func (*UnaryExpression) Idx1 + +```go +func (self *UnaryExpression) Idx1() file.Idx +``` + +#### type VariableDeclaration + +```go +type VariableDeclaration struct { + Var file.Idx + List []*VariableExpression +} +``` + + +#### type VariableExpression + +```go +type VariableExpression struct { + Name string + Idx file.Idx + Initializer Expression +} +``` + + +#### func (*VariableExpression) Idx0 + +```go +func (self *VariableExpression) Idx0() file.Idx +``` + +#### func (*VariableExpression) Idx1 + +```go +func (self *VariableExpression) Idx1() file.Idx +``` + +#### type VariableStatement + +```go +type VariableStatement struct { + Var file.Idx + List []Expression +} +``` + + +#### func (*VariableStatement) Idx0 + +```go +func (self *VariableStatement) Idx0() file.Idx +``` + +#### func (*VariableStatement) Idx1 + +```go +func (self *VariableStatement) Idx1() file.Idx +``` + +#### type WhileStatement + +```go +type WhileStatement struct { + While file.Idx + Test Expression + Body Statement +} +``` + + +#### func (*WhileStatement) Idx0 + +```go +func (self *WhileStatement) Idx0() file.Idx +``` + +#### func (*WhileStatement) Idx1 + +```go +func (self *WhileStatement) Idx1() file.Idx +``` + +#### type WithStatement + +```go +type WithStatement struct { + With file.Idx + Object Expression + Body Statement +} +``` + + +#### func (*WithStatement) Idx0 + +```go +func (self *WithStatement) Idx0() file.Idx +``` + +#### func (*WithStatement) Idx1 + +```go +func (self *WithStatement) Idx1() file.Idx +``` + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/robertkrimen/otto/ast/comments.go b/vendor/github.com/robertkrimen/otto/ast/comments.go new file mode 100644 index 00000000..ef2cc3d8 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/comments.go @@ -0,0 +1,278 @@ +package ast + +import ( + "fmt" + "github.com/robertkrimen/otto/file" +) + +// CommentPosition determines where the comment is in a given context +type CommentPosition int + +const ( + _ CommentPosition = iota + LEADING // Before the pertinent expression + TRAILING // After the pertinent expression + KEY // Before a key in an object + COLON // After a colon in a field declaration + FINAL // Final comments in a block, not belonging to a specific expression or the comment after a trailing , in an array or object literal + IF // After an if keyword + WHILE // After a while keyword + DO // After do keyword + FOR // After a for keyword + WITH // After a with keyword + TBD +) + +// Comment contains the data of the comment +type Comment struct { + Begin file.Idx + Text string + Position CommentPosition +} + +// NewComment creates a new comment +func NewComment(text string, idx file.Idx) *Comment { + comment := &Comment{ + Begin: idx, + Text: text, + Position: TBD, + } + + return comment +} + +// String returns a stringified version of the position +func (cp CommentPosition) String() string { + switch cp { + case LEADING: + return "Leading" + case TRAILING: + return "Trailing" + case KEY: + return "Key" + case COLON: + return "Colon" + case FINAL: + return "Final" + case IF: + return "If" + case WHILE: + return "While" + case DO: + return "Do" + case FOR: + return "For" + case WITH: + return "With" + default: + return "???" + } +} + +// String returns a stringified version of the comment +func (c Comment) String() string { + return fmt.Sprintf("Comment: %v", c.Text) +} + +// Comments defines the current view of comments from the parser +type Comments struct { + // CommentMap is a reference to the parser comment map + CommentMap CommentMap + // Comments lists the comments scanned, not linked to a node yet + Comments []*Comment + // future lists the comments after a line break during a sequence of comments + future []*Comment + // Current is node for which comments are linked to + Current Expression + + // wasLineBreak determines if a line break occured while scanning for comments + wasLineBreak bool + // primary determines whether or not processing a primary expression + primary bool + // afterBlock determines whether or not being after a block statement + afterBlock bool +} + +func NewComments() *Comments { + comments := &Comments{ + CommentMap: CommentMap{}, + } + + return comments +} + +func (c *Comments) String() string { + return fmt.Sprintf("NODE: %v, Comments: %v, Future: %v(LINEBREAK:%v)", c.Current, len(c.Comments), len(c.future), c.wasLineBreak) +} + +// FetchAll returns all the currently scanned comments, +// including those from the next line +func (c *Comments) FetchAll() []*Comment { + defer func() { + c.Comments = nil + c.future = nil + }() + + return append(c.Comments, c.future...) +} + +// Fetch returns all the currently scanned comments +func (c *Comments) Fetch() []*Comment { + defer func() { + c.Comments = nil + }() + + return c.Comments +} + +// ResetLineBreak marks the beginning of a new statement +func (c *Comments) ResetLineBreak() { + c.wasLineBreak = false +} + +// MarkPrimary will mark the context as processing a primary expression +func (c *Comments) MarkPrimary() { + c.primary = true + c.wasLineBreak = false +} + +// AfterBlock will mark the context as being after a block. +func (c *Comments) AfterBlock() { + c.afterBlock = true +} + +// AddComment adds a comment to the view. +// Depending on the context, comments are added normally or as post line break. +func (c *Comments) AddComment(comment *Comment) { + if c.primary { + if !c.wasLineBreak { + c.Comments = append(c.Comments, comment) + } else { + c.future = append(c.future, comment) + } + } else { + if !c.wasLineBreak || (c.Current == nil && !c.afterBlock) { + c.Comments = append(c.Comments, comment) + } else { + c.future = append(c.future, comment) + } + } +} + +// MarkComments will mark the found comments as the given position. +func (c *Comments) MarkComments(position CommentPosition) { + for _, comment := range c.Comments { + if comment.Position == TBD { + comment.Position = position + } + } + for _, c := range c.future { + if c.Position == TBD { + c.Position = position + } + } +} + +// Unset the current node and apply the comments to the current expression. +// Resets context variables. +func (c *Comments) Unset() { + if c.Current != nil { + c.applyComments(c.Current, c.Current, TRAILING) + c.Current = nil + } + c.wasLineBreak = false + c.primary = false + c.afterBlock = false +} + +// SetExpression sets the current expression. +// It is applied the found comments, unless the previous expression has not been unset. +// It is skipped if the node is already set or if it is a part of the previous node. +func (c *Comments) SetExpression(node Expression) { + // Skipping same node + if c.Current == node { + return + } + if c.Current != nil && c.Current.Idx1() == node.Idx1() { + c.Current = node + return + } + previous := c.Current + c.Current = node + + // Apply the found comments and futures to the node and the previous. + c.applyComments(node, previous, TRAILING) +} + +// PostProcessNode applies all found comments to the given node +func (c *Comments) PostProcessNode(node Node) { + c.applyComments(node, nil, TRAILING) +} + +// applyComments applies both the comments and the future comments to the given node and the previous one, +// based on the context. +func (c *Comments) applyComments(node, previous Node, position CommentPosition) { + if previous != nil { + c.CommentMap.AddComments(previous, c.Comments, position) + c.Comments = nil + } else { + c.CommentMap.AddComments(node, c.Comments, position) + c.Comments = nil + } + // Only apply the future comments to the node if the previous is set. + // This is for detecting end of line comments and which node comments on the following lines belongs to + if previous != nil { + c.CommentMap.AddComments(node, c.future, position) + c.future = nil + } +} + +// AtLineBreak will mark a line break +func (c *Comments) AtLineBreak() { + c.wasLineBreak = true +} + +// CommentMap is the data structure where all found comments are stored +type CommentMap map[Node][]*Comment + +// AddComment adds a single comment to the map +func (cm CommentMap) AddComment(node Node, comment *Comment) { + list := cm[node] + list = append(list, comment) + + cm[node] = list +} + +// AddComments adds a slice of comments, given a node and an updated position +func (cm CommentMap) AddComments(node Node, comments []*Comment, position CommentPosition) { + for _, comment := range comments { + if comment.Position == TBD { + comment.Position = position + } + cm.AddComment(node, comment) + } +} + +// Size returns the size of the map +func (cm CommentMap) Size() int { + size := 0 + for _, comments := range cm { + size += len(comments) + } + + return size +} + +// MoveComments moves comments with a given position from a node to another +func (cm CommentMap) MoveComments(from, to Node, position CommentPosition) { + for i, c := range cm[from] { + if c.Position == position { + cm.AddComment(to, c) + + // Remove the comment from the "from" slice + cm[from][i] = cm[from][len(cm[from])-1] + cm[from][len(cm[from])-1] = nil + cm[from] = cm[from][:len(cm[from])-1] + } + } +} diff --git a/vendor/github.com/robertkrimen/otto/ast/comments_test.go b/vendor/github.com/robertkrimen/otto/ast/comments_test.go new file mode 100644 index 00000000..86d41b19 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/comments_test.go @@ -0,0 +1,61 @@ +package ast + +import ( + "github.com/robertkrimen/otto/file" + "testing" +) + +func TestCommentMap(t *testing.T) { + statement := &EmptyStatement{file.Idx(1)} + comment := &Comment{1, "test", LEADING} + + cm := CommentMap{} + cm.AddComment(statement, comment) + + if cm.Size() != 1 { + t.Errorf("the number of comments is %v, not 1", cm.Size()) + } + + if len(cm[statement]) != 1 { + t.Errorf("the number of comments is %v, not 1", cm.Size()) + } + + if cm[statement][0].Text != "test" { + t.Errorf("the text is %v, not \"test\"", cm[statement][0].Text) + } +} + +func TestCommentMap_move(t *testing.T) { + statement1 := &EmptyStatement{file.Idx(1)} + statement2 := &EmptyStatement{file.Idx(2)} + comment := &Comment{1, "test", LEADING} + + cm := CommentMap{} + cm.AddComment(statement1, comment) + + if cm.Size() != 1 { + t.Errorf("the number of comments is %v, not 1", cm.Size()) + } + + if len(cm[statement1]) != 1 { + t.Errorf("the number of comments is %v, not 1", cm.Size()) + } + + if len(cm[statement2]) != 0 { + t.Errorf("the number of comments is %v, not 0", cm.Size()) + } + + cm.MoveComments(statement1, statement2, LEADING) + + if cm.Size() != 1 { + t.Errorf("the number of comments is %v, not 1", cm.Size()) + } + + if len(cm[statement2]) != 1 { + t.Errorf("the number of comments is %v, not 1", cm.Size()) + } + + if len(cm[statement1]) != 0 { + t.Errorf("the number of comments is %v, not 0", cm.Size()) + } +} diff --git a/vendor/github.com/robertkrimen/otto/ast/node.go b/vendor/github.com/robertkrimen/otto/ast/node.go new file mode 100644 index 00000000..7e45abe9 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/node.go @@ -0,0 +1,515 @@ +/* +Package ast declares types representing a JavaScript AST. + +Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +*/ +package ast + +import ( + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +// All nodes implement the Node interface. +type Node interface { + Idx0() file.Idx // The index of the first character belonging to the node + Idx1() file.Idx // The index of the first character immediately after the node +} + +// ========== // +// Expression // +// ========== // + +type ( + // All expression nodes implement the Expression interface. + Expression interface { + Node + _expressionNode() + } + + ArrayLiteral struct { + LeftBracket file.Idx + RightBracket file.Idx + Value []Expression + } + + AssignExpression struct { + Operator token.Token + Left Expression + Right Expression + } + + BadExpression struct { + From file.Idx + To file.Idx + } + + BinaryExpression struct { + Operator token.Token + Left Expression + Right Expression + Comparison bool + } + + BooleanLiteral struct { + Idx file.Idx + Literal string + Value bool + } + + BracketExpression struct { + Left Expression + Member Expression + LeftBracket file.Idx + RightBracket file.Idx + } + + CallExpression struct { + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx + } + + ConditionalExpression struct { + Test Expression + Consequent Expression + Alternate Expression + } + + DotExpression struct { + Left Expression + Identifier *Identifier + } + + EmptyExpression struct { + Begin file.Idx + End file.Idx + } + + FunctionLiteral struct { + Function file.Idx + Name *Identifier + ParameterList *ParameterList + Body Statement + Source string + + DeclarationList []Declaration + } + + Identifier struct { + Name string + Idx file.Idx + } + + NewExpression struct { + New file.Idx + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx + } + + NullLiteral struct { + Idx file.Idx + Literal string + } + + NumberLiteral struct { + Idx file.Idx + Literal string + Value interface{} + } + + ObjectLiteral struct { + LeftBrace file.Idx + RightBrace file.Idx + Value []Property + } + + ParameterList struct { + Opening file.Idx + List []*Identifier + Closing file.Idx + } + + Property struct { + Key string + Kind string + Value Expression + } + + RegExpLiteral struct { + Idx file.Idx + Literal string + Pattern string + Flags string + Value string + } + + SequenceExpression struct { + Sequence []Expression + } + + StringLiteral struct { + Idx file.Idx + Literal string + Value string + } + + ThisExpression struct { + Idx file.Idx + } + + UnaryExpression struct { + Operator token.Token + Idx file.Idx // If a prefix operation + Operand Expression + Postfix bool + } + + VariableExpression struct { + Name string + Idx file.Idx + Initializer Expression + } +) + +// _expressionNode + +func (*ArrayLiteral) _expressionNode() {} +func (*AssignExpression) _expressionNode() {} +func (*BadExpression) _expressionNode() {} +func (*BinaryExpression) _expressionNode() {} +func (*BooleanLiteral) _expressionNode() {} +func (*BracketExpression) _expressionNode() {} +func (*CallExpression) _expressionNode() {} +func (*ConditionalExpression) _expressionNode() {} +func (*DotExpression) _expressionNode() {} +func (*EmptyExpression) _expressionNode() {} +func (*FunctionLiteral) _expressionNode() {} +func (*Identifier) _expressionNode() {} +func (*NewExpression) _expressionNode() {} +func (*NullLiteral) _expressionNode() {} +func (*NumberLiteral) _expressionNode() {} +func (*ObjectLiteral) _expressionNode() {} +func (*RegExpLiteral) _expressionNode() {} +func (*SequenceExpression) _expressionNode() {} +func (*StringLiteral) _expressionNode() {} +func (*ThisExpression) _expressionNode() {} +func (*UnaryExpression) _expressionNode() {} +func (*VariableExpression) _expressionNode() {} + +// ========= // +// Statement // +// ========= // + +type ( + // All statement nodes implement the Statement interface. + Statement interface { + Node + _statementNode() + } + + BadStatement struct { + From file.Idx + To file.Idx + } + + BlockStatement struct { + LeftBrace file.Idx + List []Statement + RightBrace file.Idx + } + + BranchStatement struct { + Idx file.Idx + Token token.Token + Label *Identifier + } + + CaseStatement struct { + Case file.Idx + Test Expression + Consequent []Statement + } + + CatchStatement struct { + Catch file.Idx + Parameter *Identifier + Body Statement + } + + DebuggerStatement struct { + Debugger file.Idx + } + + DoWhileStatement struct { + Do file.Idx + Test Expression + Body Statement + } + + EmptyStatement struct { + Semicolon file.Idx + } + + ExpressionStatement struct { + Expression Expression + } + + ForInStatement struct { + For file.Idx + Into Expression + Source Expression + Body Statement + } + + ForStatement struct { + For file.Idx + Initializer Expression + Update Expression + Test Expression + Body Statement + } + + FunctionStatement struct { + Function *FunctionLiteral + } + + IfStatement struct { + If file.Idx + Test Expression + Consequent Statement + Alternate Statement + } + + LabelledStatement struct { + Label *Identifier + Colon file.Idx + Statement Statement + } + + ReturnStatement struct { + Return file.Idx + Argument Expression + } + + SwitchStatement struct { + Switch file.Idx + Discriminant Expression + Default int + Body []*CaseStatement + } + + ThrowStatement struct { + Throw file.Idx + Argument Expression + } + + TryStatement struct { + Try file.Idx + Body Statement + Catch *CatchStatement + Finally Statement + } + + VariableStatement struct { + Var file.Idx + List []Expression + } + + WhileStatement struct { + While file.Idx + Test Expression + Body Statement + } + + WithStatement struct { + With file.Idx + Object Expression + Body Statement + } +) + +// _statementNode + +func (*BadStatement) _statementNode() {} +func (*BlockStatement) _statementNode() {} +func (*BranchStatement) _statementNode() {} +func (*CaseStatement) _statementNode() {} +func (*CatchStatement) _statementNode() {} +func (*DebuggerStatement) _statementNode() {} +func (*DoWhileStatement) _statementNode() {} +func (*EmptyStatement) _statementNode() {} +func (*ExpressionStatement) _statementNode() {} +func (*ForInStatement) _statementNode() {} +func (*ForStatement) _statementNode() {} +func (*FunctionStatement) _statementNode() {} +func (*IfStatement) _statementNode() {} +func (*LabelledStatement) _statementNode() {} +func (*ReturnStatement) _statementNode() {} +func (*SwitchStatement) _statementNode() {} +func (*ThrowStatement) _statementNode() {} +func (*TryStatement) _statementNode() {} +func (*VariableStatement) _statementNode() {} +func (*WhileStatement) _statementNode() {} +func (*WithStatement) _statementNode() {} + +// =========== // +// Declaration // +// =========== // + +type ( + // All declaration nodes implement the Declaration interface. + Declaration interface { + _declarationNode() + } + + FunctionDeclaration struct { + Function *FunctionLiteral + } + + VariableDeclaration struct { + Var file.Idx + List []*VariableExpression + } +) + +// _declarationNode + +func (*FunctionDeclaration) _declarationNode() {} +func (*VariableDeclaration) _declarationNode() {} + +// ==== // +// Node // +// ==== // + +type Program struct { + Body []Statement + + DeclarationList []Declaration + + File *file.File + + Comments CommentMap +} + +// ==== // +// Idx0 // +// ==== // + +func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket } +func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *BadExpression) Idx0() file.Idx { return self.From } +func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *BooleanLiteral) Idx0() file.Idx { return self.Idx } +func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() } +func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() } +func (self *DotExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *EmptyExpression) Idx0() file.Idx { return self.Begin } +func (self *FunctionLiteral) Idx0() file.Idx { return self.Function } +func (self *Identifier) Idx0() file.Idx { return self.Idx } +func (self *NewExpression) Idx0() file.Idx { return self.New } +func (self *NullLiteral) Idx0() file.Idx { return self.Idx } +func (self *NumberLiteral) Idx0() file.Idx { return self.Idx } +func (self *ObjectLiteral) Idx0() file.Idx { return self.LeftBrace } +func (self *RegExpLiteral) Idx0() file.Idx { return self.Idx } +func (self *SequenceExpression) Idx0() file.Idx { return self.Sequence[0].Idx0() } +func (self *StringLiteral) Idx0() file.Idx { return self.Idx } +func (self *ThisExpression) Idx0() file.Idx { return self.Idx } +func (self *UnaryExpression) Idx0() file.Idx { return self.Idx } +func (self *VariableExpression) Idx0() file.Idx { return self.Idx } + +func (self *BadStatement) Idx0() file.Idx { return self.From } +func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace } +func (self *BranchStatement) Idx0() file.Idx { return self.Idx } +func (self *CaseStatement) Idx0() file.Idx { return self.Case } +func (self *CatchStatement) Idx0() file.Idx { return self.Catch } +func (self *DebuggerStatement) Idx0() file.Idx { return self.Debugger } +func (self *DoWhileStatement) Idx0() file.Idx { return self.Do } +func (self *EmptyStatement) Idx0() file.Idx { return self.Semicolon } +func (self *ExpressionStatement) Idx0() file.Idx { return self.Expression.Idx0() } +func (self *ForInStatement) Idx0() file.Idx { return self.For } +func (self *ForStatement) Idx0() file.Idx { return self.For } +func (self *FunctionStatement) Idx0() file.Idx { return self.Function.Idx0() } +func (self *IfStatement) Idx0() file.Idx { return self.If } +func (self *LabelledStatement) Idx0() file.Idx { return self.Label.Idx0() } +func (self *Program) Idx0() file.Idx { return self.Body[0].Idx0() } +func (self *ReturnStatement) Idx0() file.Idx { return self.Return } +func (self *SwitchStatement) Idx0() file.Idx { return self.Switch } +func (self *ThrowStatement) Idx0() file.Idx { return self.Throw } +func (self *TryStatement) Idx0() file.Idx { return self.Try } +func (self *VariableStatement) Idx0() file.Idx { return self.Var } +func (self *WhileStatement) Idx0() file.Idx { return self.While } +func (self *WithStatement) Idx0() file.Idx { return self.With } + +// ==== // +// Idx1 // +// ==== // + +func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket } +func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() } +func (self *BadExpression) Idx1() file.Idx { return self.To } +func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() } +func (self *BooleanLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + 1 } +func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } +func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() } +func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1() } +func (self *EmptyExpression) Idx1() file.Idx { return self.End } +func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() } +func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) } +func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } +func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null" +func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace } +func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() } +func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *ThisExpression) Idx1() file.Idx { return self.Idx + 4 } +func (self *UnaryExpression) Idx1() file.Idx { + if self.Postfix { + return self.Operand.Idx1() + 2 // ++ -- + } + return self.Operand.Idx1() +} +func (self *VariableExpression) Idx1() file.Idx { + if self.Initializer == nil { + return file.Idx(int(self.Idx) + len(self.Name) + 1) + } + return self.Initializer.Idx1() +} + +func (self *BadStatement) Idx1() file.Idx { return self.To } +func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 } +func (self *BranchStatement) Idx1() file.Idx { return self.Idx } +func (self *CaseStatement) Idx1() file.Idx { return self.Consequent[len(self.Consequent)-1].Idx1() } +func (self *CatchStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *DebuggerStatement) Idx1() file.Idx { return self.Debugger + 8 } +func (self *DoWhileStatement) Idx1() file.Idx { return self.Test.Idx1() } +func (self *EmptyStatement) Idx1() file.Idx { return self.Semicolon + 1 } +func (self *ExpressionStatement) Idx1() file.Idx { return self.Expression.Idx1() } +func (self *ForInStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *ForStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *FunctionStatement) Idx1() file.Idx { return self.Function.Idx1() } +func (self *IfStatement) Idx1() file.Idx { + if self.Alternate != nil { + return self.Alternate.Idx1() + } + return self.Consequent.Idx1() +} +func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 } +func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } +func (self *ReturnStatement) Idx1() file.Idx { return self.Return } +func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } +func (self *ThrowStatement) Idx1() file.Idx { return self.Throw } +func (self *TryStatement) Idx1() file.Idx { return self.Try } +func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() } +func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() } diff --git a/vendor/github.com/robertkrimen/otto/ast/walk.go b/vendor/github.com/robertkrimen/otto/ast/walk.go new file mode 100644 index 00000000..7580a82b --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/walk.go @@ -0,0 +1,217 @@ +package ast + +import "fmt" + +// Visitor Enter method is invoked for each node encountered by Walk. +// If the result visitor w is not nil, Walk visits each of the children +// of node with the visitor v, followed by a call of the Exit method. +type Visitor interface { + Enter(n Node) (v Visitor) + Exit(n Node) +} + +// Walk traverses an AST in depth-first order: It starts by calling +// v.Enter(node); node must not be nil. If the visitor v returned by +// v.Enter(node) is not nil, Walk is invoked recursively with visitor +// v for each of the non-nil children of node, followed by a call +// of v.Exit(node). +func Walk(v Visitor, n Node) { + if n == nil { + return + } + if v = v.Enter(n); v == nil { + return + } + + defer v.Exit(n) + + switch n := n.(type) { + case *ArrayLiteral: + if n != nil { + for _, ex := range n.Value { + Walk(v, ex) + } + } + case *AssignExpression: + if n != nil { + Walk(v, n.Left) + Walk(v, n.Right) + } + case *BadExpression: + case *BinaryExpression: + if n != nil { + Walk(v, n.Left) + Walk(v, n.Right) + } + case *BlockStatement: + if n != nil { + for _, s := range n.List { + Walk(v, s) + } + } + case *BooleanLiteral: + case *BracketExpression: + if n != nil { + Walk(v, n.Left) + Walk(v, n.Member) + } + case *BranchStatement: + if n != nil { + Walk(v, n.Label) + } + case *CallExpression: + if n != nil { + Walk(v, n.Callee) + for _, a := range n.ArgumentList { + Walk(v, a) + } + } + case *CaseStatement: + if n != nil { + Walk(v, n.Test) + for _, c := range n.Consequent { + Walk(v, c) + } + } + case *CatchStatement: + if n != nil { + Walk(v, n.Parameter) + Walk(v, n.Body) + } + case *ConditionalExpression: + if n != nil { + Walk(v, n.Test) + Walk(v, n.Consequent) + Walk(v, n.Alternate) + } + case *DebuggerStatement: + case *DoWhileStatement: + if n != nil { + Walk(v, n.Test) + Walk(v, n.Body) + } + case *DotExpression: + if n != nil { + Walk(v, n.Left) + } + case *EmptyExpression: + case *EmptyStatement: + case *ExpressionStatement: + if n != nil { + Walk(v, n.Expression) + } + case *ForInStatement: + if n != nil { + Walk(v, n.Into) + Walk(v, n.Source) + Walk(v, n.Body) + } + case *ForStatement: + if n != nil { + Walk(v, n.Initializer) + Walk(v, n.Update) + Walk(v, n.Test) + Walk(v, n.Body) + } + case *FunctionLiteral: + if n != nil { + Walk(v, n.Name) + for _, p := range n.ParameterList.List { + Walk(v, p) + } + Walk(v, n.Body) + } + case *FunctionStatement: + if n != nil { + Walk(v, n.Function) + } + case *Identifier: + case *IfStatement: + if n != nil { + Walk(v, n.Test) + Walk(v, n.Consequent) + Walk(v, n.Alternate) + } + case *LabelledStatement: + if n != nil { + Walk(v, n.Statement) + } + case *NewExpression: + if n != nil { + Walk(v, n.Callee) + for _, a := range n.ArgumentList { + Walk(v, a) + } + } + case *NullLiteral: + case *NumberLiteral: + case *ObjectLiteral: + if n != nil { + for _, p := range n.Value { + Walk(v, p.Value) + } + } + case *Program: + if n != nil { + for _, b := range n.Body { + Walk(v, b) + } + } + case *RegExpLiteral: + case *ReturnStatement: + if n != nil { + Walk(v, n.Argument) + } + case *SequenceExpression: + if n != nil { + for _, e := range n.Sequence { + Walk(v, e) + } + } + case *StringLiteral: + case *SwitchStatement: + if n != nil { + Walk(v, n.Discriminant) + for _, c := range n.Body { + Walk(v, c) + } + } + case *ThisExpression: + case *ThrowStatement: + if n != nil { + Walk(v, n.Argument) + } + case *TryStatement: + if n != nil { + Walk(v, n.Body) + Walk(v, n.Catch) + Walk(v, n.Finally) + } + case *UnaryExpression: + if n != nil { + Walk(v, n.Operand) + } + case *VariableExpression: + if n != nil { + Walk(v, n.Initializer) + } + case *VariableStatement: + if n != nil { + for _, e := range n.List { + Walk(v, e) + } + } + case *WhileStatement: + if n != nil { + Walk(v, n.Test) + Walk(v, n.Body) + } + case *WithStatement: + if n != nil { + Walk(v, n.Object) + Walk(v, n.Body) + } + default: + panic(fmt.Sprintf("Walk: unexpected node type %T", n)) + } +} diff --git a/vendor/github.com/robertkrimen/otto/ast/walk_example_test.go b/vendor/github.com/robertkrimen/otto/ast/walk_example_test.go new file mode 100644 index 00000000..7590e06d --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/walk_example_test.go @@ -0,0 +1,52 @@ +package ast_test + +import ( + "fmt" + "log" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/parser" +) + +type walkExample struct { + source string + shift file.Idx +} + +func (w *walkExample) Enter(n ast.Node) ast.Visitor { + if id, ok := n.(*ast.Identifier); ok && id != nil { + idx := n.Idx0() + w.shift - 1 + s := w.source[:idx] + "new_" + w.source[idx:] + w.source = s + w.shift += 4 + } + if v, ok := n.(*ast.VariableExpression); ok && v != nil { + idx := n.Idx0() + w.shift - 1 + s := w.source[:idx] + "varnew_" + w.source[idx:] + w.source = s + w.shift += 7 + } + + return w +} + +func (w *walkExample) Exit(n ast.Node) { + // AST node n has had all its children walked. Pop it out of your + // stack, or do whatever processing you need to do, if any. +} + +func ExampleVisitor_codeRewrite() { + source := `var b = function() {test(); try {} catch(e) {} var test = "test(); var test = 1"} // test` + program, err := parser.ParseFile(nil, "", source, 0) + if err != nil { + log.Fatal(err) + } + + w := &walkExample{source: source} + + ast.Walk(w, program) + + fmt.Println(w.source) + // Output: var varnew_b = function() {new_test(); try {} catch(new_e) {} var varnew_test = "test(); var test = 1"} // test +} diff --git a/vendor/github.com/robertkrimen/otto/ast/walk_test.go b/vendor/github.com/robertkrimen/otto/ast/walk_test.go new file mode 100644 index 00000000..14440998 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/walk_test.go @@ -0,0 +1,82 @@ +package ast_test + +import ( + "log" + "testing" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/parser" +) + +type walker struct { + stack []ast.Node + source string + shift file.Idx +} + +// push and pop below are to prove the symmetry of Enter/Exit calls + +func (w *walker) push(n ast.Node) { + w.stack = append(w.stack, n) +} + +func (w *walker) pop(n ast.Node) { + size := len(w.stack) + if size <= 0 { + panic("pop of empty stack") + } + + toPop := w.stack[size-1] + if toPop != n { + panic("pop: nodes do not equal") + } + + w.stack[size-1] = nil + w.stack = w.stack[:size-1] +} + +func (w *walker) Enter(n ast.Node) ast.Visitor { + w.push(n) + + if id, ok := n.(*ast.Identifier); ok && id != nil { + idx := n.Idx0() + w.shift - 1 + s := w.source[:idx] + "new_" + w.source[idx:] + w.source = s + w.shift += 4 + } + if v, ok := n.(*ast.VariableExpression); ok && v != nil { + idx := n.Idx0() + w.shift - 1 + s := w.source[:idx] + "varnew_" + w.source[idx:] + w.source = s + w.shift += 7 + } + + return w +} + +func (w *walker) Exit(n ast.Node) { + w.pop(n) +} + +func TestVisitorRewrite(t *testing.T) { + source := `var b = function() {test(); try {} catch(e) {} var test = "test(); var test = 1"} // test` + program, err := parser.ParseFile(nil, "", source, 0) + if err != nil { + log.Fatal(err) + } + + w := &walker{source: source} + + ast.Walk(w, program) + + xformed := `var varnew_b = function() {new_test(); try {} catch(new_e) {} var varnew_test = "test(); var test = 1"} // test` + + if w.source != xformed { + t.Errorf("source is `%s` not `%s`", w.source, xformed) + } + + if len(w.stack) != 0 { + t.Errorf("stack should be empty, but is length: %d", len(w.stack)) + } +} diff --git a/vendor/github.com/robertkrimen/otto/bug_test.go b/vendor/github.com/robertkrimen/otto/bug_test.go new file mode 100644 index 00000000..811dc856 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/bug_test.go @@ -0,0 +1,695 @@ +package otto + +import ( + "testing" + "time" +) + +func Test_issue116(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [1,-1].sort(function(a, b) { + return a - b; + }); + `, "-1,1") + }) +} + +func Test_262(t *testing.T) { + tt(t, func() { + test, _ := test() + + // 11.13.1-1-1 + test(`raise: + eval("42 = 42;"); + `, "ReferenceError: Invalid left-hand side in assignment") + }) +} + +func Test_issue5(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`'abc' === 'def'`, false) + test(`'\t' === '\r'`, false) + }) +} + +func Test_issue13(t *testing.T) { + tt(t, func() { + test, tester := test() + vm := tester.vm + + value, err := vm.ToValue(map[string]interface{}{ + "string": "Xyzzy", + "number": 42, + "array": []string{"def", "ghi"}, + }) + if err != nil { + t.Error(err) + t.FailNow() + } + + fn, err := vm.Object(` + (function(value){ + return ""+[value.string, value.number, value.array] + }) + `) + if err != nil { + t.Error(err) + t.FailNow() + } + + result, err := fn.Value().Call(fn.Value(), value) + if err != nil { + t.Error(err) + t.FailNow() + } + is(result.string(), "Xyzzy,42,def,ghi") + + anything := struct { + Abc interface{} + }{ + Abc: map[string]interface{}{ + "def": []interface{}{ + []interface{}{ + "a", "b", "c", "", "d", "e", + }, + map[string]interface{}{ + "jkl": "Nothing happens.", + }, + }, + "ghi": -1, + }, + } + + vm.Set("anything", anything) + test(` + [ + anything, + "~", + anything.Abc, + "~", + anything.Abc.def, + "~", + anything.Abc.def[1].jkl, + "~", + anything.Abc.ghi, + ]; + `, "[object Object],~,[object Object],~,a,b,c,,d,e,[object Object],~,Nothing happens.,~,-1") + }) +} + +func Test_issue16(t *testing.T) { + tt(t, func() { + test, vm := test() + + test(` + var def = { + "abc": ["abc"], + "xyz": ["xyz"] + }; + def.abc.concat(def.xyz); + `, "abc,xyz") + + vm.Set("ghi", []string{"jkl", "mno"}) + + test(` + def.abc.concat(def.xyz).concat(ghi); + `, "abc,xyz,jkl,mno") + + test(` + ghi.concat(def.abc.concat(def.xyz)); + `, "jkl,mno,abc,xyz") + + vm.Set("pqr", []interface{}{"jkl", 42, 3.14159, true}) + + test(` + pqr.concat(ghi, def.abc, def, def.xyz); + `, "jkl,42,3.14159,true,jkl,mno,abc,[object Object],xyz") + + test(` + pqr.concat(ghi, def.abc, def, def.xyz).length; + `, 9) + }) +} + +func Test_issue21(t *testing.T) { + tt(t, func() { + vm1 := New() + vm1.Run(` + abc = {} + abc.ghi = "Nothing happens."; + var jkl = 0; + abc.def = function() { + jkl += 1; + return 1; + } + `) + abc, err := vm1.Get("abc") + is(err, nil) + + vm2 := New() + vm2.Set("cba", abc) + _, err = vm2.Run(` + var pqr = 0; + cba.mno = function() { + pqr -= 1; + return 1; + } + cba.def(); + cba.def(); + cba.def(); + `) + is(err, nil) + + jkl, err := vm1.Get("jkl") + is(err, nil) + is(jkl, 3) + + _, err = vm1.Run(` + abc.mno(); + abc.mno(); + abc.mno(); + `) + is(err, nil) + + pqr, err := vm2.Get("pqr") + is(err, nil) + is(pqr, -3) + }) +} + +func Test_issue24(t *testing.T) { + tt(t, func() { + _, vm := test() + + { + vm.Set("abc", []string{"abc", "def", "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.([]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", [...]string{"abc", "def", "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.([3]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", &[...]string{"abc", "def", "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(*[3]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", map[int]string{0: "abc", 1: "def", 2: "ghi"}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(map[int]string) + is(valid, true) + + is(value[0], "abc") + is(value[2], "ghi") + } + } + + { + vm.Set("abc", _abcStruct{Abc: true, Ghi: "Nothing happens."}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(_abcStruct) + is(valid, true) + + is(value.Abc, true) + is(value.Ghi, "Nothing happens.") + } + } + + { + vm.Set("abc", &_abcStruct{Abc: true, Ghi: "Nothing happens."}) + value, err := vm.Get("abc") + is(err, nil) + export, _ := value.Export() + { + value, valid := export.(*_abcStruct) + is(valid, true) + + is(value.Abc, true) + is(value.Ghi, "Nothing happens.") + } + } + }) +} + +func Test_issue39(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0, def = [], ghi = function() { + if (abc < 10) return ++abc; + return undefined; + } + for (var jkl; (jkl = ghi());) def.push(jkl); + def; + `, "1,2,3,4,5,6,7,8,9,10") + + test(` + var abc = ["1", "2", "3", "4"]; + var def = []; + for (var ghi; (ghi = abc.shift());) { + def.push(ghi); + } + def; + `, "1,2,3,4") + }) +} + +func Test_issue64(t *testing.T) { + tt(t, func() { + test, vm := test() + + defer mockTimeLocal(time.UTC)() + + abc := map[string]interface{}{ + "time": time.Unix(0, 0), + } + vm.Set("abc", abc) + + def := struct { + Public string + private string + }{ + "Public", "private", + } + vm.Set("def", def) + + test(`"sec" in abc.time`, false) + + test(` + [ "Public" in def, "private" in def, def.Public, def.private ]; + `, "true,false,Public,") + + test(`JSON.stringify(abc)`, `{"time":"1970-01-01T00:00:00Z"}`) + }) +} + +func Test_issue73(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("abc", [4]int{3, 2, 1, 0}) + + test(` + var def = [ 0, 1, 2, 3 ]; + JSON.stringify(def) + JSON.stringify(abc); + `, "[0,1,2,3][3,2,1,0]") + }) +} + +func Test_7_3_1(t *testing.T) { + tt(t, func() { + test(` + eval("var test7_3_1\u2028abc = 66;"); + [ abc, typeof test7_3_1 ]; + `, "66,undefined") + }) +} + +func Test_7_3_3(t *testing.T) { + tt(t, func() { + test(`raise: + eval("//\u2028 =;"); + `, "SyntaxError: Unexpected token =") + }) +} + +func Test_S7_3_A2_1_T1(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + eval("'\u000Astr\u000Aing\u000A'") + `, "SyntaxError: Unexpected token ILLEGAL") + }) +} + +func Test_S7_8_3_A2_1_T1(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ .0 === 0.0, .0, .1 === 0.1, .1 ] + `, "true,0,true,0.1") + }) +} + +func Test_S7_8_4_A4_2_T3(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + "\a" + `, "a") + }) +} + +func Test_S7_9_A1(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var def; + abc: for (var i = 0; i <= 0; i++) { + for (var j = 0; j <= 1; j++) { + if (j === 0) { + continue abc; + } else { + def = true; + } + } + } + [ def, i, j ]; + `, ",1,0") + }) +} + +func Test_S7_9_A3(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + (function(){ + return + 1; + })() + `, "undefined") + }) +} + +func Test_7_3_10(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + eval("var \u0061\u0062\u0063 = 3.14159;"); + abc; + `, 3.14159) + + test(` + abc = undefined; + eval("var \\u0061\\u0062\\u0063 = 3.14159;"); + abc; + `, 3.14159) + }) +} + +func Test_bug(t *testing.T) { + tt(t, func() { + test, _ := test() + + // 10.4.2-1-5 + test(` + "abc\ +def" + `, "abcdef") + + test(` + eval("'abc';\ + 'def'") + `, "def") + + // S12.6.1_A10 + test(` + var abc = 0; + do { + if(typeof(def) === "function"){ + abc = -1; + break; + } else { + abc = 1; + break; + } + } while(function def(){}); + abc; + `, 1) + + // S12.7_A7 + test(`raise: + abc: + while (true) { + eval("continue abc"); + } + `, "SyntaxError: Undefined label 'abc'") + + // S15.1.2.1_A3.3_T3 + test(`raise: + eval("return"); + `, "SyntaxError: Illegal return statement") + + // 15.2.3.3-2-33 + test(` + var abc = { "AB\n\\cd": 1 }; + Object.getOwnPropertyDescriptor(abc, "AB\n\\cd").value; + `, 1) + + // S15.3_A2_T1 + test(`raise: + Function.call(this, "var x / = 1;"); + `, "SyntaxError: Unexpected token /") + + // ? + test(` + (function(){ + var abc = []; + (function(){ + abc.push(0); + abc.push(1); + })(undefined); + if ((function(){ return true; })()) { + (function(){ + abc.push(2); + })(); + } + return abc; + })(); + `, "0,1,2") + + if false { + // 15.9.5.43-0-10 + // Should be an invalid date + test(` + date = new Date(1970, 0, -99999999, 0, 0, 0, 1); + `, "") + } + + // S7.8.3_A1.2_T1 + test(` + [ 0e1, 1e1, 2e1, 3e1, 4e1, 5e1, 6e1, 7e1, 8e1, 9e1 ]; + `, "0,10,20,30,40,50,60,70,80,90") + + // S15.10.2.7_A3_T2 + test(` + var abc = /\s+abc\s+/.exec("\t abc def"); + [ abc.length, abc.index, abc.input, abc ]; + `, "1,0,\t abc def,\t abc ") + }) +} + +func Test_issue79(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("abc", []_abcStruct{ + { + Ghi: "一", + Def: 1, + }, + { + Def: 3, + Ghi: "三", + }, + { + Def: 2, + Ghi: "二", + }, + { + Def: 4, + Ghi: "å››", + }, + }) + + test(` + abc.sort(function(a,b){ return b.Def-a.Def }); + def = []; + for (i = 0; i < abc.length; i++) { + def.push(abc[i].String()) + } + def; + `, "å››,三,二,一") + }) +} + +func Test_issue80(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + JSON.stringify([ + 1401868959, + 14018689591, + 140186895901, + 1401868959001, + 14018689590001, + 140186895900001, + 1401868959000001, + ]); + `, "[1401868959,14018689591,140186895901,1401868959001,14018689590001,140186895900001,1401868959000001]") + }) +} + +func Test_issue86(t *testing.T) { + tt(t, func() { + test, tester := test() + + test(` + var obj = Object.create({}, { + abc: { + get: function(){ + return 1; + } + } + }); + obj.abc; + `, 1) + + v, err := tester.vm.Copy().Run(`obj.abc;`) + is(is(v, 1), is(nil, err)) + }) +} + +func Test_issue87(t *testing.T) { + tt(t, func() { + test, vm := test() + + test(` + var def = 0; + abc: { + for (;;) { + def = !1; + break abc; + } + def = !0; + } + def; + `, false) + + _, err := vm.Run(` +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, +r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< +32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>3]|=parseInt(a.substr(b, +2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}}, +u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;gn;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]= +c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes; +d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math); +(function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j 2 (ASCII 50) +47 +// radix 11 => A/a (ASCII 65/97) +54/+86 +var parseInt_alphabetTable = func() []string { + table := []string{"", "", "01"} + for radix := 3; radix <= 36; radix += 1 { + alphabet := table[radix-1] + if radix <= 10 { + alphabet += string(radix + 47) + } else { + alphabet += string(radix+54) + string(radix+86) + } + table = append(table, alphabet) + } + return table +}() + +func digitValue(chr rune) int { + switch { + case '0' <= chr && chr <= '9': + return int(chr - '0') + case 'a' <= chr && chr <= 'z': + return int(chr - 'a' + 10) + case 'A' <= chr && chr <= 'Z': + return int(chr - 'A' + 10) + } + return 36 // Larger than any legal digit value +} + +func builtinGlobal_parseInt(call FunctionCall) Value { + input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace) + if len(input) == 0 { + return NaNValue() + } + + radix := int(toInt32(call.Argument(1))) + + negative := false + switch input[0] { + case '+': + input = input[1:] + case '-': + negative = true + input = input[1:] + } + + strip := true + if radix == 0 { + radix = 10 + } else { + if radix < 2 || radix > 36 { + return NaNValue() + } else if radix != 16 { + strip = false + } + } + + switch len(input) { + case 0: + return NaNValue() + case 1: + default: + if strip { + if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { + input = input[2:] + radix = 16 + } + } + } + + base := radix + index := 0 + for ; index < len(input); index++ { + digit := digitValue(rune(input[index])) // If not ASCII, then an error anyway + if digit >= base { + break + } + } + input = input[0:index] + + value, err := strconv.ParseInt(input, radix, 64) + if err != nil { + if err.(*strconv.NumError).Err == strconv.ErrRange { + base := float64(base) + // Could just be a very large number (e.g. 0x8000000000000000) + var value float64 + for _, chr := range input { + digit := float64(digitValue(chr)) + if digit >= base { + goto error + } + value = value*base + digit + } + if negative { + value *= -1 + } + return toValue_float64(value) + } + error: + return NaNValue() + } + if negative { + value *= -1 + } + + return toValue_int64(value) +} + +var parseFloat_matchBadSpecial = regexp.MustCompile(`[\+\-]?(?:[Ii]nf$|infinity)`) +var parseFloat_matchValid = regexp.MustCompile(`[0-9eE\+\-\.]|Infinity`) + +func builtinGlobal_parseFloat(call FunctionCall) Value { + // Caveat emptor: This implementation does NOT match the specification + input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace) + + if parseFloat_matchBadSpecial.MatchString(input) { + return NaNValue() + } + value, err := strconv.ParseFloat(input, 64) + if err != nil { + for end := len(input); end > 0; end-- { + input := input[0:end] + if !parseFloat_matchValid.MatchString(input) { + return NaNValue() + } + value, err = strconv.ParseFloat(input, 64) + if err == nil { + break + } + } + if err != nil { + return NaNValue() + } + } + return toValue_float64(value) +} + +// encodeURI/decodeURI + +func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value { + value := call.Argument(0) + var input []uint16 + switch vl := value.value.(type) { + case []uint16: + input = vl + default: + input = utf16.Encode([]rune(value.string())) + } + if len(input) == 0 { + return toValue_string("") + } + output := []byte{} + length := len(input) + encode := make([]byte, 4) + for index := 0; index < length; { + value := input[index] + decode := utf16.Decode(input[index : index+1]) + if value >= 0xDC00 && value <= 0xDFFF { + panic(call.runtime.panicURIError("URI malformed")) + } + if value >= 0xD800 && value <= 0xDBFF { + index += 1 + if index >= length { + panic(call.runtime.panicURIError("URI malformed")) + } + // input = ..., value, value1, ... + value1 := input[index] + if value1 < 0xDC00 || value1 > 0xDFFF { + panic(call.runtime.panicURIError("URI malformed")) + } + decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000} + } + index += 1 + size := utf8.EncodeRune(encode, decode[0]) + encode := encode[0:size] + output = append(output, encode...) + } + { + value := escape.ReplaceAllFunc(output, func(target []byte) []byte { + // Probably a better way of doing this + if target[0] == ' ' { + return []byte("%20") + } + return []byte(url.QueryEscape(string(target))) + }) + return toValue_string(string(value)) + } +} + +var encodeURI_Regexp = regexp.MustCompile(`([^~!@#$&*()=:/,;?+'])`) + +func builtinGlobal_encodeURI(call FunctionCall) Value { + return _builtinGlobal_encodeURI(call, encodeURI_Regexp) +} + +var encodeURIComponent_Regexp = regexp.MustCompile(`([^~!*()'])`) + +func builtinGlobal_encodeURIComponent(call FunctionCall) Value { + return _builtinGlobal_encodeURI(call, encodeURIComponent_Regexp) +} + +// 3B/2F/3F/3A/40/26/3D/2B/24/2C/23 +var decodeURI_guard = regexp.MustCompile(`(?i)(?:%)(3B|2F|3F|3A|40|26|3D|2B|24|2C|23)`) + +func _decodeURI(input string, reserve bool) (string, bool) { + if reserve { + input = decodeURI_guard.ReplaceAllString(input, "%25$1") + } + input = strings.Replace(input, "+", "%2B", -1) // Ugly hack to make QueryUnescape work with our use case + output, err := url.QueryUnescape(input) + if err != nil || !utf8.ValidString(output) { + return "", true + } + return output, false +} + +func builtinGlobal_decodeURI(call FunctionCall) Value { + output, err := _decodeURI(call.Argument(0).string(), true) + if err { + panic(call.runtime.panicURIError("URI malformed")) + } + return toValue_string(output) +} + +func builtinGlobal_decodeURIComponent(call FunctionCall) Value { + output, err := _decodeURI(call.Argument(0).string(), false) + if err { + panic(call.runtime.panicURIError("URI malformed")) + } + return toValue_string(output) +} + +// escape/unescape + +func builtin_shouldEscape(chr byte) bool { + if 'A' <= chr && chr <= 'Z' || 'a' <= chr && chr <= 'z' || '0' <= chr && chr <= '9' { + return false + } + return !strings.ContainsRune("*_+-./", rune(chr)) +} + +const escapeBase16 = "0123456789ABCDEF" + +func builtin_escape(input string) string { + output := make([]byte, 0, len(input)) + length := len(input) + for index := 0; index < length; { + if builtin_shouldEscape(input[index]) { + chr, width := utf8.DecodeRuneInString(input[index:]) + chr16 := utf16.Encode([]rune{chr})[0] + if 256 > chr16 { + output = append(output, '%', + escapeBase16[chr16>>4], + escapeBase16[chr16&15], + ) + } else { + output = append(output, '%', 'u', + escapeBase16[chr16>>12], + escapeBase16[(chr16>>8)&15], + escapeBase16[(chr16>>4)&15], + escapeBase16[chr16&15], + ) + } + index += width + + } else { + output = append(output, input[index]) + index += 1 + } + } + return string(output) +} + +func builtin_unescape(input string) string { + output := make([]rune, 0, len(input)) + length := len(input) + for index := 0; index < length; { + if input[index] == '%' { + if index <= length-6 && input[index+1] == 'u' { + byte16, err := hex.DecodeString(input[index+2 : index+6]) + if err == nil { + value := uint16(byte16[0])<<8 + uint16(byte16[1]) + chr := utf16.Decode([]uint16{value})[0] + output = append(output, chr) + index += 6 + continue + } + } + if index <= length-3 { + byte8, err := hex.DecodeString(input[index+1 : index+3]) + if err == nil { + value := uint16(byte8[0]) + chr := utf16.Decode([]uint16{value})[0] + output = append(output, chr) + index += 3 + continue + } + } + } + output = append(output, rune(input[index])) + index += 1 + } + return string(output) +} + +func builtinGlobal_escape(call FunctionCall) Value { + return toValue_string(builtin_escape(call.Argument(0).string())) +} + +func builtinGlobal_unescape(call FunctionCall) Value { + return toValue_string(builtin_unescape(call.Argument(0).string())) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_array.go b/vendor/github.com/robertkrimen/otto/builtin_array.go new file mode 100644 index 00000000..56dd95ab --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_array.go @@ -0,0 +1,684 @@ +package otto + +import ( + "strconv" + "strings" +) + +// Array + +func builtinArray(call FunctionCall) Value { + return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList)) +} + +func builtinNewArray(self *_object, argumentList []Value) Value { + return toValue_object(builtinNewArrayNative(self.runtime, argumentList)) +} + +func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object { + if len(argumentList) == 1 { + firstArgument := argumentList[0] + if firstArgument.IsNumber() { + return runtime.newArray(arrayUint32(runtime, firstArgument)) + } + } + return runtime.newArrayOf(argumentList) +} + +func builtinArray_toString(call FunctionCall) Value { + thisObject := call.thisObject() + join := thisObject.get("join") + if join.isCallable() { + join := join._object() + return join.call(call.This, call.ArgumentList, false, nativeFrame) + } + return builtinObject_toString(call) +} + +func builtinArray_toLocaleString(call FunctionCall) Value { + separator := "," + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if length == 0 { + return toValue_string("") + } + stringList := make([]string, 0, length) + for index := int64(0); index < length; index += 1 { + value := thisObject.get(arrayIndexToString(index)) + stringValue := "" + switch value.kind { + case valueEmpty, valueUndefined, valueNull: + default: + object := call.runtime.toObject(value) + toLocaleString := object.get("toLocaleString") + if !toLocaleString.isCallable() { + panic(call.runtime.panicTypeError()) + } + stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string() + } + stringList = append(stringList, stringValue) + } + return toValue_string(strings.Join(stringList, separator)) +} + +func builtinArray_concat(call FunctionCall) Value { + thisObject := call.thisObject() + valueArray := []Value{} + source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...) + for _, item := range source { + switch item.kind { + case valueObject: + object := item._object() + if isArray(object) { + length := object.get("length").number().int64 + for index := int64(0); index < length; index += 1 { + name := strconv.FormatInt(index, 10) + if object.hasProperty(name) { + valueArray = append(valueArray, object.get(name)) + } else { + valueArray = append(valueArray, Value{}) + } + } + continue + } + fallthrough + default: + valueArray = append(valueArray, item) + } + } + return toValue_object(call.runtime.newArrayOf(valueArray)) +} + +func builtinArray_shift(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if 0 == length { + thisObject.put("length", toValue_int64(0), true) + return Value{} + } + first := thisObject.get("0") + for index := int64(1); index < length; index++ { + from := arrayIndexToString(index) + to := arrayIndexToString(index - 1) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + thisObject.delete(arrayIndexToString(length-1), true) + thisObject.put("length", toValue_int64(length-1), true) + return first +} + +func builtinArray_push(call FunctionCall) Value { + thisObject := call.thisObject() + itemList := call.ArgumentList + index := int64(toUint32(thisObject.get("length"))) + for len(itemList) > 0 { + thisObject.put(arrayIndexToString(index), itemList[0], true) + itemList = itemList[1:] + index += 1 + } + length := toValue_int64(index) + thisObject.put("length", length, true) + return length +} + +func builtinArray_pop(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if 0 == length { + thisObject.put("length", toValue_uint32(0), true) + return Value{} + } + last := thisObject.get(arrayIndexToString(length - 1)) + thisObject.delete(arrayIndexToString(length-1), true) + thisObject.put("length", toValue_int64(length-1), true) + return last +} + +func builtinArray_join(call FunctionCall) Value { + separator := "," + { + argument := call.Argument(0) + if argument.IsDefined() { + separator = argument.string() + } + } + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if length == 0 { + return toValue_string("") + } + stringList := make([]string, 0, length) + for index := int64(0); index < length; index += 1 { + value := thisObject.get(arrayIndexToString(index)) + stringValue := "" + switch value.kind { + case valueEmpty, valueUndefined, valueNull: + default: + stringValue = value.string() + } + stringList = append(stringList, stringValue) + } + return toValue_string(strings.Join(stringList, separator)) +} + +func builtinArray_splice(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + + start := valueToRangeIndex(call.Argument(0), length, false) + deleteCount := length - start + if arg, ok := call.getArgument(1); ok { + deleteCount = valueToRangeIndex(arg, length-start, true) + } + valueArray := make([]Value, deleteCount) + + for index := int64(0); index < deleteCount; index++ { + indexString := arrayIndexToString(int64(start + index)) + if thisObject.hasProperty(indexString) { + valueArray[index] = thisObject.get(indexString) + } + } + + // 0, <1, 2, 3, 4>, 5, 6, 7 + // a, b + // length 8 - delete 4 @ start 1 + + itemList := []Value{} + itemCount := int64(len(call.ArgumentList)) + if itemCount > 2 { + itemCount -= 2 // Less the first two arguments + itemList = call.ArgumentList[2:] + } else { + itemCount = 0 + } + if itemCount < deleteCount { + // The Object/Array is shrinking + stop := int64(length) - deleteCount + // The new length of the Object/Array before + // appending the itemList remainder + // Stopping at the lower bound of the insertion: + // Move an item from the after the deleted portion + // to a position after the inserted portion + for index := start; index < stop; index++ { + from := arrayIndexToString(index + deleteCount) // Position just after deletion + to := arrayIndexToString(index + itemCount) // Position just after splice (insertion) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + // Delete off the end + // We don't bother to delete below (if any) since those + // will be overwritten anyway + for index := int64(length); index > (stop + itemCount); index-- { + thisObject.delete(arrayIndexToString(index-1), true) + } + } else if itemCount > deleteCount { + // The Object/Array is growing + // The itemCount is greater than the deleteCount, so we do + // not have to worry about overwriting what we should be moving + // --- + // Starting from the upper bound of the deletion: + // Move an item from the after the deleted portion + // to a position after the inserted portion + for index := int64(length) - deleteCount; index > start; index-- { + from := arrayIndexToString(index + deleteCount - 1) + to := arrayIndexToString(index + itemCount - 1) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + } + + for index := int64(0); index < itemCount; index++ { + thisObject.put(arrayIndexToString(index+start), itemList[index], true) + } + thisObject.put("length", toValue_int64(int64(length)+itemCount-deleteCount), true) + + return toValue_object(call.runtime.newArrayOf(valueArray)) +} + +func builtinArray_slice(call FunctionCall) Value { + thisObject := call.thisObject() + + length := int64(toUint32(thisObject.get("length"))) + start, end := rangeStartEnd(call.ArgumentList, length, false) + + if start >= end { + // Always an empty array + return toValue_object(call.runtime.newArray(0)) + } + sliceLength := end - start + sliceValueArray := make([]Value, sliceLength) + + for index := int64(0); index < sliceLength; index++ { + from := arrayIndexToString(index + start) + if thisObject.hasProperty(from) { + sliceValueArray[index] = thisObject.get(from) + } + } + + return toValue_object(call.runtime.newArrayOf(sliceValueArray)) +} + +func builtinArray_unshift(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + itemList := call.ArgumentList + itemCount := int64(len(itemList)) + + for index := length; index > 0; index-- { + from := arrayIndexToString(index - 1) + to := arrayIndexToString(index + itemCount - 1) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + + for index := int64(0); index < itemCount; index++ { + thisObject.put(arrayIndexToString(index), itemList[index], true) + } + + newLength := toValue_int64(length + itemCount) + thisObject.put("length", newLength, true) + return newLength +} + +func builtinArray_reverse(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + + lower := struct { + name string + index int64 + exists bool + }{} + upper := lower + + lower.index = 0 + middle := length / 2 // Division will floor + + for lower.index != middle { + lower.name = arrayIndexToString(lower.index) + upper.index = length - lower.index - 1 + upper.name = arrayIndexToString(upper.index) + + lower.exists = thisObject.hasProperty(lower.name) + upper.exists = thisObject.hasProperty(upper.name) + + if lower.exists && upper.exists { + lowerValue := thisObject.get(lower.name) + upperValue := thisObject.get(upper.name) + thisObject.put(lower.name, upperValue, true) + thisObject.put(upper.name, lowerValue, true) + } else if !lower.exists && upper.exists { + value := thisObject.get(upper.name) + thisObject.delete(upper.name, true) + thisObject.put(lower.name, value, true) + } else if lower.exists && !upper.exists { + value := thisObject.get(lower.name) + thisObject.delete(lower.name, true) + thisObject.put(upper.name, value, true) + } else { + // Nothing happens. + } + + lower.index += 1 + } + + return call.This +} + +func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int { + j := struct { + name string + exists bool + defined bool + value string + }{} + k := j + j.name = arrayIndexToString(int64(index0)) + j.exists = thisObject.hasProperty(j.name) + k.name = arrayIndexToString(int64(index1)) + k.exists = thisObject.hasProperty(k.name) + + if !j.exists && !k.exists { + return 0 + } else if !j.exists { + return 1 + } else if !k.exists { + return -1 + } + + x := thisObject.get(j.name) + y := thisObject.get(k.name) + j.defined = x.IsDefined() + k.defined = y.IsDefined() + + if !j.defined && !k.defined { + return 0 + } else if !j.defined { + return 1 + } else if !k.defined { + return -1 + } + + if compare == nil { + j.value = x.string() + k.value = y.string() + + if j.value == k.value { + return 0 + } else if j.value < k.value { + return -1 + } + + return 1 + } + + return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame))) +} + +func arraySortSwap(thisObject *_object, index0, index1 uint) { + + j := struct { + name string + exists bool + }{} + k := j + + j.name = arrayIndexToString(int64(index0)) + j.exists = thisObject.hasProperty(j.name) + k.name = arrayIndexToString(int64(index1)) + k.exists = thisObject.hasProperty(k.name) + + if j.exists && k.exists { + jValue := thisObject.get(j.name) + kValue := thisObject.get(k.name) + thisObject.put(j.name, kValue, true) + thisObject.put(k.name, jValue, true) + } else if !j.exists && k.exists { + value := thisObject.get(k.name) + thisObject.delete(k.name, true) + thisObject.put(j.name, value, true) + } else if j.exists && !k.exists { + value := thisObject.get(j.name) + thisObject.delete(j.name, true) + thisObject.put(k.name, value, true) + } else { + // Nothing happens. + } +} + +func arraySortQuickPartition(thisObject *_object, left, right, pivot uint, compare *_object) (uint, uint) { + arraySortSwap(thisObject, pivot, right) // Right is now the pivot value + cursor := left + cursor2 := left + for index := left; index < right; index++ { + comparison := sortCompare(thisObject, index, right, compare) // Compare to the pivot value + if comparison < 0 { + arraySortSwap(thisObject, index, cursor) + if cursor < cursor2 { + arraySortSwap(thisObject, index, cursor2) + } + cursor += 1 + cursor2 += 1 + } else if comparison == 0 { + arraySortSwap(thisObject, index, cursor2) + cursor2 += 1 + } + } + arraySortSwap(thisObject, cursor2, right) + return cursor, cursor2 +} + +func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) { + if left < right { + middle := left + (right-left)/2 + pivot, pivot2 := arraySortQuickPartition(thisObject, left, right, middle, compare) + if pivot > 0 { + arraySortQuickSort(thisObject, left, pivot-1, compare) + } + arraySortQuickSort(thisObject, pivot2+1, right, compare) + } +} + +func builtinArray_sort(call FunctionCall) Value { + thisObject := call.thisObject() + length := uint(toUint32(thisObject.get("length"))) + compareValue := call.Argument(0) + compare := compareValue._object() + if compareValue.IsUndefined() { + } else if !compareValue.isCallable() { + panic(call.runtime.panicTypeError()) + } + if length > 1 { + arraySortQuickSort(thisObject, 0, length-1, compare) + } + return call.This +} + +func builtinArray_isArray(call FunctionCall) Value { + return toValue_bool(isArray(call.Argument(0)._object())) +} + +func builtinArray_indexOf(call FunctionCall) Value { + thisObject, matchValue := call.thisObject(), call.Argument(0) + if length := int64(toUint32(thisObject.get("length"))); length > 0 { + index := int64(0) + if len(call.ArgumentList) > 1 { + index = call.Argument(1).number().int64 + } + if index < 0 { + if index += length; index < 0 { + index = 0 + } + } else if index >= length { + index = -1 + } + for ; index >= 0 && index < length; index++ { + name := arrayIndexToString(int64(index)) + if !thisObject.hasProperty(name) { + continue + } + value := thisObject.get(name) + if strictEqualityComparison(matchValue, value) { + return toValue_uint32(uint32(index)) + } + } + } + return toValue_int(-1) +} + +func builtinArray_lastIndexOf(call FunctionCall) Value { + thisObject, matchValue := call.thisObject(), call.Argument(0) + length := int64(toUint32(thisObject.get("length"))) + index := length - 1 + if len(call.ArgumentList) > 1 { + index = call.Argument(1).number().int64 + } + if 0 > index { + index += length + } + if index > length { + index = length - 1 + } else if 0 > index { + return toValue_int(-1) + } + for ; index >= 0; index-- { + name := arrayIndexToString(int64(index)) + if !thisObject.hasProperty(name) { + continue + } + value := thisObject.get(name) + if strictEqualityComparison(matchValue, value) { + return toValue_uint32(uint32(index)) + } + } + return toValue_int(-1) +} + +func builtinArray_every(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() { + continue + } + return falseValue + } + } + return trueValue + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_some(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() { + return trueValue + } + } + } + return falseValue + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_forEach(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this) + } + } + return Value{} + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_map(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + values := make([]Value, length) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this) + } else { + values[index] = Value{} + } + } + return toValue_object(call.runtime.newArrayOf(values)) + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_filter(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + values := make([]Value, 0) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + value := thisObject.get(key) + if iterator.call(call.runtime, callThis, value, index, this).bool() { + values = append(values, value) + } + } + } + return toValue_object(call.runtime.newArrayOf(values)) + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_reduce(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + initial := len(call.ArgumentList) > 1 + start := call.Argument(1) + length := int64(toUint32(thisObject.get("length"))) + index := int64(0) + if length > 0 || initial { + var accumulator Value + if !initial { + for ; index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = thisObject.get(key) + index++ + break + } + } + } else { + accumulator = start + } + for ; index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this) + } + } + return accumulator + } + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_reduceRight(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + initial := len(call.ArgumentList) > 1 + start := call.Argument(1) + length := int64(toUint32(thisObject.get("length"))) + if length > 0 || initial { + index := length - 1 + var accumulator Value + if !initial { + for ; index >= 0; index-- { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = thisObject.get(key) + index-- + break + } + } + } else { + accumulator = start + } + for ; index >= 0; index-- { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this) + } + } + return accumulator + } + } + panic(call.runtime.panicTypeError()) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_boolean.go b/vendor/github.com/robertkrimen/otto/builtin_boolean.go new file mode 100644 index 00000000..59b8e789 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_boolean.go @@ -0,0 +1,28 @@ +package otto + +// Boolean + +func builtinBoolean(call FunctionCall) Value { + return toValue_bool(call.Argument(0).bool()) +} + +func builtinNewBoolean(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0))) +} + +func builtinBoolean_toString(call FunctionCall) Value { + value := call.This + if !value.IsBoolean() { + // Will throw a TypeError if ThisObject is not a Boolean + value = call.thisClassObject("Boolean").primitiveValue() + } + return toValue_string(value.string()) +} + +func builtinBoolean_valueOf(call FunctionCall) Value { + value := call.This + if !value.IsBoolean() { + value = call.thisClassObject("Boolean").primitiveValue() + } + return value +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_date.go b/vendor/github.com/robertkrimen/otto/builtin_date.go new file mode 100644 index 00000000..f20bf8e3 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_date.go @@ -0,0 +1,615 @@ +package otto + +import ( + "math" + Time "time" +) + +// Date + +const ( + // TODO Be like V8? + // builtinDate_goDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)" + builtinDate_goDateTimeLayout = Time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST" + builtinDate_goDateLayout = "Mon, 02 Jan 2006" + builtinDate_goTimeLayout = "15:04:05 MST" +) + +func builtinDate(call FunctionCall) Value { + date := &_dateObject{} + date.Set(newDateTime([]Value{}, Time.Local)) + return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout)) +} + +func builtinNewDate(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local))) +} + +func builtinDate_toString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format(builtinDate_goDateTimeLayout)) +} + +func builtinDate_toDateString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format(builtinDate_goDateLayout)) +} + +func builtinDate_toTimeString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format(builtinDate_goTimeLayout)) +} + +func builtinDate_toUTCString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout)) +} + +func builtinDate_toISOString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Format("2006-01-02T15:04:05.000Z")) +} + +func builtinDate_toJSON(call FunctionCall) Value { + object := call.thisObject() + value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue + { // FIXME value.isFinite + value := value.float64() + if math.IsNaN(value) || math.IsInf(value, 0) { + return nullValue + } + } + toISOString := object.get("toISOString") + if !toISOString.isCallable() { + // FIXME + panic(call.runtime.panicTypeError()) + } + return toISOString.call(call.runtime, toValue_object(object), []Value{}) +} + +func builtinDate_toGMTString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT")) +} + +func builtinDate_getTime(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + // We do this (convert away from a float) so the user + // does not get something back in exponential notation + return toValue_int64(int64(date.Epoch())) +} + +func builtinDate_setTime(call FunctionCall) Value { + object := call.thisObject() + date := dateObjectOf(call.runtime, call.thisObject()) + date.Set(call.Argument(0).float64()) + object.value = date + return date.Value() +} + +func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) { + object := call.thisObject() + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return nil, nil, nil, nil + } + + if argumentLimit > len(call.ArgumentList) { + argumentLimit = len(call.ArgumentList) + } + + if argumentLimit == 0 { + object.value = invalidDateObject + return nil, nil, nil, nil + } + + valueList := make([]int, argumentLimit) + for index := 0; index < argumentLimit; index++ { + value := call.ArgumentList[index] + nm := value.number() + switch nm.kind { + case numberInteger, numberFloat: + default: + object.value = invalidDateObject + return nil, nil, nil, nil + } + valueList[index] = int(nm.int64) + } + baseTime := date.Time() + if timeLocal { + baseTime = baseTime.Local() + } + ecmaTime := ecmaTime(baseTime) + return object, &date, &ecmaTime, valueList +} + +func builtinDate_parse(call FunctionCall) Value { + date := call.Argument(0).string() + return toValue_float64(dateParse(date)) +} + +func builtinDate_UTC(call FunctionCall) Value { + return toValue_float64(newDateTime(call.ArgumentList, Time.UTC)) +} + +func builtinDate_now(call FunctionCall) Value { + call.ArgumentList = []Value(nil) + return builtinDate_UTC(call) +} + +// This is a placeholder +func builtinDate_toLocaleString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format("2006-01-02 15:04:05")) +} + +// This is a placeholder +func builtinDate_toLocaleDateString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format("2006-01-02")) +} + +// This is a placeholder +func builtinDate_toLocaleTimeString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format("15:04:05")) +} + +func builtinDate_valueOf(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return date.Value() +} + +func builtinDate_getYear(call FunctionCall) Value { + // Will throw a TypeError is ThisObject is nil or + // does not have Class of "Date" + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Year() - 1900) +} + +func builtinDate_getFullYear(call FunctionCall) Value { + // Will throw a TypeError is ThisObject is nil or + // does not have Class of "Date" + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Year()) +} + +func builtinDate_getUTCFullYear(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Year()) +} + +func builtinDate_getMonth(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoMonth(date.Time().Local().Month())) +} + +func builtinDate_getUTCMonth(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoMonth(date.Time().Month())) +} + +func builtinDate_getDate(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Day()) +} + +func builtinDate_getUTCDate(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Day()) +} + +func builtinDate_getDay(call FunctionCall) Value { + // Actually day of the week + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoDay(date.Time().Local().Weekday())) +} + +func builtinDate_getUTCDay(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoDay(date.Time().Weekday())) +} + +func builtinDate_getHours(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Hour()) +} + +func builtinDate_getUTCHours(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Hour()) +} + +func builtinDate_getMinutes(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Minute()) +} + +func builtinDate_getUTCMinutes(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Minute()) +} + +func builtinDate_getSeconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Second()) +} + +func builtinDate_getUTCSeconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Second()) +} + +func builtinDate_getMilliseconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Nanosecond() / (100 * 100 * 100)) +} + +func builtinDate_getUTCMilliseconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Nanosecond() / (100 * 100 * 100)) +} + +func builtinDate_getTimezoneOffset(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + timeLocal := date.Time().Local() + // Is this kosher? + timeLocalAsUTC := Time.Date( + timeLocal.Year(), + timeLocal.Month(), + timeLocal.Day(), + timeLocal.Hour(), + timeLocal.Minute(), + timeLocal.Second(), + timeLocal.Nanosecond(), + Time.UTC, + ) + return toValue_float64(date.Time().Sub(timeLocalAsUTC).Seconds() / 60) +} + +func builtinDate_setMilliseconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.millisecond = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCMilliseconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.millisecond = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setSeconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.millisecond = value[1] + } + ecmaTime.second = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCSeconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.millisecond = value[1] + } + ecmaTime.second = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setMinutes(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.millisecond = value[2] + ecmaTime.second = value[1] + } else if len(value) > 1 { + ecmaTime.second = value[1] + } + ecmaTime.minute = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCMinutes(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.millisecond = value[2] + ecmaTime.second = value[1] + } else if len(value) > 1 { + ecmaTime.second = value[1] + } + ecmaTime.minute = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setHours(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 3 { + ecmaTime.millisecond = value[3] + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 2 { + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 1 { + ecmaTime.minute = value[1] + } + ecmaTime.hour = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCHours(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 3 { + ecmaTime.millisecond = value[3] + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 2 { + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 1 { + ecmaTime.minute = value[1] + } + ecmaTime.hour = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setDate(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.day = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCDate(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.day = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setMonth(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.day = value[1] + } + ecmaTime.month = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCMonth(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.day = value[1] + } + ecmaTime.month = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setYear(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) + if ecmaTime == nil { + return NaNValue() + } + + year := value[0] + if 0 <= year && year <= 99 { + year += 1900 + } + ecmaTime.year = year + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setFullYear(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.day = value[2] + ecmaTime.month = value[1] + } else if len(value) > 1 { + ecmaTime.month = value[1] + } + ecmaTime.year = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCFullYear(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.day = value[2] + ecmaTime.month = value[1] + } else if len(value) > 1 { + ecmaTime.month = value[1] + } + ecmaTime.year = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +// toUTCString +// toISOString +// toJSONString +// toJSON diff --git a/vendor/github.com/robertkrimen/otto/builtin_error.go b/vendor/github.com/robertkrimen/otto/builtin_error.go new file mode 100644 index 00000000..41da84ef --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_error.go @@ -0,0 +1,126 @@ +package otto + +import ( + "fmt" +) + +func builtinError(call FunctionCall) Value { + return toValue_object(call.runtime.newError("Error", call.Argument(0), 1)) +} + +func builtinNewError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newError("Error", valueOfArrayIndex(argumentList, 0), 0)) +} + +func builtinError_toString(call FunctionCall) Value { + thisObject := call.thisObject() + if thisObject == nil { + panic(call.runtime.panicTypeError()) + } + + name := "Error" + nameValue := thisObject.get("name") + if nameValue.IsDefined() { + name = nameValue.string() + } + + message := "" + messageValue := thisObject.get("message") + if messageValue.IsDefined() { + message = messageValue.string() + } + + if len(name) == 0 { + return toValue_string(message) + } + + if len(message) == 0 { + return toValue_string(name) + } + + return toValue_string(fmt.Sprintf("%s: %s", name, message)) +} + +func (runtime *_runtime) newEvalError(message Value) *_object { + self := runtime.newErrorObject("EvalError", message, 0) + self.prototype = runtime.global.EvalErrorPrototype + return self +} + +func builtinEvalError(call FunctionCall) Value { + return toValue_object(call.runtime.newEvalError(call.Argument(0))) +} + +func builtinNewEvalError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newTypeError(message Value) *_object { + self := runtime.newErrorObject("TypeError", message, 0) + self.prototype = runtime.global.TypeErrorPrototype + return self +} + +func builtinTypeError(call FunctionCall) Value { + return toValue_object(call.runtime.newTypeError(call.Argument(0))) +} + +func builtinNewTypeError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newRangeError(message Value) *_object { + self := runtime.newErrorObject("RangeError", message, 0) + self.prototype = runtime.global.RangeErrorPrototype + return self +} + +func builtinRangeError(call FunctionCall) Value { + return toValue_object(call.runtime.newRangeError(call.Argument(0))) +} + +func builtinNewRangeError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newURIError(message Value) *_object { + self := runtime.newErrorObject("URIError", message, 0) + self.prototype = runtime.global.URIErrorPrototype + return self +} + +func (runtime *_runtime) newReferenceError(message Value) *_object { + self := runtime.newErrorObject("ReferenceError", message, 0) + self.prototype = runtime.global.ReferenceErrorPrototype + return self +} + +func builtinReferenceError(call FunctionCall) Value { + return toValue_object(call.runtime.newReferenceError(call.Argument(0))) +} + +func builtinNewReferenceError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newSyntaxError(message Value) *_object { + self := runtime.newErrorObject("SyntaxError", message, 0) + self.prototype = runtime.global.SyntaxErrorPrototype + return self +} + +func builtinSyntaxError(call FunctionCall) Value { + return toValue_object(call.runtime.newSyntaxError(call.Argument(0))) +} + +func builtinNewSyntaxError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0))) +} + +func builtinURIError(call FunctionCall) Value { + return toValue_object(call.runtime.newURIError(call.Argument(0))) +} + +func builtinNewURIError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0))) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_function.go b/vendor/github.com/robertkrimen/otto/builtin_function.go new file mode 100644 index 00000000..3d07566c --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_function.go @@ -0,0 +1,129 @@ +package otto + +import ( + "fmt" + "regexp" + "strings" + "unicode" + + "github.com/robertkrimen/otto/parser" +) + +// Function + +func builtinFunction(call FunctionCall) Value { + return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList)) +} + +func builtinNewFunction(self *_object, argumentList []Value) Value { + return toValue_object(builtinNewFunctionNative(self.runtime, argumentList)) +} + +func argumentList2parameterList(argumentList []Value) []string { + parameterList := make([]string, 0, len(argumentList)) + for _, value := range argumentList { + tmp := strings.FieldsFunc(value.string(), func(chr rune) bool { + return chr == ',' || unicode.IsSpace(chr) + }) + parameterList = append(parameterList, tmp...) + } + return parameterList +} + +var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`) + +func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object { + var parameterList, body string + count := len(argumentList) + if count > 0 { + tmp := make([]string, 0, count-1) + for _, value := range argumentList[0 : count-1] { + tmp = append(tmp, value.string()) + } + parameterList = strings.Join(tmp, ",") + body = argumentList[count-1].string() + } + + // FIXME + function, err := parser.ParseFunction(parameterList, body) + runtime.parseThrow(err) // Will panic/throw appropriately + cmpl := _compiler{} + cmpl_function := cmpl.parseExpression(function) + + return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.globalStash) +} + +func builtinFunction_toString(call FunctionCall) Value { + object := call.thisClassObject("Function") // Should throw a TypeError unless Function + switch fn := object.value.(type) { + case _nativeFunctionObject: + return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name)) + case _nodeFunctionObject: + return toValue_string(fn.node.source) + case _bindFunctionObject: + return toValue_string("function () { [native code] }") + } + + panic(call.runtime.panicTypeError("Function.toString()")) +} + +func builtinFunction_apply(call FunctionCall) Value { + if !call.This.isCallable() { + panic(call.runtime.panicTypeError()) + } + this := call.Argument(0) + if this.IsUndefined() { + // FIXME Not ECMA5 + this = toValue_object(call.runtime.globalObject) + } + argumentList := call.Argument(1) + switch argumentList.kind { + case valueUndefined, valueNull: + return call.thisObject().call(this, nil, false, nativeFrame) + case valueObject: + default: + panic(call.runtime.panicTypeError()) + } + + arrayObject := argumentList._object() + thisObject := call.thisObject() + length := int64(toUint32(arrayObject.get("length"))) + valueArray := make([]Value, length) + for index := int64(0); index < length; index++ { + valueArray[index] = arrayObject.get(arrayIndexToString(index)) + } + return thisObject.call(this, valueArray, false, nativeFrame) +} + +func builtinFunction_call(call FunctionCall) Value { + if !call.This.isCallable() { + panic(call.runtime.panicTypeError()) + } + thisObject := call.thisObject() + this := call.Argument(0) + if this.IsUndefined() { + // FIXME Not ECMA5 + this = toValue_object(call.runtime.globalObject) + } + if len(call.ArgumentList) >= 1 { + return thisObject.call(this, call.ArgumentList[1:], false, nativeFrame) + } + return thisObject.call(this, nil, false, nativeFrame) +} + +func builtinFunction_bind(call FunctionCall) Value { + target := call.This + if !target.isCallable() { + panic(call.runtime.panicTypeError()) + } + targetObject := target._object() + + this := call.Argument(0) + argumentList := call.slice(1) + if this.IsUndefined() { + // FIXME Do this elsewhere? + this = toValue_object(call.runtime.globalObject) + } + + return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList)) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_json.go b/vendor/github.com/robertkrimen/otto/builtin_json.go new file mode 100644 index 00000000..aed54bf1 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_json.go @@ -0,0 +1,299 @@ +package otto + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +type _builtinJSON_parseContext struct { + call FunctionCall + reviver Value +} + +func builtinJSON_parse(call FunctionCall) Value { + ctx := _builtinJSON_parseContext{ + call: call, + } + revive := false + if reviver := call.Argument(1); reviver.isCallable() { + revive = true + ctx.reviver = reviver + } + + var root interface{} + err := json.Unmarshal([]byte(call.Argument(0).string()), &root) + if err != nil { + panic(call.runtime.panicSyntaxError(err.Error())) + } + value, exists := builtinJSON_parseWalk(ctx, root) + if !exists { + value = Value{} + } + if revive { + root := ctx.call.runtime.newObject() + root.put("", value, false) + return builtinJSON_reviveWalk(ctx, root, "") + } + return value +} + +func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value { + value := holder.get(name) + if object := value._object(); object != nil { + if isArray(object) { + length := int64(objectLength(object)) + for index := int64(0); index < length; index += 1 { + name := arrayIndexToString(index) + value := builtinJSON_reviveWalk(ctx, object, name) + if value.IsUndefined() { + object.delete(name, false) + } else { + object.defineProperty(name, value, 0111, false) + } + } + } else { + object.enumerate(false, func(name string) bool { + value := builtinJSON_reviveWalk(ctx, object, name) + if value.IsUndefined() { + object.delete(name, false) + } else { + object.defineProperty(name, value, 0111, false) + } + return true + }) + } + } + return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value) +} + +func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) { + switch value := rawValue.(type) { + case nil: + return nullValue, true + case bool: + return toValue_bool(value), true + case string: + return toValue_string(value), true + case float64: + return toValue_float64(value), true + case []interface{}: + arrayValue := make([]Value, len(value)) + for index, rawValue := range value { + if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists { + arrayValue[index] = value + } + } + return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true + case map[string]interface{}: + object := ctx.call.runtime.newObject() + for name, rawValue := range value { + if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists { + object.put(name, value, false) + } + } + return toValue_object(object), true + } + return Value{}, false +} + +type _builtinJSON_stringifyContext struct { + call FunctionCall + stack []*_object + propertyList []string + replacerFunction *Value + gap string +} + +func builtinJSON_stringify(call FunctionCall) Value { + ctx := _builtinJSON_stringifyContext{ + call: call, + stack: []*_object{nil}, + } + replacer := call.Argument(1)._object() + if replacer != nil { + if isArray(replacer) { + length := objectLength(replacer) + seen := map[string]bool{} + propertyList := make([]string, length) + length = 0 + for index, _ := range propertyList { + value := replacer.get(arrayIndexToString(int64(index))) + switch value.kind { + case valueObject: + switch value.value.(*_object).class { + case "String": + case "Number": + default: + continue + } + case valueString: + case valueNumber: + default: + continue + } + name := value.string() + if seen[name] { + continue + } + seen[name] = true + length += 1 + propertyList[index] = name + } + ctx.propertyList = propertyList[0:length] + } else if replacer.class == "Function" { + value := toValue_object(replacer) + ctx.replacerFunction = &value + } + } + if spaceValue, exists := call.getArgument(2); exists { + if spaceValue.kind == valueObject { + switch spaceValue.value.(*_object).class { + case "String": + spaceValue = toValue_string(spaceValue.string()) + case "Number": + spaceValue = spaceValue.numberValue() + } + } + switch spaceValue.kind { + case valueString: + value := spaceValue.string() + if len(value) > 10 { + ctx.gap = value[0:10] + } else { + ctx.gap = value + } + case valueNumber: + value := spaceValue.number().int64 + if value > 10 { + value = 10 + } else if value < 0 { + value = 0 + } + ctx.gap = strings.Repeat(" ", int(value)) + } + } + holder := call.runtime.newObject() + holder.put("", call.Argument(0), false) + value, exists := builtinJSON_stringifyWalk(ctx, "", holder) + if !exists { + return Value{} + } + valueJSON, err := json.Marshal(value) + if err != nil { + panic(call.runtime.panicTypeError(err.Error())) + } + if ctx.gap != "" { + valueJSON1 := bytes.Buffer{} + json.Indent(&valueJSON1, valueJSON, "", ctx.gap) + valueJSON = valueJSON1.Bytes() + } + return toValue_string(string(valueJSON)) +} + +func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) { + value := holder.get(key) + + if value.IsObject() { + object := value._object() + if toJSON := object.get("toJSON"); toJSON.IsFunction() { + value = toJSON.call(ctx.call.runtime, value, key) + } else { + // If the object is a GoStruct or something that implements json.Marshaler + if object.objectClass.marshalJSON != nil { + marshaler := object.objectClass.marshalJSON(object) + if marshaler != nil { + return marshaler, true + } + } + } + } + + if ctx.replacerFunction != nil { + value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value) + } + + if value.kind == valueObject { + switch value.value.(*_object).class { + case "Boolean": + value = value._object().value.(Value) + case "String": + value = toValue_string(value.string()) + case "Number": + value = value.numberValue() + } + } + + switch value.kind { + case valueBoolean: + return value.bool(), true + case valueString: + return value.string(), true + case valueNumber: + integer := value.number() + switch integer.kind { + case numberInteger: + return integer.int64, true + case numberFloat: + return integer.float64, true + default: + return nil, true + } + case valueNull: + return nil, true + case valueObject: + holder := value._object() + if value := value._object(); nil != value { + for _, object := range ctx.stack { + if holder == object { + panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON")) + } + } + ctx.stack = append(ctx.stack, value) + defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }() + } + if isArray(holder) { + var length uint32 + switch value := holder.get("length").value.(type) { + case uint32: + length = value + case int: + if value >= 0 { + length = uint32(value) + } + default: + panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value))) + } + array := make([]interface{}, length) + for index, _ := range array { + name := arrayIndexToString(int64(index)) + value, _ := builtinJSON_stringifyWalk(ctx, name, holder) + array[index] = value + } + return array, true + } else if holder.class != "Function" { + object := map[string]interface{}{} + if ctx.propertyList != nil { + for _, name := range ctx.propertyList { + value, exists := builtinJSON_stringifyWalk(ctx, name, holder) + if exists { + object[name] = value + } + } + } else { + // Go maps are without order, so this doesn't conform to the ECMA ordering + // standard, but oh well... + holder.enumerate(false, func(name string) bool { + value, exists := builtinJSON_stringifyWalk(ctx, name, holder) + if exists { + object[name] = value + } + return true + }) + } + return object, true + } + } + return nil, false +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_math.go b/vendor/github.com/robertkrimen/otto/builtin_math.go new file mode 100644 index 00000000..7ce90c33 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_math.go @@ -0,0 +1,151 @@ +package otto + +import ( + "math" + "math/rand" +) + +// Math + +func builtinMath_abs(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Abs(number)) +} + +func builtinMath_acos(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Acos(number)) +} + +func builtinMath_asin(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Asin(number)) +} + +func builtinMath_atan(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Atan(number)) +} + +func builtinMath_atan2(call FunctionCall) Value { + y := call.Argument(0).float64() + if math.IsNaN(y) { + return NaNValue() + } + x := call.Argument(1).float64() + if math.IsNaN(x) { + return NaNValue() + } + return toValue_float64(math.Atan2(y, x)) +} + +func builtinMath_cos(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Cos(number)) +} + +func builtinMath_ceil(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Ceil(number)) +} + +func builtinMath_exp(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Exp(number)) +} + +func builtinMath_floor(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Floor(number)) +} + +func builtinMath_log(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Log(number)) +} + +func builtinMath_max(call FunctionCall) Value { + switch len(call.ArgumentList) { + case 0: + return negativeInfinityValue() + case 1: + return toValue_float64(call.ArgumentList[0].float64()) + } + result := call.ArgumentList[0].float64() + if math.IsNaN(result) { + return NaNValue() + } + for _, value := range call.ArgumentList[1:] { + value := value.float64() + if math.IsNaN(value) { + return NaNValue() + } + result = math.Max(result, value) + } + return toValue_float64(result) +} + +func builtinMath_min(call FunctionCall) Value { + switch len(call.ArgumentList) { + case 0: + return positiveInfinityValue() + case 1: + return toValue_float64(call.ArgumentList[0].float64()) + } + result := call.ArgumentList[0].float64() + if math.IsNaN(result) { + return NaNValue() + } + for _, value := range call.ArgumentList[1:] { + value := value.float64() + if math.IsNaN(value) { + return NaNValue() + } + result = math.Min(result, value) + } + return toValue_float64(result) +} + +func builtinMath_pow(call FunctionCall) Value { + // TODO Make sure this works according to the specification (15.8.2.13) + x := call.Argument(0).float64() + y := call.Argument(1).float64() + if math.Abs(x) == 1 && math.IsInf(y, 0) { + return NaNValue() + } + return toValue_float64(math.Pow(x, y)) +} + +func builtinMath_random(call FunctionCall) Value { + var v float64 + if call.runtime.random != nil { + v = call.runtime.random() + } else { + v = rand.Float64() + } + return toValue_float64(v) +} + +func builtinMath_round(call FunctionCall) Value { + number := call.Argument(0).float64() + value := math.Floor(number + 0.5) + if value == 0 { + value = math.Copysign(0, number) + } + return toValue_float64(value) +} + +func builtinMath_sin(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Sin(number)) +} + +func builtinMath_sqrt(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Sqrt(number)) +} + +func builtinMath_tan(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Tan(number)) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_number.go b/vendor/github.com/robertkrimen/otto/builtin_number.go new file mode 100644 index 00000000..f99a42a2 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_number.go @@ -0,0 +1,93 @@ +package otto + +import ( + "math" + "strconv" +) + +// Number + +func numberValueFromNumberArgumentList(argumentList []Value) Value { + if len(argumentList) > 0 { + return argumentList[0].numberValue() + } + return toValue_int(0) +} + +func builtinNumber(call FunctionCall) Value { + return numberValueFromNumberArgumentList(call.ArgumentList) +} + +func builtinNewNumber(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList))) +} + +func builtinNumber_toString(call FunctionCall) Value { + // Will throw a TypeError if ThisObject is not a Number + value := call.thisClassObject("Number").primitiveValue() + radix := 10 + radixArgument := call.Argument(0) + if radixArgument.IsDefined() { + integer := toIntegerFloat(radixArgument) + if integer < 2 || integer > 36 { + panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36")) + } + radix = int(integer) + } + if radix == 10 { + return toValue_string(value.string()) + } + return toValue_string(numberToStringRadix(value, radix)) +} + +func builtinNumber_valueOf(call FunctionCall) Value { + return call.thisClassObject("Number").primitiveValue() +} + +func builtinNumber_toFixed(call FunctionCall) Value { + precision := toIntegerFloat(call.Argument(0)) + if 20 < precision || 0 > precision { + panic(call.runtime.panicRangeError("toFixed() precision must be between 0 and 20")) + } + if call.This.IsNaN() { + return toValue_string("NaN") + } + value := call.This.float64() + if math.Abs(value) >= 1e21 { + return toValue_string(floatToString(value, 64)) + } + return toValue_string(strconv.FormatFloat(call.This.float64(), 'f', int(precision), 64)) +} + +func builtinNumber_toExponential(call FunctionCall) Value { + if call.This.IsNaN() { + return toValue_string("NaN") + } + precision := float64(-1) + if value := call.Argument(0); value.IsDefined() { + precision = toIntegerFloat(value) + if 0 > precision { + panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36")) + } + } + return toValue_string(strconv.FormatFloat(call.This.float64(), 'e', int(precision), 64)) +} + +func builtinNumber_toPrecision(call FunctionCall) Value { + if call.This.IsNaN() { + return toValue_string("NaN") + } + value := call.Argument(0) + if value.IsUndefined() { + return toValue_string(call.This.string()) + } + precision := toIntegerFloat(value) + if 1 > precision { + panic(call.runtime.panicRangeError("toPrecision() precision must be greater than 1")) + } + return toValue_string(strconv.FormatFloat(call.This.float64(), 'g', int(precision), 64)) +} + +func builtinNumber_toLocaleString(call FunctionCall) Value { + return builtinNumber_toString(call) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_object.go b/vendor/github.com/robertkrimen/otto/builtin_object.go new file mode 100644 index 00000000..c2433f7b --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_object.go @@ -0,0 +1,289 @@ +package otto + +import ( + "fmt" +) + +// Object + +func builtinObject(call FunctionCall) Value { + value := call.Argument(0) + switch value.kind { + case valueUndefined, valueNull: + return toValue_object(call.runtime.newObject()) + } + + return toValue_object(call.runtime.toObject(value)) +} + +func builtinNewObject(self *_object, argumentList []Value) Value { + value := valueOfArrayIndex(argumentList, 0) + switch value.kind { + case valueNull, valueUndefined: + case valueNumber, valueString, valueBoolean: + return toValue_object(self.runtime.toObject(value)) + case valueObject: + return value + default: + } + return toValue_object(self.runtime.newObject()) +} + +func builtinObject_valueOf(call FunctionCall) Value { + return toValue_object(call.thisObject()) +} + +func builtinObject_hasOwnProperty(call FunctionCall) Value { + propertyName := call.Argument(0).string() + thisObject := call.thisObject() + return toValue_bool(thisObject.hasOwnProperty(propertyName)) +} + +func builtinObject_isPrototypeOf(call FunctionCall) Value { + value := call.Argument(0) + if !value.IsObject() { + return falseValue + } + prototype := call.toObject(value).prototype + thisObject := call.thisObject() + for prototype != nil { + if thisObject == prototype { + return trueValue + } + prototype = prototype.prototype + } + return falseValue +} + +func builtinObject_propertyIsEnumerable(call FunctionCall) Value { + propertyName := call.Argument(0).string() + thisObject := call.thisObject() + property := thisObject.getOwnProperty(propertyName) + if property != nil && property.enumerable() { + return trueValue + } + return falseValue +} + +func builtinObject_toString(call FunctionCall) Value { + result := "" + if call.This.IsUndefined() { + result = "[object Undefined]" + } else if call.This.IsNull() { + result = "[object Null]" + } else { + result = fmt.Sprintf("[object %s]", call.thisObject().class) + } + return toValue_string(result) +} + +func builtinObject_toLocaleString(call FunctionCall) Value { + toString := call.thisObject().get("toString") + if !toString.isCallable() { + panic(call.runtime.panicTypeError()) + } + return toString.call(call.runtime, call.This) +} + +func builtinObject_getPrototypeOf(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + + if object.prototype == nil { + return nullValue + } + + return toValue_object(object.prototype) +} + +func builtinObject_getOwnPropertyDescriptor(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + + name := call.Argument(1).string() + descriptor := object.getOwnProperty(name) + if descriptor == nil { + return Value{} + } + return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor)) +} + +func builtinObject_defineProperty(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + name := call.Argument(1).string() + descriptor := toPropertyDescriptor(call.runtime, call.Argument(2)) + object.defineOwnProperty(name, descriptor, true) + return objectValue +} + +func builtinObject_defineProperties(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + + properties := call.runtime.toObject(call.Argument(1)) + properties.enumerate(false, func(name string) bool { + descriptor := toPropertyDescriptor(call.runtime, properties.get(name)) + object.defineOwnProperty(name, descriptor, true) + return true + }) + + return objectValue +} + +func builtinObject_create(call FunctionCall) Value { + prototypeValue := call.Argument(0) + if !prototypeValue.IsNull() && !prototypeValue.IsObject() { + panic(call.runtime.panicTypeError()) + } + + object := call.runtime.newObject() + object.prototype = prototypeValue._object() + + propertiesValue := call.Argument(1) + if propertiesValue.IsDefined() { + properties := call.runtime.toObject(propertiesValue) + properties.enumerate(false, func(name string) bool { + descriptor := toPropertyDescriptor(call.runtime, properties.get(name)) + object.defineOwnProperty(name, descriptor, true) + return true + }) + } + + return toValue_object(object) +} + +func builtinObject_isExtensible(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + return toValue_bool(object.extensible) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_preventExtensions(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + object.extensible = false + } else { + panic(call.runtime.panicTypeError()) + } + return object +} + +func builtinObject_isSealed(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + if object.extensible { + return toValue_bool(false) + } + result := true + object.enumerate(true, func(name string) bool { + property := object.getProperty(name) + if property.configurable() { + result = false + } + return true + }) + return toValue_bool(result) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_seal(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + object.enumerate(true, func(name string) bool { + if property := object.getOwnProperty(name); nil != property && property.configurable() { + property.configureOff() + object.defineOwnProperty(name, *property, true) + } + return true + }) + object.extensible = false + } else { + panic(call.runtime.panicTypeError()) + } + return object +} + +func builtinObject_isFrozen(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + if object.extensible { + return toValue_bool(false) + } + result := true + object.enumerate(true, func(name string) bool { + property := object.getProperty(name) + if property.configurable() || property.writable() { + result = false + } + return true + }) + return toValue_bool(result) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_freeze(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + object.enumerate(true, func(name string) bool { + if property, update := object.getOwnProperty(name), false; nil != property { + if property.isDataDescriptor() && property.writable() { + property.writeOff() + update = true + } + if property.configurable() { + property.configureOff() + update = true + } + if update { + object.defineOwnProperty(name, *property, true) + } + } + return true + }) + object.extensible = false + } else { + panic(call.runtime.panicTypeError()) + } + return object +} + +func builtinObject_keys(call FunctionCall) Value { + if object, keys := call.Argument(0)._object(), []Value(nil); nil != object { + object.enumerate(false, func(name string) bool { + keys = append(keys, toValue_string(name)) + return true + }) + return toValue_object(call.runtime.newArrayOf(keys)) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_getOwnPropertyNames(call FunctionCall) Value { + if object, propertyNames := call.Argument(0)._object(), []Value(nil); nil != object { + object.enumerate(true, func(name string) bool { + if object.hasOwnProperty(name) { + propertyNames = append(propertyNames, toValue_string(name)) + } + return true + }) + return toValue_object(call.runtime.newArrayOf(propertyNames)) + } + panic(call.runtime.panicTypeError()) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_regexp.go b/vendor/github.com/robertkrimen/otto/builtin_regexp.go new file mode 100644 index 00000000..99422510 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_regexp.go @@ -0,0 +1,65 @@ +package otto + +import ( + "fmt" +) + +// RegExp + +func builtinRegExp(call FunctionCall) Value { + pattern := call.Argument(0) + flags := call.Argument(1) + if object := pattern._object(); object != nil { + if object.class == "RegExp" && flags.IsUndefined() { + return pattern + } + } + return toValue_object(call.runtime.newRegExp(pattern, flags)) +} + +func builtinNewRegExp(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newRegExp( + valueOfArrayIndex(argumentList, 0), + valueOfArrayIndex(argumentList, 1), + )) +} + +func builtinRegExp_toString(call FunctionCall) Value { + thisObject := call.thisObject() + source := thisObject.get("source").string() + flags := []byte{} + if thisObject.get("global").bool() { + flags = append(flags, 'g') + } + if thisObject.get("ignoreCase").bool() { + flags = append(flags, 'i') + } + if thisObject.get("multiline").bool() { + flags = append(flags, 'm') + } + return toValue_string(fmt.Sprintf("/%s/%s", source, flags)) +} + +func builtinRegExp_exec(call FunctionCall) Value { + thisObject := call.thisObject() + target := call.Argument(0).string() + match, result := execRegExp(thisObject, target) + if !match { + return nullValue + } + return toValue_object(execResultToArray(call.runtime, target, result)) +} + +func builtinRegExp_test(call FunctionCall) Value { + thisObject := call.thisObject() + target := call.Argument(0).string() + match, _ := execRegExp(thisObject, target) + return toValue_bool(match) +} + +func builtinRegExp_compile(call FunctionCall) Value { + // This (useless) function is deprecated, but is here to provide some + // semblance of compatibility. + // Caveat emptor: it may not be around for long. + return Value{} +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_string.go b/vendor/github.com/robertkrimen/otto/builtin_string.go new file mode 100644 index 00000000..6a171845 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_string.go @@ -0,0 +1,500 @@ +package otto + +import ( + "bytes" + "regexp" + "strconv" + "strings" + "unicode/utf8" +) + +// String + +func stringValueFromStringArgumentList(argumentList []Value) Value { + if len(argumentList) > 0 { + return toValue_string(argumentList[0].string()) + } + return toValue_string("") +} + +func builtinString(call FunctionCall) Value { + return stringValueFromStringArgumentList(call.ArgumentList) +} + +func builtinNewString(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList))) +} + +func builtinString_toString(call FunctionCall) Value { + return call.thisClassObject("String").primitiveValue() +} +func builtinString_valueOf(call FunctionCall) Value { + return call.thisClassObject("String").primitiveValue() +} + +func builtinString_fromCharCode(call FunctionCall) Value { + chrList := make([]uint16, len(call.ArgumentList)) + for index, value := range call.ArgumentList { + chrList[index] = toUint16(value) + } + return toValue_string16(chrList) +} + +func builtinString_charAt(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + idx := int(call.Argument(0).number().int64) + chr := stringAt(call.This._object().stringValue(), idx) + if chr == utf8.RuneError { + return toValue_string("") + } + return toValue_string(string(chr)) +} + +func builtinString_charCodeAt(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + idx := int(call.Argument(0).number().int64) + chr := stringAt(call.This._object().stringValue(), idx) + if chr == utf8.RuneError { + return NaNValue() + } + return toValue_uint16(uint16(chr)) +} + +func builtinString_concat(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + var value bytes.Buffer + value.WriteString(call.This.string()) + for _, item := range call.ArgumentList { + value.WriteString(item.string()) + } + return toValue_string(value.String()) +} + +func builtinString_indexOf(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + value := call.This.string() + target := call.Argument(0).string() + if 2 > len(call.ArgumentList) { + return toValue_int(strings.Index(value, target)) + } + start := toIntegerFloat(call.Argument(1)) + if 0 > start { + start = 0 + } else if start >= float64(len(value)) { + if target == "" { + return toValue_int(len(value)) + } + return toValue_int(-1) + } + index := strings.Index(value[int(start):], target) + if index >= 0 { + index += int(start) + } + return toValue_int(index) +} + +func builtinString_lastIndexOf(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + value := call.This.string() + target := call.Argument(0).string() + if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() { + return toValue_int(strings.LastIndex(value, target)) + } + length := len(value) + if length == 0 { + return toValue_int(strings.LastIndex(value, target)) + } + start := call.ArgumentList[1].number() + if start.kind == numberInfinity { // FIXME + // startNumber is infinity, so start is the end of string (start = length) + return toValue_int(strings.LastIndex(value, target)) + } + if 0 > start.int64 { + start.int64 = 0 + } + end := int(start.int64) + len(target) + if end > length { + end = length + } + return toValue_int(strings.LastIndex(value[:end], target)) +} + +func builtinString_match(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + matcherValue := call.Argument(0) + matcher := matcherValue._object() + if !matcherValue.IsObject() || matcher.class != "RegExp" { + matcher = call.runtime.newRegExp(matcherValue, Value{}) + } + global := matcher.get("global").bool() + if !global { + match, result := execRegExp(matcher, target) + if !match { + return nullValue + } + return toValue_object(execResultToArray(call.runtime, target, result)) + } + + { + result := matcher.regExpValue().regularExpression.FindAllStringIndex(target, -1) + matchCount := len(result) + if result == nil { + matcher.put("lastIndex", toValue_int(0), true) + return Value{} // !match + } + matchCount = len(result) + valueArray := make([]Value, matchCount) + for index := 0; index < matchCount; index++ { + valueArray[index] = toValue_string(target[result[index][0]:result[index][1]]) + } + matcher.put("lastIndex", toValue_int(result[matchCount-1][1]), true) + return toValue_object(call.runtime.newArrayOf(valueArray)) + } +} + +var builtinString_replace_Regexp = regexp.MustCompile("\\$(?:[\\$\\&\\'\\`1-9]|0[1-9]|[1-9][0-9])") + +func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int, target []byte, replaceValue []byte) (output []byte) { + matchCount := len(match) / 2 + output = input + if match[0] != lastIndex { + output = append(output, target[lastIndex:match[0]]...) + } + replacement := builtinString_replace_Regexp.ReplaceAllFunc(replaceValue, func(part []byte) []byte { + // TODO Check if match[0] or match[1] can be -1 in this scenario + switch part[1] { + case '$': + return []byte{'$'} + case '&': + return target[match[0]:match[1]] + case '`': + return target[:match[0]] + case '\'': + return target[match[1]:len(target)] + } + matchNumberParse, error := strconv.ParseInt(string(part[1:]), 10, 64) + matchNumber := int(matchNumberParse) + if error != nil || matchNumber >= matchCount { + return []byte{} + } + offset := 2 * matchNumber + if match[offset] != -1 { + return target[match[offset]:match[offset+1]] + } + return []byte{} // The empty string + }) + output = append(output, replacement...) + return output +} + +func builtinString_replace(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := []byte(call.This.string()) + searchValue := call.Argument(0) + searchObject := searchValue._object() + + // TODO If a capture is -1? + var search *regexp.Regexp + global := false + find := 1 + if searchValue.IsObject() && searchObject.class == "RegExp" { + regExp := searchObject.regExpValue() + search = regExp.regularExpression + if regExp.global { + find = -1 + } + } else { + search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string())) + } + + found := search.FindAllSubmatchIndex(target, find) + if found == nil { + return toValue_string(string(target)) // !match + } + + { + lastIndex := 0 + result := []byte{} + + replaceValue := call.Argument(1) + if replaceValue.isCallable() { + target := string(target) + replace := replaceValue._object() + for _, match := range found { + if match[0] != lastIndex { + result = append(result, target[lastIndex:match[0]]...) + } + matchCount := len(match) / 2 + argumentList := make([]Value, matchCount+2) + for index := 0; index < matchCount; index++ { + offset := 2 * index + if match[offset] != -1 { + argumentList[index] = toValue_string(target[match[offset]:match[offset+1]]) + } else { + argumentList[index] = Value{} + } + } + argumentList[matchCount+0] = toValue_int(match[0]) + argumentList[matchCount+1] = toValue_string(target) + replacement := replace.call(Value{}, argumentList, false, nativeFrame).string() + result = append(result, []byte(replacement)...) + lastIndex = match[1] + } + + } else { + replace := []byte(replaceValue.string()) + for _, match := range found { + result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace) + lastIndex = match[1] + } + } + + if lastIndex != len(target) { + result = append(result, target[lastIndex:]...) + } + + if global && searchObject != nil { + searchObject.put("lastIndex", toValue_int(lastIndex), true) + } + + return toValue_string(string(result)) + } +} + +func builtinString_search(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + searchValue := call.Argument(0) + search := searchValue._object() + if !searchValue.IsObject() || search.class != "RegExp" { + search = call.runtime.newRegExp(searchValue, Value{}) + } + result := search.regExpValue().regularExpression.FindStringIndex(target) + if result == nil { + return toValue_int(-1) + } + return toValue_int(result[0]) +} + +func stringSplitMatch(target string, targetLength int64, index uint, search string, searchLength int64) (bool, uint) { + if int64(index)+searchLength > searchLength { + return false, 0 + } + found := strings.Index(target[index:], search) + if 0 > found { + return false, 0 + } + return true, uint(found) +} + +func builtinString_split(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + + separatorValue := call.Argument(0) + limitValue := call.Argument(1) + limit := -1 + if limitValue.IsDefined() { + limit = int(toUint32(limitValue)) + } + + if limit == 0 { + return toValue_object(call.runtime.newArray(0)) + } + + if separatorValue.IsUndefined() { + return toValue_object(call.runtime.newArrayOf([]Value{toValue_string(target)})) + } + + if separatorValue.isRegExp() { + targetLength := len(target) + search := separatorValue._object().regExpValue().regularExpression + valueArray := []Value{} + result := search.FindAllStringSubmatchIndex(target, -1) + lastIndex := 0 + found := 0 + + for _, match := range result { + if match[0] == match[1] { + // FIXME Ugh, this is a hack + if match[0] == 0 || match[0] == targetLength { + continue + } + } + + if lastIndex != match[0] { + valueArray = append(valueArray, toValue_string(target[lastIndex:match[0]])) + found++ + } else if lastIndex == match[0] { + if lastIndex != -1 { + valueArray = append(valueArray, toValue_string("")) + found++ + } + } + + lastIndex = match[1] + if found == limit { + goto RETURN + } + + captureCount := len(match) / 2 + for index := 1; index < captureCount; index++ { + offset := index * 2 + value := Value{} + if match[offset] != -1 { + value = toValue_string(target[match[offset]:match[offset+1]]) + } + valueArray = append(valueArray, value) + found++ + if found == limit { + goto RETURN + } + } + } + + if found != limit { + if lastIndex != targetLength { + valueArray = append(valueArray, toValue_string(target[lastIndex:targetLength])) + } else { + valueArray = append(valueArray, toValue_string("")) + } + } + + RETURN: + return toValue_object(call.runtime.newArrayOf(valueArray)) + + } else { + separator := separatorValue.string() + + splitLimit := limit + excess := false + if limit > 0 { + splitLimit = limit + 1 + excess = true + } + + split := strings.SplitN(target, separator, splitLimit) + + if excess && len(split) > limit { + split = split[:limit] + } + + valueArray := make([]Value, len(split)) + for index, value := range split { + valueArray[index] = toValue_string(value) + } + + return toValue_object(call.runtime.newArrayOf(valueArray)) + } +} + +func builtinString_slice(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + + length := int64(len(target)) + start, end := rangeStartEnd(call.ArgumentList, length, false) + if end-start <= 0 { + return toValue_string("") + } + return toValue_string(target[start:end]) +} + +func builtinString_substring(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + + length := int64(len(target)) + start, end := rangeStartEnd(call.ArgumentList, length, true) + if start > end { + start, end = end, start + } + return toValue_string(target[start:end]) +} + +func builtinString_substr(call FunctionCall) Value { + target := call.This.string() + + size := int64(len(target)) + start, length := rangeStartLength(call.ArgumentList, size) + + if start >= size { + return toValue_string("") + } + + if length <= 0 { + return toValue_string("") + } + + if start+length >= size { + // Cap length to be to the end of the string + // start = 3, length = 5, size = 4 [0, 1, 2, 3] + // 4 - 3 = 1 + // target[3:4] + length = size - start + } + + return toValue_string(target[start : start+length]) +} + +func builtinString_toLowerCase(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue_string(strings.ToLower(call.This.string())) +} + +func builtinString_toUpperCase(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue_string(strings.ToUpper(call.This.string())) +} + +// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters +const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF" + +func builtinString_trim(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue(strings.Trim(call.This.string(), + builtinString_trim_whitespace)) +} + +// Mozilla extension, not ECMAScript 5 +func builtinString_trimLeft(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue(strings.TrimLeft(call.This.string(), + builtinString_trim_whitespace)) +} + +// Mozilla extension, not ECMAScript 5 +func builtinString_trimRight(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue(strings.TrimRight(call.This.string(), + builtinString_trim_whitespace)) +} + +func builtinString_localeCompare(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + this := call.This.string() + that := call.Argument(0).string() + if this < that { + return toValue_int(-1) + } else if this == that { + return toValue_int(0) + } + return toValue_int(1) +} + +/* +An alternate version of String.trim +func builtinString_trim(call FunctionCall) Value { + checkObjectCoercible(call.This) + return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator)) +} +*/ + +func builtinString_toLocaleLowerCase(call FunctionCall) Value { + return builtinString_toLowerCase(call) +} + +func builtinString_toLocaleUpperCase(call FunctionCall) Value { + return builtinString_toUpperCase(call) +} diff --git a/vendor/github.com/robertkrimen/otto/builtin_test.go b/vendor/github.com/robertkrimen/otto/builtin_test.go new file mode 100644 index 00000000..f5be00ab --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/builtin_test.go @@ -0,0 +1,136 @@ +package otto + +import ( + "testing" +) + +func TestString_substr(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + "abc".substr(0,1), // "a" + "abc".substr(0,2), // "ab" + "abc".substr(0,3), // "abc" + "abc".substr(0,4), // "abc" + "abc".substr(0,9), // "abc" + ]; + `, "a,ab,abc,abc,abc") + + test(` + [ + "abc".substr(1,1), // "b" + "abc".substr(1,2), // "bc" + "abc".substr(1,3), // "bc" + "abc".substr(1,4), // "bc" + "abc".substr(1,9), // "bc" + ]; + `, "b,bc,bc,bc,bc") + + test(` + [ + "abc".substr(2,1), // "c" + "abc".substr(2,2), // "c" + "abc".substr(2,3), // "c" + "abc".substr(2,4), // "c" + "abc".substr(2,9), // "c" + ]; + `, "c,c,c,c,c") + + test(` + [ + "abc".substr(3,1), // "" + "abc".substr(3,2), // "" + "abc".substr(3,3), // "" + "abc".substr(3,4), // "" + "abc".substr(3,9), // "" + ]; + `, ",,,,") + + test(` + [ + "abc".substr(0), // "abc" + "abc".substr(1), // "bc" + "abc".substr(2), // "c" + "abc".substr(3), // "" + "abc".substr(9), // "" + ]; + `, "abc,bc,c,,") + + test(` + [ + "abc".substr(-9), // "abc" + "abc".substr(-3), // "abc" + "abc".substr(-2), // "bc" + "abc".substr(-1), // "c" + ]; + `, "abc,abc,bc,c") + + test(` + [ + "abc".substr(-9, 1), // "a" + "abc".substr(-3, 1), // "a" + "abc".substr(-2, 1), // "b" + "abc".substr(-1, 1), // "c" + "abc".substr(-1, 2), // "c" + ]; + `, "a,a,b,c,c") + + test(`"abcd".substr(3, 5)`, "d") + }) +} + +func Test_builtin_escape(t *testing.T) { + tt(t, func() { + is(builtin_escape("abc"), "abc") + + is(builtin_escape("="), "%3D") + + is(builtin_escape("abc=%+32"), "abc%3D%25+32") + + is(builtin_escape("世界"), "%u4E16%u754C") + }) +} + +func Test_builtin_unescape(t *testing.T) { + tt(t, func() { + is(builtin_unescape("abc"), "abc") + + is(builtin_unescape("=%3D"), "==") + + is(builtin_unescape("abc%3D%25+32"), "abc=%+32") + + is(builtin_unescape("%u4E16%u754C"), "世界") + }) +} + +func TestGlobal_escape(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + escape("abc"), // "abc" + escape("="), // "%3D" + escape("abc=%+32"), // "abc%3D%25+32" + escape("\u4e16\u754c"), // "%u4E16%u754C" + ]; + `, "abc,%3D,abc%3D%25+32,%u4E16%u754C") + }) +} + +func TestGlobal_unescape(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + unescape("abc"), // "abc" + unescape("=%3D"), // "==" + unescape("abc%3D%25+32"), // "abc=%+32" + unescape("%u4E16%u754C"), // "世界" + ]; + `, "abc,==,abc=%+32,世界") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/call_test.go b/vendor/github.com/robertkrimen/otto/call_test.go new file mode 100644 index 00000000..d1f687a9 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/call_test.go @@ -0,0 +1,1512 @@ +package otto + +import ( + "reflect" + "testing" +) + +func BenchmarkNativeCallWithString(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string) {}) + s, _ := vm.Compile("test.js", `x("zzz")`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithFloat32(b *testing.B) { + vm := New() + vm.Set("x", func(a1 float32) {}) + s, _ := vm.Compile("test.js", `x(1.1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithFloat64(b *testing.B) { + vm := New() + vm.Set("x", func(a1 float64) {}) + s, _ := vm.Compile("test.js", `x(1.1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithInt(b *testing.B) { + vm := New() + vm.Set("x", func(a1 int) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithUint(b *testing.B) { + vm := New() + vm.Set("x", func(a1 uint) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithInt8(b *testing.B) { + vm := New() + vm.Set("x", func(a1 int8) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithUint8(b *testing.B) { + vm := New() + vm.Set("x", func(a1 uint8) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithInt16(b *testing.B) { + vm := New() + vm.Set("x", func(a1 int16) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithUint16(b *testing.B) { + vm := New() + vm.Set("x", func(a1 uint16) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithInt32(b *testing.B) { + vm := New() + vm.Set("x", func(a1 int32) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithUint32(b *testing.B) { + vm := New() + vm.Set("x", func(a1 uint32) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithInt64(b *testing.B) { + vm := New() + vm.Set("x", func(a1 int64) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithUint64(b *testing.B) { + vm := New() + vm.Set("x", func(a1 uint64) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringInt(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 int) {}) + s, _ := vm.Compile("test.js", `x("zzz", 1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadic0(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x()`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadic1(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x(1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadic3(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x(1, 2, 3)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadic10(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntArray0(b *testing.B) { + vm := New() + vm.Set("x", func(a []int) {}) + s, _ := vm.Compile("test.js", `x([])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntArray1(b *testing.B) { + vm := New() + vm.Set("x", func(a []int) {}) + s, _ := vm.Compile("test.js", `x([1])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntArray3(b *testing.B) { + vm := New() + vm.Set("x", func(a []int) {}) + s, _ := vm.Compile("test.js", `x([1, 2, 3])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntArray10(b *testing.B) { + vm := New() + vm.Set("x", func(a []int) {}) + s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadicArray0(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x([])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadicArray1(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x([1])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadicArray3(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x([1, 2, 3])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithIntVariadicArray10(b *testing.B) { + vm := New() + vm.Set("x", func(a ...int) {}) + s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadic0(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a")`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadic1(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a", 1)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadic3(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a", 1, 2, 3)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadic10(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadicArray0(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a", [])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadicArray1(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a", [1])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadicArray3(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a", [1, 2, 3])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithStringIntVariadicArray10(b *testing.B) { + vm := New() + vm.Set("x", func(a1 string, a2 ...int) {}) + s, _ := vm.Compile("test.js", `x("a", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithMap(b *testing.B) { + vm := New() + vm.Set("x", func(a map[string]string) {}) + s, _ := vm.Compile("test.js", `x({a: "b", c: "d"})`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithMapVariadic(b *testing.B) { + vm := New() + vm.Set("x", func(a ...map[string]string) {}) + s, _ := vm.Compile("test.js", `x({a: "b", c: "d"}, {w: "x", y: "z"})`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithMapVariadicArray(b *testing.B) { + vm := New() + vm.Set("x", func(a ...map[string]string) {}) + s, _ := vm.Compile("test.js", `x([{a: "b", c: "d"}, {w: "x", y: "z"}])`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithFunction(b *testing.B) { + vm := New() + vm.Set("x", func(a func()) {}) + s, _ := vm.Compile("test.js", `x(function() {})`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithFunctionInt(b *testing.B) { + vm := New() + vm.Set("x", func(a func(int)) {}) + s, _ := vm.Compile("test.js", `x(function(n) {})`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func BenchmarkNativeCallWithFunctionString(b *testing.B) { + vm := New() + vm.Set("x", func(a func(string)) {}) + s, _ := vm.Compile("test.js", `x(function(n) {})`) + for i := 0; i < b.N; i++ { + vm.Run(s) + } +} + +func TestNativeCallWithString(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string) { + if a1 != "zzz" { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("zzz")`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithFloat32(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 float32) { + if a1 != 1.1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1.1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithFloat64(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 float64) { + if a1 != 1.1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1.1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithInt(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 int) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithUint(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 uint) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithInt8(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 int8) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithUint8(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 uint8) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithInt16(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 int16) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithUint16(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 uint16) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithInt32(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 int32) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithUint32(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 uint32) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithInt64(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 int64) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithUint64(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 uint64) { + if a1 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringInt(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 int) { + if a1 != "zzz" || a2 != 1 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("zzz", 1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadic0(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x()`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadic1(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{1}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadic3(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{1, 2, 3}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1, 2, 3)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadic10(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntArray0(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a []int) { + if !reflect.DeepEqual(a, []int{}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntArray1(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a []int) { + if !reflect.DeepEqual(a, []int{1}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([1])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntArray3(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a []int) { + if !reflect.DeepEqual(a, []int{1, 2, 3}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([1, 2, 3])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntArray10(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a []int) { + if !reflect.DeepEqual(a, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadicArray0(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadicArray1(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{1}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([1])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadicArray3(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{1, 2, 3}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([1, 2, 3])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithIntVariadicArray10(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...int) { + if !reflect.DeepEqual(a, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadic0(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a")`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadic1(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{1}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a", 1)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadic3(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a", 1, 2, 3)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadic10(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadicArray0(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a", [])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadicArray1(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{1}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a", [1])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadicArray3(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a", [1, 2, 3])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithStringIntVariadicArray10(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a1 string, a2 ...int) { + if a1 != "a" || !reflect.DeepEqual(a2, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x("a", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithMap(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a map[string]string) { + if !reflect.DeepEqual(a, map[string]string{"a": "b", "c": "d"}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x({a: "b", c: "d"})`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithMapVariadic(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...map[string]string) { + if !reflect.DeepEqual(a, []map[string]string{{"a": "b", "c": "d"}, {"w": "x", "y": "z"}}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x({a: "b", c: "d"}, {w: "x", y: "z"})`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithMapVariadicArray(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(a ...map[string]string) { + if !reflect.DeepEqual(a, []map[string]string{{"a": "b", "c": "d"}, {"w": "x", "y": "z"}}) { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x([{a: "b", c: "d"}, {w: "x", y: "z"}])`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithFunctionVoidBool(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(fn func() bool) { + if !fn() { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(function() { return true; })`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithFunctionIntInt(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(fn func(int) int) { + if fn(5) != 5 { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(function(n) { return n; })`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallWithFunctionStringString(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", func(fn func(string) string) { + if fn("zzz") != "zzz" { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `x(function(n) { return n; })`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +type testNativeCallWithStruct struct { + Prefix string +} + +type testNativeCallWithStructArg struct { + Text string +} + +func (t testNativeCallWithStruct) MakeStruct(s string) testNativeCallWithStructArg { + return testNativeCallWithStructArg{Text: s} +} + +func (t testNativeCallWithStruct) MakeStructPointer(s string) *testNativeCallWithStructArg { + return &testNativeCallWithStructArg{Text: s} +} + +func (t testNativeCallWithStruct) CallWithStruct(a testNativeCallWithStructArg) string { + return t.Prefix + a.Text +} + +func (t *testNativeCallWithStruct) CallPointerWithStruct(a testNativeCallWithStructArg) string { + return t.Prefix + a.Text +} + +func (t testNativeCallWithStruct) CallWithStructPointer(a *testNativeCallWithStructArg) string { + return t.Prefix + a.Text +} + +func (t *testNativeCallWithStruct) CallPointerWithStructPointer(a *testNativeCallWithStructArg) string { + return t.Prefix + a.Text +} + +func TestNativeCallMethodWithStruct(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", testNativeCallWithStruct{Prefix: "a"}) + + vm.Set("t", func(s string) { + if s != "ab" { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `t(x.CallWithStruct(x.MakeStruct("b")))`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallPointerMethodWithStruct(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", &testNativeCallWithStruct{Prefix: "a"}) + + vm.Set("t", func(s string) { + if s != "ab" { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `t(x.CallPointerWithStruct(x.MakeStruct("b")))`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallMethodWithStructPointer(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", testNativeCallWithStruct{Prefix: "a"}) + + vm.Set("t", func(s string) { + if s != "ab" { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `t(x.CallWithStructPointer(x.MakeStructPointer("b")))`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallPointerMethodWithStructPointer(t *testing.T) { + vm := New() + + called := false + + vm.Set("x", &testNativeCallWithStruct{Prefix: "a"}) + + vm.Set("t", func(s string) { + if s != "ab" { + t.Fail() + } + + called = true + }) + + s, _ := vm.Compile("test.js", `t(x.CallPointerWithStructPointer(x.MakeStructPointer("b")))`) + + if _, err := vm.Run(s); err != nil { + t.Logf("err should have been nil; was %s\n", err.Error()) + t.Fail() + } + + if !called { + t.Fail() + } +} + +func TestNativeCallNilInterfaceArg(t *testing.T) { + vm := New() + vm.Set("f1", func(v interface{}) {}) + vm.Call("f1", nil, nil) +} diff --git a/vendor/github.com/robertkrimen/otto/clone.go b/vendor/github.com/robertkrimen/otto/clone.go new file mode 100644 index 00000000..23b59f8a --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/clone.go @@ -0,0 +1,173 @@ +package otto + +import ( + "fmt" +) + +type _clone struct { + runtime *_runtime + _object map[*_object]*_object + _objectStash map[*_objectStash]*_objectStash + _dclStash map[*_dclStash]*_dclStash + _fnStash map[*_fnStash]*_fnStash +} + +func (in *_runtime) clone() *_runtime { + + in.lck.Lock() + defer in.lck.Unlock() + + out := &_runtime{ + debugger: in.debugger, + random: in.random, + stackLimit: in.stackLimit, + traceLimit: in.traceLimit, + } + + clone := _clone{ + runtime: out, + _object: make(map[*_object]*_object), + _objectStash: make(map[*_objectStash]*_objectStash), + _dclStash: make(map[*_dclStash]*_dclStash), + _fnStash: make(map[*_fnStash]*_fnStash), + } + + globalObject := clone.object(in.globalObject) + out.globalStash = out.newObjectStash(globalObject, nil) + out.globalObject = globalObject + out.global = _global{ + clone.object(in.global.Object), + clone.object(in.global.Function), + clone.object(in.global.Array), + clone.object(in.global.String), + clone.object(in.global.Boolean), + clone.object(in.global.Number), + clone.object(in.global.Math), + clone.object(in.global.Date), + clone.object(in.global.RegExp), + clone.object(in.global.Error), + clone.object(in.global.EvalError), + clone.object(in.global.TypeError), + clone.object(in.global.RangeError), + clone.object(in.global.ReferenceError), + clone.object(in.global.SyntaxError), + clone.object(in.global.URIError), + clone.object(in.global.JSON), + + clone.object(in.global.ObjectPrototype), + clone.object(in.global.FunctionPrototype), + clone.object(in.global.ArrayPrototype), + clone.object(in.global.StringPrototype), + clone.object(in.global.BooleanPrototype), + clone.object(in.global.NumberPrototype), + clone.object(in.global.DatePrototype), + clone.object(in.global.RegExpPrototype), + clone.object(in.global.ErrorPrototype), + clone.object(in.global.EvalErrorPrototype), + clone.object(in.global.TypeErrorPrototype), + clone.object(in.global.RangeErrorPrototype), + clone.object(in.global.ReferenceErrorPrototype), + clone.object(in.global.SyntaxErrorPrototype), + clone.object(in.global.URIErrorPrototype), + } + + out.eval = out.globalObject.property["eval"].value.(Value).value.(*_object) + out.globalObject.prototype = out.global.ObjectPrototype + + // Not sure if this is necessary, but give some help to the GC + clone.runtime = nil + clone._object = nil + clone._objectStash = nil + clone._dclStash = nil + clone._fnStash = nil + + return out +} + +func (clone *_clone) object(in *_object) *_object { + if out, exists := clone._object[in]; exists { + return out + } + out := &_object{} + clone._object[in] = out + return in.objectClass.clone(in, out, clone) +} + +func (clone *_clone) dclStash(in *_dclStash) (*_dclStash, bool) { + if out, exists := clone._dclStash[in]; exists { + return out, true + } + out := &_dclStash{} + clone._dclStash[in] = out + return out, false +} + +func (clone *_clone) objectStash(in *_objectStash) (*_objectStash, bool) { + if out, exists := clone._objectStash[in]; exists { + return out, true + } + out := &_objectStash{} + clone._objectStash[in] = out + return out, false +} + +func (clone *_clone) fnStash(in *_fnStash) (*_fnStash, bool) { + if out, exists := clone._fnStash[in]; exists { + return out, true + } + out := &_fnStash{} + clone._fnStash[in] = out + return out, false +} + +func (clone *_clone) value(in Value) Value { + out := in + switch value := in.value.(type) { + case *_object: + out.value = clone.object(value) + } + return out +} + +func (clone *_clone) valueArray(in []Value) []Value { + out := make([]Value, len(in)) + for index, value := range in { + out[index] = clone.value(value) + } + return out +} + +func (clone *_clone) stash(in _stash) _stash { + if in == nil { + return nil + } + return in.clone(clone) +} + +func (clone *_clone) property(in _property) _property { + out := in + + switch value := in.value.(type) { + case Value: + out.value = clone.value(value) + case _propertyGetSet: + p := _propertyGetSet{} + if value[0] != nil { + p[0] = clone.object(value[0]) + } + if value[1] != nil { + p[1] = clone.object(value[1]) + } + out.value = p + default: + panic(fmt.Errorf("in.value.(Value) != true; in.value is %T", in.value)) + } + + return out +} + +func (clone *_clone) dclProperty(in _dclProperty) _dclProperty { + out := in + out.value = clone.value(in.value) + return out +} diff --git a/vendor/github.com/robertkrimen/otto/clone_test.go b/vendor/github.com/robertkrimen/otto/clone_test.go new file mode 100644 index 00000000..8e60464a --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/clone_test.go @@ -0,0 +1,18 @@ +package otto + +import ( + "testing" +) + +func TestCloneGetterSetter(t *testing.T) { + vm := New() + + vm.Run(`var x = Object.create(null, { + x: { + get: function() {}, + set: function() {}, + }, + })`) + + vm.Copy() +} diff --git a/vendor/github.com/robertkrimen/otto/cmpl.go b/vendor/github.com/robertkrimen/otto/cmpl.go new file mode 100644 index 00000000..c191b452 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/cmpl.go @@ -0,0 +1,24 @@ +package otto + +import ( + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" +) + +type _file struct { + name string + src string + base int // This will always be 1 or greater +} + +type _compiler struct { + file *file.File + program *ast.Program +} + +func (cmpl *_compiler) parse() *_nodeProgram { + if cmpl.program != nil { + cmpl.file = cmpl.program.File + } + return cmpl._parse(cmpl.program) +} diff --git a/vendor/github.com/robertkrimen/otto/cmpl_evaluate.go b/vendor/github.com/robertkrimen/otto/cmpl_evaluate.go new file mode 100644 index 00000000..6741bf39 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/cmpl_evaluate.go @@ -0,0 +1,96 @@ +package otto + +import ( + "strconv" +) + +func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram, eval bool) Value { + if !eval { + self.enterGlobalScope() + defer func() { + self.leaveScope() + }() + } + self.cmpl_functionDeclaration(node.functionList) + self.cmpl_variableDeclaration(node.varList) + self.scope.frame.file = node.file + return self.cmpl_evaluate_nodeStatementList(node.body) +} + +func (self *_runtime) cmpl_call_nodeFunction(function *_object, stash *_fnStash, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value { + + indexOfParameterName := make([]string, len(argumentList)) + // function(abc, def, ghi) + // indexOfParameterName[0] = "abc" + // indexOfParameterName[1] = "def" + // indexOfParameterName[2] = "ghi" + // ... + + argumentsFound := false + for index, name := range node.parameterList { + if name == "arguments" { + argumentsFound = true + } + value := Value{} + if index < len(argumentList) { + value = argumentList[index] + indexOfParameterName[index] = name + } + // strict = false + self.scope.lexical.setValue(name, value, false) + } + + if !argumentsFound { + arguments := self.newArgumentsObject(indexOfParameterName, stash, len(argumentList)) + arguments.defineProperty("callee", toValue_object(function), 0101, false) + stash.arguments = arguments + // strict = false + self.scope.lexical.setValue("arguments", toValue_object(arguments), false) + for index, _ := range argumentList { + if index < len(node.parameterList) { + continue + } + indexAsString := strconv.FormatInt(int64(index), 10) + arguments.defineProperty(indexAsString, argumentList[index], 0111, false) + } + } + + self.cmpl_functionDeclaration(node.functionList) + self.cmpl_variableDeclaration(node.varList) + + result := self.cmpl_evaluate_nodeStatement(node.body) + if result.kind == valueResult { + return result + } + + return Value{} +} + +func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) { + executionContext := self.scope + eval := executionContext.eval + stash := executionContext.variable + + for _, function := range list { + name := function.name + value := self.cmpl_evaluate_nodeExpression(function) + if !stash.hasBinding(name) { + stash.createBinding(name, eval == true, value) + } else { + // TODO 10.5.5.e + stash.setBinding(name, value, false) // TODO strict + } + } +} + +func (self *_runtime) cmpl_variableDeclaration(list []string) { + executionContext := self.scope + eval := executionContext.eval + stash := executionContext.variable + + for _, name := range list { + if !stash.hasBinding(name) { + stash.createBinding(name, eval == true, Value{}) // TODO strict? + } + } +} diff --git a/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go b/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go new file mode 100644 index 00000000..8586a484 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go @@ -0,0 +1,460 @@ +package otto + +import ( + "fmt" + "math" + "runtime" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value { + // Allow interpreter interruption + // If the Interrupt channel is nil, then + // we avoid runtime.Gosched() overhead (if any) + // FIXME: Test this + if self.otto.Interrupt != nil { + runtime.Gosched() + select { + case value := <-self.otto.Interrupt: + value() + default: + } + } + + switch node := node.(type) { + + case *_nodeArrayLiteral: + return self.cmpl_evaluate_nodeArrayLiteral(node) + + case *_nodeAssignExpression: + return self.cmpl_evaluate_nodeAssignExpression(node) + + case *_nodeBinaryExpression: + if node.comparison { + return self.cmpl_evaluate_nodeBinaryExpression_comparison(node) + } else { + return self.cmpl_evaluate_nodeBinaryExpression(node) + } + + case *_nodeBracketExpression: + return self.cmpl_evaluate_nodeBracketExpression(node) + + case *_nodeCallExpression: + return self.cmpl_evaluate_nodeCallExpression(node, nil) + + case *_nodeConditionalExpression: + return self.cmpl_evaluate_nodeConditionalExpression(node) + + case *_nodeDotExpression: + return self.cmpl_evaluate_nodeDotExpression(node) + + case *_nodeFunctionLiteral: + var local = self.scope.lexical + if node.name != "" { + local = self.newDeclarationStash(local) + } + + value := toValue_object(self.newNodeFunction(node, local)) + if node.name != "" { + local.createBinding(node.name, false, value) + } + return value + + case *_nodeIdentifier: + name := node.name + // TODO Should be true or false (strictness) depending on context + // getIdentifierReference should not return nil, but we check anyway and panic + // so as not to propagate the nil into something else + reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx)) + if reference == nil { + // Should never get here! + panic(hereBeDragons("referenceError == nil: " + name)) + } + return toValue(reference) + + case *_nodeLiteral: + return node.value + + case *_nodeNewExpression: + return self.cmpl_evaluate_nodeNewExpression(node) + + case *_nodeObjectLiteral: + return self.cmpl_evaluate_nodeObjectLiteral(node) + + case *_nodeRegExpLiteral: + return toValue_object(self._newRegExp(node.pattern, node.flags)) + + case *_nodeSequenceExpression: + return self.cmpl_evaluate_nodeSequenceExpression(node) + + case *_nodeThisExpression: + return toValue_object(self.scope.this) + + case *_nodeUnaryExpression: + return self.cmpl_evaluate_nodeUnaryExpression(node) + + case *_nodeVariableExpression: + return self.cmpl_evaluate_nodeVariableExpression(node) + } + + panic(fmt.Errorf("Here be dragons: evaluate_nodeExpression(%T)", node)) +} + +func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Value { + + valueArray := []Value{} + + for _, node := range node.value { + if node == nil { + valueArray = append(valueArray, emptyValue) + } else { + valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve()) + } + } + + result := self.newArrayOf(valueArray) + + return toValue_object(result) +} + +func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left) + right := self.cmpl_evaluate_nodeExpression(node.right) + rightValue := right.resolve() + + result := rightValue + if node.operator != token.ASSIGN { + result = self.calculateBinaryExpression(node.operator, left, rightValue) + } + + self.putValue(left.reference(), result) + + return result +} + +func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left) + leftValue := left.resolve() + + switch node.operator { + // Logical + case token.LOGICAL_AND: + if !leftValue.bool() { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return right.resolve() + case token.LOGICAL_OR: + if leftValue.bool() { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return right.resolve() + } + + return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right)) +} + +func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left).resolve() + right := self.cmpl_evaluate_nodeExpression(node.right).resolve() + + return toValue_bool(self.calculateComparison(node.operator, left, right)) +} + +func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value { + target := self.cmpl_evaluate_nodeExpression(node.left) + targetValue := target.resolve() + member := self.cmpl_evaluate_nodeExpression(node.member) + memberValue := member.resolve() + + // TODO Pass in base value as-is, and defer toObject till later? + object, err := self.objectCoerce(targetValue) + if err != nil { + panic(self.panicTypeError("Cannot access member '%s' of %s", memberValue.string(), err.Error(), _at(node.idx))) + } + return toValue(newPropertyReference(self, object, memberValue.string(), false, _at(node.idx))) +} + +func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value { + rt := self + this := Value{} + callee := self.cmpl_evaluate_nodeExpression(node.callee) + + argumentList := []Value{} + if withArgumentList != nil { + argumentList = self.toValueArray(withArgumentList...) + } else { + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) + } + } + + rf := callee.reference() + vl := callee.resolve() + + eval := false // Whether this call is a (candidate for) direct call to eval + name := "" + if rf != nil { + switch rf := rf.(type) { + case *_propertyReference: + name = rf.name + object := rf.base + this = toValue_object(object) + eval = rf.name == "eval" // Possible direct eval + case *_stashReference: + // TODO ImplicitThisValue + name = rf.name + eval = rf.name == "eval" // Possible direct eval + default: + // FIXME? + panic(rt.panicTypeError("Here be dragons")) + } + } + + at := _at(-1) + switch callee := node.callee.(type) { + case *_nodeIdentifier: + at = _at(callee.idx) + case *_nodeDotExpression: + at = _at(callee.idx) + case *_nodeBracketExpression: + at = _at(callee.idx) + } + + frame := _frame{ + callee: name, + file: self.scope.frame.file, + } + + if !vl.IsFunction() { + if name == "" { + // FIXME Maybe typeof? + panic(rt.panicTypeError("%v is not a function", vl, at)) + } + panic(rt.panicTypeError("'%s' is not a function", name, at)) + } + + self.scope.frame.offset = int(at) + + return vl._object().call(this, argumentList, eval, frame) +} + +func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := test.resolve() + if testValue.bool() { + return self.cmpl_evaluate_nodeExpression(node.consequent) + } + return self.cmpl_evaluate_nodeExpression(node.alternate) +} + +func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value { + target := self.cmpl_evaluate_nodeExpression(node.left) + targetValue := target.resolve() + // TODO Pass in base value as-is, and defer toObject till later? + object, err := self.objectCoerce(targetValue) + if err != nil { + panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error(), _at(node.idx))) + } + return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx))) +} + +func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value { + rt := self + callee := self.cmpl_evaluate_nodeExpression(node.callee) + + argumentList := []Value{} + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) + } + + rf := callee.reference() + vl := callee.resolve() + + name := "" + if rf != nil { + switch rf := rf.(type) { + case *_propertyReference: + name = rf.name + case *_stashReference: + name = rf.name + default: + panic(rt.panicTypeError("Here be dragons")) + } + } + + at := _at(-1) + switch callee := node.callee.(type) { + case *_nodeIdentifier: + at = _at(callee.idx) + case *_nodeDotExpression: + at = _at(callee.idx) + case *_nodeBracketExpression: + at = _at(callee.idx) + } + + if !vl.IsFunction() { + if name == "" { + // FIXME Maybe typeof? + panic(rt.panicTypeError("%v is not a function", vl, at)) + } + panic(rt.panicTypeError("'%s' is not a function", name, at)) + } + + self.scope.frame.offset = int(at) + + return vl._object().construct(argumentList) +} + +func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value { + + result := self.newObject() + + for _, property := range node.value { + switch property.kind { + case "value": + result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false) + case "get": + getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) + descriptor := _property{} + descriptor.mode = 0211 + descriptor.value = _propertyGetSet{getter, nil} + result.defineOwnProperty(property.key, descriptor, false) + case "set": + setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) + descriptor := _property{} + descriptor.mode = 0211 + descriptor.value = _propertyGetSet{nil, setter} + result.defineOwnProperty(property.key, descriptor, false) + default: + panic(fmt.Errorf("Here be dragons: evaluate_nodeObjectLiteral: invalid property.Kind: %v", property.kind)) + } + } + + return toValue_object(result) +} + +func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceExpression) Value { + var result Value + for _, node := range node.sequence { + result = self.cmpl_evaluate_nodeExpression(node) + result = result.resolve() + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpression) Value { + + target := self.cmpl_evaluate_nodeExpression(node.operand) + switch node.operator { + case token.TYPEOF, token.DELETE: + if target.kind == valueReference && target.reference().invalid() { + if node.operator == token.TYPEOF { + return toValue_string("undefined") + } + return trueValue + } + } + + switch node.operator { + case token.NOT: + targetValue := target.resolve() + if targetValue.bool() { + return falseValue + } + return trueValue + case token.BITWISE_NOT: + targetValue := target.resolve() + integerValue := toInt32(targetValue) + return toValue_int32(^integerValue) + case token.PLUS: + targetValue := target.resolve() + return toValue_float64(targetValue.float64()) + case token.MINUS: + targetValue := target.resolve() + value := targetValue.float64() + // TODO Test this + sign := float64(-1) + if math.Signbit(value) { + sign = 1 + } + return toValue_float64(math.Copysign(value, sign)) + case token.INCREMENT: + targetValue := target.resolve() + if node.postfix { + // Postfix++ + oldValue := targetValue.float64() + newValue := toValue_float64(+1 + oldValue) + self.putValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // ++Prefix + newValue := toValue_float64(+1 + targetValue.float64()) + self.putValue(target.reference(), newValue) + return newValue + } + case token.DECREMENT: + targetValue := target.resolve() + if node.postfix { + // Postfix-- + oldValue := targetValue.float64() + newValue := toValue_float64(-1 + oldValue) + self.putValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // --Prefix + newValue := toValue_float64(-1 + targetValue.float64()) + self.putValue(target.reference(), newValue) + return newValue + } + case token.VOID: + target.resolve() // FIXME Side effect? + return Value{} + case token.DELETE: + reference := target.reference() + if reference == nil { + return trueValue + } + return toValue_bool(target.reference().delete()) + case token.TYPEOF: + targetValue := target.resolve() + switch targetValue.kind { + case valueUndefined: + return toValue_string("undefined") + case valueNull: + return toValue_string("object") + case valueBoolean: + return toValue_string("boolean") + case valueNumber: + return toValue_string("number") + case valueString: + return toValue_string("string") + case valueObject: + if targetValue._object().isCall() { + return toValue_string("function") + } + return toValue_string("object") + default: + // FIXME ? + } + } + + panic(hereBeDragons()) +} + +func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value { + if node.initializer != nil { + // FIXME If reference is nil + left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx)) + right := self.cmpl_evaluate_nodeExpression(node.initializer) + rightValue := right.resolve() + + self.putValue(left, rightValue) + } + return toValue_string(node.name) +} diff --git a/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go b/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go new file mode 100644 index 00000000..e16c6ac6 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go @@ -0,0 +1,424 @@ +package otto + +import ( + "fmt" + "runtime" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value { + // Allow interpreter interruption + // If the Interrupt channel is nil, then + // we avoid runtime.Gosched() overhead (if any) + // FIXME: Test this + if self.otto.Interrupt != nil { + runtime.Gosched() + select { + case value := <-self.otto.Interrupt: + value() + default: + } + } + + switch node := node.(type) { + + case *_nodeBlockStatement: + labels := self.labels + self.labels = nil + + value := self.cmpl_evaluate_nodeStatementList(node.list) + switch value.kind { + case valueResult: + switch value.evaluateBreak(labels) { + case resultBreak: + return emptyValue + } + } + return value + + case *_nodeBranchStatement: + target := node.label + switch node.branch { // FIXME Maybe node.kind? node.operator? + case token.BREAK: + return toValue(newBreakResult(target)) + case token.CONTINUE: + return toValue(newContinueResult(target)) + } + + case *_nodeDebuggerStatement: + if self.debugger != nil { + self.debugger(self.otto) + } + return emptyValue // Nothing happens. + + case *_nodeDoWhileStatement: + return self.cmpl_evaluate_nodeDoWhileStatement(node) + + case *_nodeEmptyStatement: + return emptyValue + + case *_nodeExpressionStatement: + return self.cmpl_evaluate_nodeExpression(node.expression) + + case *_nodeForInStatement: + return self.cmpl_evaluate_nodeForInStatement(node) + + case *_nodeForStatement: + return self.cmpl_evaluate_nodeForStatement(node) + + case *_nodeIfStatement: + return self.cmpl_evaluate_nodeIfStatement(node) + + case *_nodeLabelledStatement: + self.labels = append(self.labels, node.label) + defer func() { + if len(self.labels) > 0 { + self.labels = self.labels[:len(self.labels)-1] // Pop the label + } else { + self.labels = nil + } + }() + return self.cmpl_evaluate_nodeStatement(node.statement) + + case *_nodeReturnStatement: + if node.argument != nil { + return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve())) + } + return toValue(newReturnResult(Value{})) + + case *_nodeSwitchStatement: + return self.cmpl_evaluate_nodeSwitchStatement(node) + + case *_nodeThrowStatement: + value := self.cmpl_evaluate_nodeExpression(node.argument).resolve() + panic(newException(value)) + + case *_nodeTryStatement: + return self.cmpl_evaluate_nodeTryStatement(node) + + case *_nodeVariableStatement: + // Variables are already defined, this is initialization only + for _, variable := range node.list { + self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression)) + } + return emptyValue + + case *_nodeWhileStatement: + return self.cmpl_evaluate_nodeWhileStatement(node) + + case *_nodeWithStatement: + return self.cmpl_evaluate_nodeWithStatement(node) + + } + + panic(fmt.Errorf("Here be dragons: evaluate_nodeStatement(%T)", node)) +} + +func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Value { + var result Value + for _, node := range list { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + return value + case valueEmpty: + default: + // We have getValue here to (for example) trigger a + // ReferenceError (of the not defined variety) + // Not sure if this is the best way to error out early + // for such errors or if there is a better way + // TODO Do we still need this? + result = value.resolve() + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + test := node.test + + result := emptyValue +resultBreak: + for { + for _, node := range node.body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreak + case resultContinue: + goto resultContinue + } + case valueEmpty: + default: + result = value + } + } + resultContinue: + if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { + // Stahp: do ... while (false) + break + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + source := self.cmpl_evaluate_nodeExpression(node.source) + sourceValue := source.resolve() + + switch sourceValue.kind { + case valueUndefined, valueNull: + return emptyValue + } + + sourceObject := self.toObject(sourceValue) + + into := node.into + body := node.body + + result := emptyValue + object := sourceObject + for object != nil { + enumerateValue := emptyValue + object.enumerate(false, func(name string) bool { + into := self.cmpl_evaluate_nodeExpression(into) + // In the case of: for (var abc in def) ... + if into.reference() == nil { + identifier := into.string() + // TODO Should be true or false (strictness) depending on context + into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1)) + } + self.putValue(into.reference(), toValue_string(name)) + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + enumerateValue = value + return false + case resultBreak: + object = nil + return false + case resultContinue: + return true + } + case valueEmpty: + default: + enumerateValue = value + } + } + return true + }) + if object == nil { + break + } + object = object.prototype + if !enumerateValue.isEmpty() { + result = enumerateValue + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + initializer := node.initializer + test := node.test + update := node.update + body := node.body + + if initializer != nil { + initialResult := self.cmpl_evaluate_nodeExpression(initializer) + initialResult.resolve() // Side-effect trigger + } + + result := emptyValue +resultBreak: + for { + if test != nil { + testResult := self.cmpl_evaluate_nodeExpression(test) + testResultValue := testResult.resolve() + if testResultValue.bool() == false { + break + } + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreak + case resultContinue: + goto resultContinue + } + case valueEmpty: + default: + result = value + } + } + resultContinue: + if update != nil { + updateResult := self.cmpl_evaluate_nodeExpression(update) + updateResult.resolve() // Side-effect trigger + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := test.resolve() + if testValue.bool() { + return self.cmpl_evaluate_nodeStatement(node.consequent) + } else if node.alternate != nil { + return self.cmpl_evaluate_nodeStatement(node.alternate) + } + + return emptyValue +} + +func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + discriminantResult := self.cmpl_evaluate_nodeExpression(node.discriminant) + target := node.default_ + + for index, clause := range node.body { + test := clause.test + if test != nil { + if self.calculateComparison(token.STRICT_EQUAL, discriminantResult, self.cmpl_evaluate_nodeExpression(test)) { + target = index + break + } + } + } + + result := emptyValue + if target != -1 { + for _, clause := range node.body[target:] { + for _, statement := range clause.consequent { + value := self.cmpl_evaluate_nodeStatement(statement) + switch value.kind { + case valueResult: + switch value.evaluateBreak(labels) { + case resultReturn: + return value + case resultBreak: + return emptyValue + } + case valueEmpty: + default: + result = value + } + } + } + } + + return result +} + +func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Value { + tryCatchValue, exception := self.tryCatchEvaluate(func() Value { + return self.cmpl_evaluate_nodeStatement(node.body) + }) + + if exception && node.catch != nil { + outer := self.scope.lexical + self.scope.lexical = self.newDeclarationStash(outer) + defer func() { + self.scope.lexical = outer + }() + // TODO If necessary, convert TypeError => TypeError + // That, is, such errors can be thrown despite not being JavaScript "native" + // strict = false + self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false) + + // FIXME node.CatchParameter + // FIXME node.Catch + tryCatchValue, exception = self.tryCatchEvaluate(func() Value { + return self.cmpl_evaluate_nodeStatement(node.catch.body) + }) + } + + if node.finally != nil { + finallyValue := self.cmpl_evaluate_nodeStatement(node.finally) + if finallyValue.kind == valueResult { + return finallyValue + } + } + + if exception { + panic(newException(tryCatchValue)) + } + + return tryCatchValue +} + +func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement) Value { + + test := node.test + body := node.body + labels := append(self.labels, "") + self.labels = nil + + result := emptyValue +resultBreakContinue: + for { + if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { + // Stahp: while (false) ... + break + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreakContinue + case resultContinue: + continue resultBreakContinue + } + case valueEmpty: + default: + result = value + } + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value { + object := self.cmpl_evaluate_nodeExpression(node.object) + outer := self.scope.lexical + lexical := self.newObjectStash(self.toObject(object.resolve()), outer) + self.scope.lexical = lexical + defer func() { + self.scope.lexical = outer + }() + + return self.cmpl_evaluate_nodeStatement(node.body) +} diff --git a/vendor/github.com/robertkrimen/otto/cmpl_parse.go b/vendor/github.com/robertkrimen/otto/cmpl_parse.go new file mode 100644 index 00000000..dc5baa12 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/cmpl_parse.go @@ -0,0 +1,656 @@ +package otto + +import ( + "fmt" + "regexp" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +var trueLiteral = &_nodeLiteral{value: toValue_bool(true)} +var falseLiteral = &_nodeLiteral{value: toValue_bool(false)} +var nullLiteral = &_nodeLiteral{value: nullValue} +var emptyStatement = &_nodeEmptyStatement{} + +func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression { + if in == nil { + return nil + } + + switch in := in.(type) { + + case *ast.ArrayLiteral: + out := &_nodeArrayLiteral{ + value: make([]_nodeExpression, len(in.Value)), + } + for i, value := range in.Value { + out.value[i] = cmpl.parseExpression(value) + } + return out + + case *ast.AssignExpression: + return &_nodeAssignExpression{ + operator: in.Operator, + left: cmpl.parseExpression(in.Left), + right: cmpl.parseExpression(in.Right), + } + + case *ast.BinaryExpression: + return &_nodeBinaryExpression{ + operator: in.Operator, + left: cmpl.parseExpression(in.Left), + right: cmpl.parseExpression(in.Right), + comparison: in.Comparison, + } + + case *ast.BooleanLiteral: + if in.Value { + return trueLiteral + } + return falseLiteral + + case *ast.BracketExpression: + return &_nodeBracketExpression{ + idx: in.Left.Idx0(), + left: cmpl.parseExpression(in.Left), + member: cmpl.parseExpression(in.Member), + } + + case *ast.CallExpression: + out := &_nodeCallExpression{ + callee: cmpl.parseExpression(in.Callee), + argumentList: make([]_nodeExpression, len(in.ArgumentList)), + } + for i, value := range in.ArgumentList { + out.argumentList[i] = cmpl.parseExpression(value) + } + return out + + case *ast.ConditionalExpression: + return &_nodeConditionalExpression{ + test: cmpl.parseExpression(in.Test), + consequent: cmpl.parseExpression(in.Consequent), + alternate: cmpl.parseExpression(in.Alternate), + } + + case *ast.DotExpression: + return &_nodeDotExpression{ + idx: in.Left.Idx0(), + left: cmpl.parseExpression(in.Left), + identifier: in.Identifier.Name, + } + + case *ast.EmptyExpression: + return nil + + case *ast.FunctionLiteral: + name := "" + if in.Name != nil { + name = in.Name.Name + } + out := &_nodeFunctionLiteral{ + name: name, + body: cmpl.parseStatement(in.Body), + source: in.Source, + file: cmpl.file, + } + if in.ParameterList != nil { + list := in.ParameterList.List + out.parameterList = make([]string, len(list)) + for i, value := range list { + out.parameterList[i] = value.Name + } + } + for _, value := range in.DeclarationList { + switch value := value.(type) { + case *ast.FunctionDeclaration: + out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral)) + case *ast.VariableDeclaration: + for _, value := range value.List { + out.varList = append(out.varList, value.Name) + } + default: + panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value)) + } + } + return out + + case *ast.Identifier: + return &_nodeIdentifier{ + idx: in.Idx, + name: in.Name, + } + + case *ast.NewExpression: + out := &_nodeNewExpression{ + callee: cmpl.parseExpression(in.Callee), + argumentList: make([]_nodeExpression, len(in.ArgumentList)), + } + for i, value := range in.ArgumentList { + out.argumentList[i] = cmpl.parseExpression(value) + } + return out + + case *ast.NullLiteral: + return nullLiteral + + case *ast.NumberLiteral: + return &_nodeLiteral{ + value: toValue(in.Value), + } + + case *ast.ObjectLiteral: + out := &_nodeObjectLiteral{ + value: make([]_nodeProperty, len(in.Value)), + } + for i, value := range in.Value { + out.value[i] = _nodeProperty{ + key: value.Key, + kind: value.Kind, + value: cmpl.parseExpression(value.Value), + } + } + return out + + case *ast.RegExpLiteral: + return &_nodeRegExpLiteral{ + flags: in.Flags, + pattern: in.Pattern, + } + + case *ast.SequenceExpression: + out := &_nodeSequenceExpression{ + sequence: make([]_nodeExpression, len(in.Sequence)), + } + for i, value := range in.Sequence { + out.sequence[i] = cmpl.parseExpression(value) + } + return out + + case *ast.StringLiteral: + return &_nodeLiteral{ + value: toValue_string(in.Value), + } + + case *ast.ThisExpression: + return &_nodeThisExpression{} + + case *ast.UnaryExpression: + return &_nodeUnaryExpression{ + operator: in.Operator, + operand: cmpl.parseExpression(in.Operand), + postfix: in.Postfix, + } + + case *ast.VariableExpression: + return &_nodeVariableExpression{ + idx: in.Idx0(), + name: in.Name, + initializer: cmpl.parseExpression(in.Initializer), + } + + } + + panic(fmt.Errorf("Here be dragons: cmpl.parseExpression(%T)", in)) +} + +func (cmpl *_compiler) parseStatement(in ast.Statement) _nodeStatement { + if in == nil { + return nil + } + + switch in := in.(type) { + + case *ast.BlockStatement: + out := &_nodeBlockStatement{ + list: make([]_nodeStatement, len(in.List)), + } + for i, value := range in.List { + out.list[i] = cmpl.parseStatement(value) + } + return out + + case *ast.BranchStatement: + out := &_nodeBranchStatement{ + branch: in.Token, + } + if in.Label != nil { + out.label = in.Label.Name + } + return out + + case *ast.DebuggerStatement: + return &_nodeDebuggerStatement{} + + case *ast.DoWhileStatement: + out := &_nodeDoWhileStatement{ + test: cmpl.parseExpression(in.Test), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.EmptyStatement: + return emptyStatement + + case *ast.ExpressionStatement: + return &_nodeExpressionStatement{ + expression: cmpl.parseExpression(in.Expression), + } + + case *ast.ForInStatement: + out := &_nodeForInStatement{ + into: cmpl.parseExpression(in.Into), + source: cmpl.parseExpression(in.Source), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.ForStatement: + out := &_nodeForStatement{ + initializer: cmpl.parseExpression(in.Initializer), + update: cmpl.parseExpression(in.Update), + test: cmpl.parseExpression(in.Test), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.FunctionStatement: + return emptyStatement + + case *ast.IfStatement: + return &_nodeIfStatement{ + test: cmpl.parseExpression(in.Test), + consequent: cmpl.parseStatement(in.Consequent), + alternate: cmpl.parseStatement(in.Alternate), + } + + case *ast.LabelledStatement: + return &_nodeLabelledStatement{ + label: in.Label.Name, + statement: cmpl.parseStatement(in.Statement), + } + + case *ast.ReturnStatement: + return &_nodeReturnStatement{ + argument: cmpl.parseExpression(in.Argument), + } + + case *ast.SwitchStatement: + out := &_nodeSwitchStatement{ + discriminant: cmpl.parseExpression(in.Discriminant), + default_: in.Default, + body: make([]*_nodeCaseStatement, len(in.Body)), + } + for i, clause := range in.Body { + out.body[i] = &_nodeCaseStatement{ + test: cmpl.parseExpression(clause.Test), + consequent: make([]_nodeStatement, len(clause.Consequent)), + } + for j, value := range clause.Consequent { + out.body[i].consequent[j] = cmpl.parseStatement(value) + } + } + return out + + case *ast.ThrowStatement: + return &_nodeThrowStatement{ + argument: cmpl.parseExpression(in.Argument), + } + + case *ast.TryStatement: + out := &_nodeTryStatement{ + body: cmpl.parseStatement(in.Body), + finally: cmpl.parseStatement(in.Finally), + } + if in.Catch != nil { + out.catch = &_nodeCatchStatement{ + parameter: in.Catch.Parameter.Name, + body: cmpl.parseStatement(in.Catch.Body), + } + } + return out + + case *ast.VariableStatement: + out := &_nodeVariableStatement{ + list: make([]_nodeExpression, len(in.List)), + } + for i, value := range in.List { + out.list[i] = cmpl.parseExpression(value) + } + return out + + case *ast.WhileStatement: + out := &_nodeWhileStatement{ + test: cmpl.parseExpression(in.Test), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.WithStatement: + return &_nodeWithStatement{ + object: cmpl.parseExpression(in.Object), + body: cmpl.parseStatement(in.Body), + } + + } + + panic(fmt.Errorf("Here be dragons: cmpl.parseStatement(%T)", in)) +} + +func cmpl_parse(in *ast.Program) *_nodeProgram { + cmpl := _compiler{ + program: in, + } + return cmpl.parse() +} + +func (cmpl *_compiler) _parse(in *ast.Program) *_nodeProgram { + out := &_nodeProgram{ + body: make([]_nodeStatement, len(in.Body)), + file: in.File, + } + for i, value := range in.Body { + out.body[i] = cmpl.parseStatement(value) + } + for _, value := range in.DeclarationList { + switch value := value.(type) { + case *ast.FunctionDeclaration: + out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral)) + case *ast.VariableDeclaration: + for _, value := range value.List { + out.varList = append(out.varList, value.Name) + } + default: + panic(fmt.Errorf("Here be dragons: cmpl.parseProgram.DeclarationList(%T)", value)) + } + } + return out +} + +type _nodeProgram struct { + body []_nodeStatement + + varList []string + functionList []*_nodeFunctionLiteral + + variableList []_nodeDeclaration + + file *file.File +} + +type _nodeDeclaration struct { + name string + definition _node +} + +type _node interface { +} + +type ( + _nodeExpression interface { + _node + _expressionNode() + } + + _nodeArrayLiteral struct { + value []_nodeExpression + } + + _nodeAssignExpression struct { + operator token.Token + left _nodeExpression + right _nodeExpression + } + + _nodeBinaryExpression struct { + operator token.Token + left _nodeExpression + right _nodeExpression + comparison bool + } + + _nodeBracketExpression struct { + idx file.Idx + left _nodeExpression + member _nodeExpression + } + + _nodeCallExpression struct { + callee _nodeExpression + argumentList []_nodeExpression + } + + _nodeConditionalExpression struct { + test _nodeExpression + consequent _nodeExpression + alternate _nodeExpression + } + + _nodeDotExpression struct { + idx file.Idx + left _nodeExpression + identifier string + } + + _nodeFunctionLiteral struct { + name string + body _nodeStatement + source string + parameterList []string + varList []string + functionList []*_nodeFunctionLiteral + file *file.File + } + + _nodeIdentifier struct { + idx file.Idx + name string + } + + _nodeLiteral struct { + value Value + } + + _nodeNewExpression struct { + callee _nodeExpression + argumentList []_nodeExpression + } + + _nodeObjectLiteral struct { + value []_nodeProperty + } + + _nodeProperty struct { + key string + kind string + value _nodeExpression + } + + _nodeRegExpLiteral struct { + flags string + pattern string // Value? + regexp *regexp.Regexp + } + + _nodeSequenceExpression struct { + sequence []_nodeExpression + } + + _nodeThisExpression struct { + } + + _nodeUnaryExpression struct { + operator token.Token + operand _nodeExpression + postfix bool + } + + _nodeVariableExpression struct { + idx file.Idx + name string + initializer _nodeExpression + } +) + +type ( + _nodeStatement interface { + _node + _statementNode() + } + + _nodeBlockStatement struct { + list []_nodeStatement + } + + _nodeBranchStatement struct { + branch token.Token + label string + } + + _nodeCaseStatement struct { + test _nodeExpression + consequent []_nodeStatement + } + + _nodeCatchStatement struct { + parameter string + body _nodeStatement + } + + _nodeDebuggerStatement struct { + } + + _nodeDoWhileStatement struct { + test _nodeExpression + body []_nodeStatement + } + + _nodeEmptyStatement struct { + } + + _nodeExpressionStatement struct { + expression _nodeExpression + } + + _nodeForInStatement struct { + into _nodeExpression + source _nodeExpression + body []_nodeStatement + } + + _nodeForStatement struct { + initializer _nodeExpression + update _nodeExpression + test _nodeExpression + body []_nodeStatement + } + + _nodeIfStatement struct { + test _nodeExpression + consequent _nodeStatement + alternate _nodeStatement + } + + _nodeLabelledStatement struct { + label string + statement _nodeStatement + } + + _nodeReturnStatement struct { + argument _nodeExpression + } + + _nodeSwitchStatement struct { + discriminant _nodeExpression + default_ int + body []*_nodeCaseStatement + } + + _nodeThrowStatement struct { + argument _nodeExpression + } + + _nodeTryStatement struct { + body _nodeStatement + catch *_nodeCatchStatement + finally _nodeStatement + } + + _nodeVariableStatement struct { + list []_nodeExpression + } + + _nodeWhileStatement struct { + test _nodeExpression + body []_nodeStatement + } + + _nodeWithStatement struct { + object _nodeExpression + body _nodeStatement + } +) + +// _expressionNode + +func (*_nodeArrayLiteral) _expressionNode() {} +func (*_nodeAssignExpression) _expressionNode() {} +func (*_nodeBinaryExpression) _expressionNode() {} +func (*_nodeBracketExpression) _expressionNode() {} +func (*_nodeCallExpression) _expressionNode() {} +func (*_nodeConditionalExpression) _expressionNode() {} +func (*_nodeDotExpression) _expressionNode() {} +func (*_nodeFunctionLiteral) _expressionNode() {} +func (*_nodeIdentifier) _expressionNode() {} +func (*_nodeLiteral) _expressionNode() {} +func (*_nodeNewExpression) _expressionNode() {} +func (*_nodeObjectLiteral) _expressionNode() {} +func (*_nodeRegExpLiteral) _expressionNode() {} +func (*_nodeSequenceExpression) _expressionNode() {} +func (*_nodeThisExpression) _expressionNode() {} +func (*_nodeUnaryExpression) _expressionNode() {} +func (*_nodeVariableExpression) _expressionNode() {} + +// _statementNode + +func (*_nodeBlockStatement) _statementNode() {} +func (*_nodeBranchStatement) _statementNode() {} +func (*_nodeCaseStatement) _statementNode() {} +func (*_nodeCatchStatement) _statementNode() {} +func (*_nodeDebuggerStatement) _statementNode() {} +func (*_nodeDoWhileStatement) _statementNode() {} +func (*_nodeEmptyStatement) _statementNode() {} +func (*_nodeExpressionStatement) _statementNode() {} +func (*_nodeForInStatement) _statementNode() {} +func (*_nodeForStatement) _statementNode() {} +func (*_nodeIfStatement) _statementNode() {} +func (*_nodeLabelledStatement) _statementNode() {} +func (*_nodeReturnStatement) _statementNode() {} +func (*_nodeSwitchStatement) _statementNode() {} +func (*_nodeThrowStatement) _statementNode() {} +func (*_nodeTryStatement) _statementNode() {} +func (*_nodeVariableStatement) _statementNode() {} +func (*_nodeWhileStatement) _statementNode() {} +func (*_nodeWithStatement) _statementNode() {} diff --git a/vendor/github.com/robertkrimen/otto/cmpl_test.go b/vendor/github.com/robertkrimen/otto/cmpl_test.go new file mode 100644 index 00000000..34b050f0 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/cmpl_test.go @@ -0,0 +1,54 @@ +package otto + +import ( + "testing" + + "github.com/robertkrimen/otto/parser" +) + +func Test_cmpl(t *testing.T) { + tt(t, func() { + vm := New() + + test := func(src string, expect ...interface{}) { + program, err := parser.ParseFile(nil, "", src, 0) + is(err, nil) + { + program := cmpl_parse(program) + value := vm.runtime.cmpl_evaluate_nodeProgram(program, false) + if len(expect) > 0 { + is(value, expect[0]) + } + } + } + + test(``, Value{}) + + test(`var abc = 1; abc;`, 1) + + test(`var abc = 1 + 1; abc;`, 2) + + test(`1 + 2;`, 3) + }) +} + +func TestParse_cmpl(t *testing.T) { + tt(t, func() { + + test := func(src string) { + program, err := parser.ParseFile(nil, "", src, 0) + is(err, nil) + is(cmpl_parse(program), "!=", nil) + } + + test(``) + + test(`var abc = 1; abc;`) + + test(` + function abc() { + return; + } + `) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/console.go b/vendor/github.com/robertkrimen/otto/console.go new file mode 100644 index 00000000..948face7 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/console.go @@ -0,0 +1,51 @@ +package otto + +import ( + "fmt" + "os" + "strings" +) + +func formatForConsole(argumentList []Value) string { + output := []string{} + for _, argument := range argumentList { + output = append(output, fmt.Sprintf("%v", argument)) + } + return strings.Join(output, " ") +} + +func builtinConsole_log(call FunctionCall) Value { + fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) + return Value{} +} + +func builtinConsole_error(call FunctionCall) Value { + fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) + return Value{} +} + +// Nothing happens. +func builtinConsole_dir(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_time(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_timeEnd(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_trace(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_assert(call FunctionCall) Value { + return Value{} +} + +func (runtime *_runtime) newConsole() *_object { + + return newConsoleObject(runtime) +} diff --git a/vendor/github.com/robertkrimen/otto/date_test.go b/vendor/github.com/robertkrimen/otto/date_test.go new file mode 100644 index 00000000..a9c71edd --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/date_test.go @@ -0,0 +1,481 @@ +package otto + +import ( + "math" + "testing" + "time" +) + +func mockTimeLocal(location *time.Location) func() { + local := time.Local + time.Local = location + return func() { + time.Local = local + } +} + +// Passing or failing should not be dependent on what time zone we're in +func mockUTC() func() { + return mockTimeLocal(time.UTC) +} + +func TestDate(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + time0 := time.Unix(1348616313, 47*1000*1000).Local() + + test(`Date`, "function Date() { [native code] }") + test(`new Date(0).toUTCString()`, "Thu, 01 Jan 1970 00:00:00 UTC") + test(`new Date(0).toGMTString()`, "Thu, 01 Jan 1970 00:00:00 GMT") + if false { + // TODO toLocale{Date,Time}String + test(`new Date(0).toLocaleString()`, "") + test(`new Date(0).toLocaleDateString()`, "") + test(`new Date(0).toLocaleTimeString()`, "") + } + test(`new Date(1348616313).getTime()`, 1348616313) + test(`new Date(1348616313).toUTCString()`, "Fri, 16 Jan 1970 14:36:56 UTC") + test(`abc = new Date(1348616313047); abc.toUTCString()`, "Tue, 25 Sep 2012 23:38:33 UTC") + test(`abc.getYear()`, time0.Year()-1900) + test(`abc.getFullYear()`, time0.Year()) + test(`abc.getUTCFullYear()`, 2012) + test(`abc.getMonth()`, int(time0.Month())-1) // Remember, the JavaScript month is 0-based + test(`abc.getUTCMonth()`, 8) + test(`abc.getDate()`, time0.Day()) + test(`abc.getUTCDate()`, 25) + test(`abc.getDay()`, int(time0.Weekday())) + test(`abc.getUTCDay()`, 2) + test(`abc.getHours()`, time0.Hour()) + test(`abc.getUTCHours()`, 23) + test(`abc.getMinutes()`, time0.Minute()) + test(`abc.getUTCMinutes()`, 38) + test(`abc.getSeconds()`, time0.Second()) + test(`abc.getUTCSeconds()`, 33) + test(`abc.getMilliseconds()`, time0.Nanosecond()/(1000*1000)) // In honor of the 47% + test(`abc.getUTCMilliseconds()`, 47) + _, offset := time0.Zone() + test(`abc.getTimezoneOffset()`, offset/-60) + + test(`new Date("Xyzzy").getTime()`, math.NaN()) + + test(`abc.setFullYear(2011); abc.toUTCString()`, "Sun, 25 Sep 2011 23:38:33 UTC") + test(`new Date(12564504e5).toUTCString()`, "Sun, 25 Oct 2009 06:00:00 UTC") + test(`new Date(2009, 9, 25).toUTCString()`, "Sun, 25 Oct 2009 00:00:00 UTC") + test(`+(new Date(2009, 9, 25))`, 1256428800000) + + format := "Mon, 2 Jan 2006 15:04:05 MST" + + time1 := time.Unix(1256450400, 0) + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), 2001*1000*1000, time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMilliseconds(2001); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), 61, time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setSeconds("61"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), time1.Hour(), 61, time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMinutes("61"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), time1.Day(), 5, time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setHours("5"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), time1.Month(), 26, time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setDate("26"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), 10, time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMonth(9); abc.toUTCString()`, time0.Format(format)) + test(`abc = new Date(12564504e5); abc.setMonth("09"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(time1.Year(), 11, time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setMonth("10"); abc.toUTCString()`, time0.Format(format)) + + time0 = time.Date(2010, time1.Month(), time1.Day(), time1.Hour(), time1.Minute(), time1.Second(), time1.Nanosecond(), time1.Location()).UTC() + test(`abc = new Date(12564504e5); abc.setFullYear(2010); abc.toUTCString()`, time0.Format(format)) + + test(`new Date("2001-01-01T10:01:02.000").getTime()`, 978343262000) + + // Date() + test(`typeof Date()`, "string") + test(`typeof Date(2006, 1, 2)`, "string") + + test(` + abc = Object.getOwnPropertyDescriptor(Date, "parse"); + [ abc.value === Date.parse, abc.writable, abc.enumerable, abc.configurable ]; + `, "true,true,false,true") + + test(` + abc = Object.getOwnPropertyDescriptor(Date.prototype, "toTimeString"); + [ abc.value === Date.prototype.toTimeString, abc.writable, abc.enumerable, abc.configurable ]; + `, "true,true,false,true") + + test(` + var abc = Object.getOwnPropertyDescriptor(Date, "prototype"); + [ [ typeof Date.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestDate_parse(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`Date.parse("2001-01-01T10:01:02.000")`, 978343262000) + + test(`Date.parse("2006-01-02T15:04:05.000")`, 1136214245000) + + test(`Date.parse("2006")`, 1136073600000) + + test(`Date.parse("1970-01-16T14:36:56+00:00")`, 1348616000) + + test(`Date.parse("1970-01-16T14:36:56.313+00:00")`, 1348616313) + + test(`Date.parse("1970-01-16T14:36:56.000")`, 1348616000) + + test(`Date.parse.length`, 1) + }) +} + +func TestDate_UTC(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`Date.UTC(2009, 9, 25)`, 1256428800000) + + test(`Date.UTC.length`, 7) + }) +} + +func TestDate_now(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + // FIXME I think this too risky + test(`+(""+Date.now()).substr(0, 10)`, float64(epochToInteger(timeToEpoch(time.Now()))/1000)) + + test(`Date.now() - Date.now(1,2,3) < 24 * 60 * 60`, true) + }) +} + +func TestDate_toISOString(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`new Date(0).toISOString()`, "1970-01-01T00:00:00.000Z") + }) +} + +func TestDate_toJSON(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`new Date(0).toJSON()`, "1970-01-01T00:00:00.000Z") + }) +} + +func TestDate_setYear(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(`new Date(12564504e5).setYear(96)`, 846223200000) + + test(`new Date(12564504e5).setYear(1996)`, 846223200000) + + test(`new Date(12564504e5).setYear(2000)`, 972453600000) + }) +} + +func TestDateDefaultValue(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var date = new Date(); + date + 0 === date.toString() + "0"; + `, true) + }) +} + +func TestDate_April1978(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var abc = new Date(1978,3); + [ abc.getYear(), abc.getMonth(), abc.valueOf() ]; + `, "78,3,260236800000") + }) +} + +func TestDate_setMilliseconds(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(); + def = abc.setMilliseconds(); + [ abc, def ]; + `, "Invalid Date,NaN") + }) +} + +func TestDate_new(t *testing.T) { + // FIXME? + // This is probably incorrect, due to differences in Go date/time handling + // versus ECMA date/time handling, but we'll leave this here for + // future reference + + if true { + return + } + + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + [ + new Date(1899, 11).valueOf(), + new Date(1899, 12).valueOf(), + new Date(1900, 0).valueOf() + ] + `, "-2211638400000,-2208960000000,-2208960000000") + }) +} + +func TestDateComparison(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var now0 = Date.now(); + var now1 = (new Date()).toString(); + [ now0 === now1, Math.abs(now0 - Date.parse(now1)) <= 1000 ]; + `, "false,true") + }) +} + +func TestDate_setSeconds(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setSeconds(10, 12); + + def.setSeconds(10); + def.setMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCSeconds(10, 12); + + def.setUTCSeconds(10); + def.setUTCMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setSeconds.length`, 2) + test(`Date.prototype.setUTCSeconds.length`, 2) + }) +} + +func TestDate_setMinutes(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setMinutes(8, 10, 12); + + def.setMinutes(8); + def.setSeconds(10); + def.setMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCMinutes(8, 10, 12); + + def.setUTCMinutes(8); + def.setUTCSeconds(10); + def.setUTCMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setMinutes.length`, 3) + test(`Date.prototype.setUTCMinutes.length`, 3) + }) +} + +func TestDate_setHours(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setHours(6, 8, 10, 12); + + def.setHours(6); + def.setMinutes(8); + def.setSeconds(10); + def.setMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCHours(6, 8, 10, 12); + + def.setUTCHours(6); + def.setUTCMinutes(8); + def.setUTCSeconds(10); + def.setUTCMilliseconds(12); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setHours.length`, 4) + test(`Date.prototype.setUTCHours.length`, 4) + }) +} + +func TestDate_setMonth(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setMonth(6, 8); + + def.setMonth(6); + def.setDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCMonth(6, 8); + + def.setUTCMonth(6); + def.setUTCDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setMonth.length`, 2) + test(`Date.prototype.setUTCMonth.length`, 2) + }) +} + +func TestDate_setFullYear(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setFullYear(1981, 6, 8); + + def.setFullYear(1981); + def.setMonth(6); + def.setDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(` + abc = new Date(1980, 10); + def = new Date(abc); + + abc.setUTCFullYear(1981, 6, 8); + + def.setUTCFullYear(1981); + def.setUTCMonth(6); + def.setUTCDate(8); + + abc.valueOf() === def.valueOf(); + `, true) + + test(`Date.prototype.setFullYear.length`, 3) + test(`Date.prototype.setUTCFullYear.length`, 3) + }) +} + +func TestDate_setTime(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + var abc = new Date(1999, 6, 1); + var def = new Date(); + def.setTime(abc.getTime()); + [ def, abc.valueOf() == def.valueOf() ]; + `, "Thu, 01 Jul 1999 00:00:00 UTC,true") + + test(`Date.prototype.setTime.length`, 1) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/dbg.go b/vendor/github.com/robertkrimen/otto/dbg.go new file mode 100644 index 00000000..51fbdc20 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/dbg.go @@ -0,0 +1,9 @@ +// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg + +package otto + +import ( + Dbg "github.com/robertkrimen/otto/dbg" +) + +var dbg, dbgf = Dbg.New() diff --git a/vendor/github.com/robertkrimen/otto/dbg/dbg.go b/vendor/github.com/robertkrimen/otto/dbg/dbg.go new file mode 100644 index 00000000..8c27fa29 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/dbg/dbg.go @@ -0,0 +1,387 @@ +// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg + +/* +Package dbg is a println/printf/log-debugging utility library. + + import ( + Dbg "github.com/robertkrimen/dbg" + ) + + dbg, dbgf := Dbg.New() + + dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi) + # "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793" + + dbgf("With a %s formatting %.2f", "little", math.Pi) + # "2013/01/28 16:51:55 With a little formatting (3.14)" + + dbgf("%/fatal//A fatal debug statement: should not be here") + # "A fatal debug statement: should not be here" + # ...and then, os.Exit(1) + + dbgf("%/panic//Can also panic %s", "this") + # "Can also panic this" + # ...as a panic, equivalent to: panic("Can also panic this") + + dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()") + # "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()" + + dbgf("%d %d", 1, 2, 3, 4, 5) + # "2013/01/28 17:16:32 Another example: 1 2 3 4 5" + + dbgf("%@: Include the function name for a little context (via %s)", "%@") + # "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)" + +By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output. +However, you can also provide your own output destination by invoking dbg.New with +a customization function: + + import ( + "bytes" + Dbg "github.com/robertkrimen/dbg" + "os" + ) + + # dbg to os.Stderr + dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { + dbgr.SetOutput(os.Stderr) + }) + + # A slightly contrived example: + var buffer bytes.Buffer + dbg, dbgf := New(func(dbgr *Dbgr) { + dbgr.SetOutput(&buffer) + }) + +*/ +package dbg + +import ( + "bytes" + "fmt" + "io" + "log" + "os" + "regexp" + "runtime" + "strings" + "unicode" +) + +type _frmt struct { + ctl string + format string + operandCount int + panic bool + fatal bool + check bool +} + +var ( + ctlTest = regexp.MustCompile(`^\s*%/`) + ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`) +) + +func operandCount(format string) int { + count := 0 + end := len(format) + for at := 0; at < end; { + for at < end && format[at] != '%' { + at++ + } + at++ + if at < end { + if format[at] != '%' && format[at] != '@' { + count++ + } + at++ + } + } + return count +} + +func parseFormat(format string) (frmt _frmt) { + if ctlTest.MatchString(format) { + format = strings.TrimLeftFunc(format, unicode.IsSpace) + index := strings.Index(format, "//") + if index != -1 { + frmt.ctl = format[0:index] + format = format[index+2:] // Skip the second slash via +2 (instead of +1) + } else { + frmt.ctl = format + format = "" + } + for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) { + for _, value := range tmp[1:] { + switch value { + case "panic": + frmt.panic = true + case "fatal": + frmt.fatal = true + case "check": + frmt.check = true + } + } + } + } + frmt.format = format + frmt.operandCount = operandCount(format) + return +} + +type Dbgr struct { + emit _emit +} + +type DbgFunction func(values ...interface{}) + +func NewDbgr() *Dbgr { + self := &Dbgr{} + return self +} + +/* +New will create and return a pair of debugging functions. You can customize where +they output to by passing in an (optional) customization function: + + import ( + Dbg "github.com/robertkrimen/dbg" + "os" + ) + + # dbg to os.Stderr + dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { + dbgr.SetOutput(os.Stderr) + }) + +*/ +func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) { + dbgr := NewDbgr() + if len(options) > 0 { + if fn, ok := options[0].(func(*Dbgr)); ok { + fn(dbgr) + } + } + return dbgr.DbgDbgf() +} + +func (self Dbgr) Dbg(values ...interface{}) { + self.getEmit().emit(_frmt{}, "", values...) +} + +func (self Dbgr) Dbgf(values ...interface{}) { + self.dbgf(values...) +} + +func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) { + dbg = func(vl ...interface{}) { + self.Dbg(vl...) + } + dbgf = func(vl ...interface{}) { + self.dbgf(vl...) + } + return dbg, dbgf // Redundant, but... +} + +func (self Dbgr) dbgf(values ...interface{}) { + + var frmt _frmt + if len(values) > 0 { + tmp := fmt.Sprint(values[0]) + frmt = parseFormat(tmp) + values = values[1:] + } + + buffer_f := bytes.Buffer{} + format := frmt.format + end := len(format) + for at := 0; at < end; { + last := at + for at < end && format[at] != '%' { + at++ + } + if at > last { + buffer_f.WriteString(format[last:at]) + } + if at >= end { + break + } + // format[at] == '%' + at++ + // format[at] == ? + if format[at] == '@' { + depth := 2 + pc, _, _, _ := runtime.Caller(depth) + name := runtime.FuncForPC(pc).Name() + buffer_f.WriteString(name) + } else { + buffer_f.WriteString(format[at-1 : at+1]) + } + at++ + } + + //values_f := append([]interface{}{}, values[0:frmt.operandCount]...) + values_f := values[0:frmt.operandCount] + values_dbg := values[frmt.operandCount:] + if len(values_dbg) > 0 { + // Adjust frmt.format: + // (%v instead of %s because: frmt.check) + { + tmp := format + if len(tmp) > 0 { + if unicode.IsSpace(rune(tmp[len(tmp)-1])) { + buffer_f.WriteString("%v") + } else { + buffer_f.WriteString(" %v") + } + } else if frmt.check { + // Performing a check, so no output + } else { + buffer_f.WriteString("%v") + } + } + + // Adjust values_f: + if !frmt.check { + tmp := []string{} + for _, value := range values_dbg { + tmp = append(tmp, fmt.Sprintf("%v", value)) + } + // First, make a copy of values_f, so we avoid overwriting values_dbg when appending + values_f = append([]interface{}{}, values_f...) + values_f = append(values_f, strings.Join(tmp, " ")) + } + } + + format = buffer_f.String() + if frmt.check { + // We do not actually emit to the log, but panic if + // a non-nil value is detected (e.g. a non-nil error) + for _, value := range values_dbg { + if value != nil { + if format == "" { + panic(value) + } else { + panic(fmt.Sprintf(format, append(values_f, value)...)) + } + } + } + } else { + self.getEmit().emit(frmt, format, values_f...) + } +} + +// Idiot-proof &Dbgr{}, etc. +func (self *Dbgr) getEmit() _emit { + if self.emit == nil { + self.emit = standardEmit() + } + return self.emit +} + +// SetOutput will accept the following as a destination for output: +// +// *log.Logger Print*/Panic*/Fatal* of the logger +// io.Writer - +// nil Reset to the default output (os.Stderr) +// "log" Print*/Panic*/Fatal* via the "log" package +// +func (self *Dbgr) SetOutput(output interface{}) { + if output == nil { + self.emit = standardEmit() + return + } + switch output := output.(type) { + case *log.Logger: + self.emit = _emitLogger{ + logger: output, + } + return + case io.Writer: + self.emit = _emitWriter{ + writer: output, + } + return + case string: + if output == "log" { + self.emit = _emitLog{} + return + } + } + panic(output) +} + +// ======== // +// = emit = // +// ======== // + +func standardEmit() _emit { + return _emitWriter{ + writer: os.Stderr, + } +} + +func ln(tmp string) string { + length := len(tmp) + if length > 0 && tmp[length-1] != '\n' { + return tmp + "\n" + } + return tmp +} + +type _emit interface { + emit(_frmt, string, ...interface{}) +} + +type _emitWriter struct { + writer io.Writer +} + +func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) { + if format == "" { + fmt.Fprintln(self.writer, values...) + } else { + if frmt.panic { + panic(fmt.Sprintf(format, values...)) + } + fmt.Fprintf(self.writer, ln(format), values...) + if frmt.fatal { + os.Exit(1) + } + } +} + +type _emitLogger struct { + logger *log.Logger +} + +func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) { + if format == "" { + self.logger.Println(values...) + } else { + if frmt.panic { + self.logger.Panicf(format, values...) + } else if frmt.fatal { + self.logger.Fatalf(format, values...) + } else { + self.logger.Printf(format, values...) + } + } +} + +type _emitLog struct { +} + +func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) { + if format == "" { + log.Println(values...) + } else { + if frmt.panic { + log.Panicf(format, values...) + } else if frmt.fatal { + log.Fatalf(format, values...) + } else { + log.Printf(format, values...) + } + } +} diff --git a/vendor/github.com/robertkrimen/otto/documentation_test.go b/vendor/github.com/robertkrimen/otto/documentation_test.go new file mode 100644 index 00000000..04646117 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/documentation_test.go @@ -0,0 +1,95 @@ +package otto + +import ( + "fmt" +) + +func ExampleSynopsis() { + + vm := New() + vm.Run(` + abc = 2 + 2; + console.log("The value of abc is " + abc); // 4 + `) + + value, _ := vm.Get("abc") + { + value, _ := value.ToInteger() + fmt.Println(value) + } + + vm.Set("def", 11) + vm.Run(` + console.log("The value of def is " + def); + `) + + vm.Set("xyzzy", "Nothing happens.") + vm.Run(` + console.log(xyzzy.length); + `) + + value, _ = vm.Run("xyzzy.length") + { + value, _ := value.ToInteger() + fmt.Println(value) + } + + value, err := vm.Run("abcdefghijlmnopqrstuvwxyz.length") + fmt.Println(value) + fmt.Println(err) + + vm.Set("sayHello", func(call FunctionCall) Value { + fmt.Printf("Hello, %s.\n", call.Argument(0).String()) + return UndefinedValue() + }) + + vm.Set("twoPlus", func(call FunctionCall) Value { + right, _ := call.Argument(0).ToInteger() + result, _ := vm.ToValue(2 + right) + return result + }) + + value, _ = vm.Run(` + sayHello("Xyzzy"); + sayHello(); + + result = twoPlus(2.0); + `) + fmt.Println(value) + + // Output: + // The value of abc is 4 + // 4 + // The value of def is 11 + // 16 + // 16 + // undefined + // ReferenceError: 'abcdefghijlmnopqrstuvwxyz' is not defined + // Hello, Xyzzy. + // Hello, undefined. + // 4 +} + +func ExampleConsole() { + + vm := New() + console := map[string]interface{}{ + "log": func(call FunctionCall) Value { + fmt.Println("console.log:", formatForConsole(call.ArgumentList)) + return UndefinedValue() + }, + } + + err := vm.Set("console", console) + + value, err := vm.Run(` + console.log("Hello, World."); + `) + fmt.Println(value) + fmt.Println(err) + + // Output: + // console.log: Hello, World. + // undefined + // +} diff --git a/vendor/github.com/robertkrimen/otto/error.go b/vendor/github.com/robertkrimen/otto/error.go new file mode 100644 index 00000000..c8b5af44 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/error.go @@ -0,0 +1,253 @@ +package otto + +import ( + "errors" + "fmt" + + "github.com/robertkrimen/otto/file" +) + +type _exception struct { + value interface{} +} + +func newException(value interface{}) *_exception { + return &_exception{ + value: value, + } +} + +func (self *_exception) eject() interface{} { + value := self.value + self.value = nil // Prevent Go from holding on to the value, whatever it is + return value +} + +type _error struct { + name string + message string + trace []_frame + + offset int +} + +func (err _error) format() string { + if len(err.name) == 0 { + return err.message + } + if len(err.message) == 0 { + return err.name + } + return fmt.Sprintf("%s: %s", err.name, err.message) +} + +func (err _error) formatWithStack() string { + str := err.format() + "\n" + for _, frame := range err.trace { + str += " at " + frame.location() + "\n" + } + return str +} + +type _frame struct { + native bool + nativeFile string + nativeLine int + file *file.File + offset int + callee string + fn interface{} +} + +var ( + nativeFrame = _frame{} +) + +type _at int + +func (fr _frame) location() string { + str := "" + + switch { + case fr.native: + str = "" + if fr.nativeFile != "" && fr.nativeLine != 0 { + str = fmt.Sprintf("%s:%d", fr.nativeFile, fr.nativeLine) + } + case fr.file != nil: + if p := fr.file.Position(file.Idx(fr.offset)); p != nil { + path, line, column := p.Filename, p.Line, p.Column + + if path == "" { + path = "" + } + + str = fmt.Sprintf("%s:%d:%d", path, line, column) + } + } + + if fr.callee != "" { + str = fmt.Sprintf("%s (%s)", fr.callee, str) + } + + return str +} + +// An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc. +type Error struct { + _error +} + +// Error returns a description of the error +// +// TypeError: 'def' is not a function +// +func (err Error) Error() string { + return err.format() +} + +// String returns a description of the error and a trace of where the +// error occurred. +// +// TypeError: 'def' is not a function +// at xyz (:3:9) +// at :7:1/ +// +func (err Error) String() string { + return err.formatWithStack() +} + +func (err _error) describe(format string, in ...interface{}) string { + return fmt.Sprintf(format, in...) +} + +func (self _error) messageValue() Value { + if self.message == "" { + return Value{} + } + return toValue_string(self.message) +} + +func (rt *_runtime) typeErrorResult(throw bool) bool { + if throw { + panic(rt.panicTypeError()) + } + return false +} + +func newError(rt *_runtime, name string, stackFramesToPop int, in ...interface{}) _error { + err := _error{ + name: name, + offset: -1, + } + description := "" + length := len(in) + + if rt != nil && rt.scope != nil { + scope := rt.scope + + for i := 0; i < stackFramesToPop; i++ { + if scope.outer != nil { + scope = scope.outer + } + } + + frame := scope.frame + + if length > 0 { + if at, ok := in[length-1].(_at); ok { + in = in[0 : length-1] + if scope != nil { + frame.offset = int(at) + } + length-- + } + if length > 0 { + description, in = in[0].(string), in[1:] + } + } + + limit := rt.traceLimit + + err.trace = append(err.trace, frame) + if scope != nil { + for scope = scope.outer; scope != nil; scope = scope.outer { + if limit--; limit == 0 { + break + } + + if scope.frame.offset >= 0 { + err.trace = append(err.trace, scope.frame) + } + } + } + } else { + if length > 0 { + description, in = in[0].(string), in[1:] + } + } + err.message = err.describe(description, in...) + + return err +} + +func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "TypeError", 0, argumentList...), + } +} + +func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "ReferenceError", 0, argumentList...), + } +} + +func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "URIError", 0, argumentList...), + } +} + +func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "SyntaxError", 0, argumentList...), + } +} + +func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "RangeError", 0, argumentList...), + } +} + +func catchPanic(function func()) (err error) { + defer func() { + if caught := recover(); caught != nil { + if exception, ok := caught.(*_exception); ok { + caught = exception.eject() + } + switch caught := caught.(type) { + case *Error: + err = caught + return + case _error: + err = &Error{caught} + return + case Value: + if vl := caught._object(); vl != nil { + switch vl := vl.value.(type) { + case _error: + err = &Error{vl} + return + } + } + err = errors.New(caught.string()) + return + } + panic(caught) + } + }() + function() + return nil +} diff --git a/vendor/github.com/robertkrimen/otto/error_native_test.go b/vendor/github.com/robertkrimen/otto/error_native_test.go new file mode 100644 index 00000000..f6579ca0 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/error_native_test.go @@ -0,0 +1,39 @@ +package otto + +import ( + "testing" +) + +// this is its own file because the tests in it rely on the line numbers of +// some of the functions defined here. putting it in with the rest of the +// tests would probably be annoying. + +func TestErrorContextNative(t *testing.T) { + tt(t, func() { + vm := New() + + vm.Set("N", func(c FunctionCall) Value { + v, err := c.Argument(0).Call(NullValue()) + if err != nil { + panic(err) + } + return v + }) + + s, _ := vm.Compile("test.js", ` + function F() { throw new Error('wow'); } + function G() { return N(F); } + `) + + vm.Run(s) + + f1, _ := vm.Get("G") + _, err := f1.Call(NullValue()) + err1 := err.(*Error) + is(err1.message, "wow") + is(len(err1.trace), 3) + is(err1.trace[0].location(), "F (test.js:2:29)") + is(err1.trace[1].location(), "github.com/robertkrimen/otto.TestErrorContextNative.func1.1 (error_native_test.go:15)") + is(err1.trace[2].location(), "G (test.js:3:26)") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/error_test.go b/vendor/github.com/robertkrimen/otto/error_test.go new file mode 100644 index 00000000..d44fedf1 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/error_test.go @@ -0,0 +1,479 @@ +package otto + +import ( + "testing" +) + +func TestError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ Error.prototype.name, Error.prototype.message, Error.prototype.hasOwnProperty("message") ]; + `, "Error,,true") + }) +} + +func TestError_instanceof(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`(new TypeError()) instanceof Error`, true) + }) +} + +func TestPanicValue(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("abc", func(call FunctionCall) Value { + value, err := call.Otto.Run(`({ def: 3.14159 })`) + is(err, nil) + panic(value) + }) + + test(` + try { + abc(); + } + catch (err) { + error = err; + } + [ error instanceof Error, error.message, error.def ]; + `, "false,,3.14159") + }) +} + +func Test_catchPanic(t *testing.T) { + tt(t, func() { + vm := New() + + _, err := vm.Run(` + A syntax error that + does not define + var; + abc; + `) + is(err, "!=", nil) + + _, err = vm.Call(`abc.def`, nil) + is(err, "!=", nil) + }) +} + +func TestErrorContext(t *testing.T) { + tt(t, func() { + vm := New() + + _, err := vm.Run(` + undefined(); + `) + { + err := err.(*Error) + is(err.message, "'undefined' is not a function") + is(len(err.trace), 1) + is(err.trace[0].location(), ":2:13") + } + + _, err = vm.Run(` + ({}).abc(); + `) + { + err := err.(*Error) + is(err.message, "'abc' is not a function") + is(len(err.trace), 1) + is(err.trace[0].location(), ":2:14") + } + + _, err = vm.Run(` + ("abc").abc(); + `) + { + err := err.(*Error) + is(err.message, "'abc' is not a function") + is(len(err.trace), 1) + is(err.trace[0].location(), ":2:14") + } + + _, err = vm.Run(` + var ghi = "ghi"; + ghi(); + `) + { + err := err.(*Error) + is(err.message, "'ghi' is not a function") + is(len(err.trace), 1) + is(err.trace[0].location(), ":3:13") + } + + _, err = vm.Run(` + function def() { + undefined(); + } + function abc() { + def(); + } + abc(); + `) + { + err := err.(*Error) + is(err.message, "'undefined' is not a function") + is(len(err.trace), 3) + is(err.trace[0].location(), "def (:3:17)") + is(err.trace[1].location(), "abc (:6:17)") + is(err.trace[2].location(), ":8:13") + } + + _, err = vm.Run(` + function abc() { + xyz(); + } + abc(); + `) + { + err := err.(*Error) + is(err.message, "'xyz' is not defined") + is(len(err.trace), 2) + is(err.trace[0].location(), "abc (:3:17)") + is(err.trace[1].location(), ":5:13") + } + + _, err = vm.Run(` + mno + 1; + `) + { + err := err.(*Error) + is(err.message, "'mno' is not defined") + is(len(err.trace), 1) + is(err.trace[0].location(), ":2:13") + } + + _, err = vm.Run(` + eval("xyz();"); + `) + { + err := err.(*Error) + is(err.message, "'xyz' is not defined") + is(len(err.trace), 1) + is(err.trace[0].location(), ":1:1") + } + + _, err = vm.Run(` + xyzzy = "Nothing happens." + eval("xyzzy();"); + `) + { + err := err.(*Error) + is(err.message, "'xyzzy' is not a function") + is(len(err.trace), 1) + is(err.trace[0].location(), ":1:1") + } + + _, err = vm.Run(` + throw Error("xyzzy"); + `) + { + err := err.(*Error) + is(err.message, "xyzzy") + is(len(err.trace), 1) + is(err.trace[0].location(), ":2:19") + } + + _, err = vm.Run(` + throw new Error("xyzzy"); + `) + { + err := err.(*Error) + is(err.message, "xyzzy") + is(len(err.trace), 1) + is(err.trace[0].location(), ":2:23") + } + + script1, err := vm.Compile("file1.js", + `function A() { + throw new Error("test"); + } + + function C() { + var o = null; + o.prop = 1; + } + `) + is(err, nil) + + _, err = vm.Run(script1) + is(err, nil) + + script2, err := vm.Compile("file2.js", + `function B() { + A() + } + `) + is(err, nil) + + _, err = vm.Run(script2) + is(err, nil) + + script3, err := vm.Compile("file3.js", "B()") + is(err, nil) + + _, err = vm.Run(script3) + { + err := err.(*Error) + is(err.message, "test") + is(len(err.trace), 3) + is(err.trace[0].location(), "A (file1.js:2:15)") + is(err.trace[1].location(), "B (file2.js:2:5)") + is(err.trace[2].location(), "file3.js:1:1") + } + + { + f, _ := vm.Get("B") + _, err := f.Call(UndefinedValue()) + err1 := err.(*Error) + is(err1.message, "test") + is(len(err1.trace), 2) + is(err1.trace[0].location(), "A (file1.js:2:15)") + is(err1.trace[1].location(), "B (file2.js:2:5)") + } + + { + f, _ := vm.Get("C") + _, err := f.Call(UndefinedValue()) + err1 := err.(*Error) + is(err1.message, "Cannot access member 'prop' of null") + is(len(err1.trace), 1) + is(err1.trace[0].location(), "C (file1.js:7:5)") + } + + }) +} + +func TestMakeCustomErrorReturn(t *testing.T) { + tt(t, func() { + vm := New() + + vm.Set("A", func(c FunctionCall) Value { + return vm.MakeCustomError("CarrotError", "carrots is life, carrots is love") + }) + + s, _ := vm.Compile("test.js", ` + function B() { return A(); } + function C() { return B(); } + function D() { return C(); } + `) + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + v, err := vm.Call("D", nil) + if err != nil { + panic(err) + } + + is(v.Class(), "Error") + + name, err := v.Object().Get("name") + if err != nil { + panic(err) + } + is(name.String(), "CarrotError") + + message, err := v.Object().Get("message") + if err != nil { + panic(err) + } + is(message.String(), "carrots is life, carrots is love") + + str, err := v.Object().Call("toString") + if err != nil { + panic(err) + } + is(str, "CarrotError: carrots is life, carrots is love") + + i, err := v.Export() + if err != nil { + panic(err) + } + t.Logf("%#v\n", i) + }) +} + +func TestMakeCustomError(t *testing.T) { + tt(t, func() { + vm := New() + + vm.Set("A", func(c FunctionCall) Value { + panic(vm.MakeCustomError("CarrotError", "carrots is life, carrots is love")) + + return UndefinedValue() + }) + + s, _ := vm.Compile("test.js", ` + function B() { A(); } + function C() { B(); } + function D() { C(); } + `) + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err := vm.Call("D", nil) + if err == nil { + panic("error should not be nil") + } + + is(err.Error(), "CarrotError: carrots is life, carrots is love") + + er := err.(*Error) + + is(er.name, "CarrotError") + is(er.message, "carrots is life, carrots is love") + }) +} + +func TestMakeCustomErrorFreshVM(t *testing.T) { + tt(t, func() { + vm := New() + e := vm.MakeCustomError("CarrotError", "carrots is life, carrots is love") + + str, err := e.ToString() + if err != nil { + panic(err) + } + + is(str, "CarrotError: carrots is life, carrots is love") + }) +} + +func TestMakeTypeError(t *testing.T) { + tt(t, func() { + vm := New() + + vm.Set("A", func(c FunctionCall) Value { + panic(vm.MakeTypeError("these aren't my glasses")) + + return UndefinedValue() + }) + + s, _ := vm.Compile("test.js", ` + function B() { A(); } + function C() { B(); } + function D() { C(); } + `) + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err := vm.Call("D", nil) + if err == nil { + panic("error should not be nil") + } + + is(err.Error(), "TypeError: these aren't my glasses") + + er := err.(*Error) + + is(er.name, "TypeError") + is(er.message, "these aren't my glasses") + }) +} + +func TestMakeRangeError(t *testing.T) { + tt(t, func() { + vm := New() + + vm.Set("A", func(c FunctionCall) Value { + panic(vm.MakeRangeError("too many")) + + return UndefinedValue() + }) + + s, _ := vm.Compile("test.js", ` + function B() { A(); } + function C() { B(); } + function D() { C(); } + `) + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err := vm.Call("D", nil) + if err == nil { + panic("error should not be nil") + } + + is(err.Error(), "RangeError: too many") + + er := err.(*Error) + + is(er.name, "RangeError") + is(er.message, "too many") + }) +} + +func TestMakeSyntaxError(t *testing.T) { + tt(t, func() { + vm := New() + + vm.Set("A", func(c FunctionCall) Value { + panic(vm.MakeSyntaxError("i think you meant \"you're\"")) + + return UndefinedValue() + }) + + s, _ := vm.Compile("test.js", ` + function B() { A(); } + function C() { B(); } + function D() { C(); } + `) + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err := vm.Call("D", nil) + if err == nil { + panic("error should not be nil") + } + + is(err.Error(), "SyntaxError: i think you meant \"you're\"") + + er := err.(*Error) + + is(er.name, "SyntaxError") + is(er.message, "i think you meant \"you're\"") + }) +} + +func TestErrorStackProperty(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.Compile("test.js", ` + function A() { throw new TypeError('uh oh'); } + function B() { return A(); } + function C() { return B(); } + + var s = null; + + try { C(); } catch (e) { s = e.stack; } + + s; + `) + if err != nil { + panic(err) + } + + v, err := vm.Run(s) + if err != nil { + panic(err) + } + + is(v.String(), "TypeError: uh oh\n at A (test.js:2:29)\n at B (test.js:3:26)\n at C (test.js:4:26)\n at test.js:8:10\n") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/evaluate.go b/vendor/github.com/robertkrimen/otto/evaluate.go new file mode 100644 index 00000000..093054cc --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/evaluate.go @@ -0,0 +1,318 @@ +package otto + +import ( + "fmt" + "math" + "strings" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) evaluateMultiply(left float64, right float64) Value { + // TODO 11.5.1 + return Value{} +} + +func (self *_runtime) evaluateDivide(left float64, right float64) Value { + if math.IsNaN(left) || math.IsNaN(right) { + return NaNValue() + } + if math.IsInf(left, 0) && math.IsInf(right, 0) { + return NaNValue() + } + if left == 0 && right == 0 { + return NaNValue() + } + if math.IsInf(left, 0) { + if math.Signbit(left) == math.Signbit(right) { + return positiveInfinityValue() + } else { + return negativeInfinityValue() + } + } + if math.IsInf(right, 0) { + if math.Signbit(left) == math.Signbit(right) { + return positiveZeroValue() + } else { + return negativeZeroValue() + } + } + if right == 0 { + if math.Signbit(left) == math.Signbit(right) { + return positiveInfinityValue() + } else { + return negativeInfinityValue() + } + } + return toValue_float64(left / right) +} + +func (self *_runtime) evaluateModulo(left float64, right float64) Value { + // TODO 11.5.3 + return Value{} +} + +func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value { + + leftValue := left.resolve() + + switch operator { + + // Additive + case token.PLUS: + leftValue = toPrimitive(leftValue) + rightValue := right.resolve() + rightValue = toPrimitive(rightValue) + + if leftValue.IsString() || rightValue.IsString() { + return toValue_string(strings.Join([]string{leftValue.string(), rightValue.string()}, "")) + } else { + return toValue_float64(leftValue.float64() + rightValue.float64()) + } + case token.MINUS: + rightValue := right.resolve() + return toValue_float64(leftValue.float64() - rightValue.float64()) + + // Multiplicative + case token.MULTIPLY: + rightValue := right.resolve() + return toValue_float64(leftValue.float64() * rightValue.float64()) + case token.SLASH: + rightValue := right.resolve() + return self.evaluateDivide(leftValue.float64(), rightValue.float64()) + case token.REMAINDER: + rightValue := right.resolve() + return toValue_float64(math.Mod(leftValue.float64(), rightValue.float64())) + + // Logical + case token.LOGICAL_AND: + left := leftValue.bool() + if !left { + return falseValue + } + return toValue_bool(right.resolve().bool()) + case token.LOGICAL_OR: + left := leftValue.bool() + if left { + return trueValue + } + return toValue_bool(right.resolve().bool()) + + // Bitwise + case token.AND: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) & toInt32(rightValue)) + case token.OR: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) | toInt32(rightValue)) + case token.EXCLUSIVE_OR: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) ^ toInt32(rightValue)) + + // Shift + // (Masking of 0x1f is to restrict the shift to a maximum of 31 places) + case token.SHIFT_LEFT: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f)) + case token.SHIFT_RIGHT: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f)) + case token.UNSIGNED_SHIFT_RIGHT: + rightValue := right.resolve() + // Shifting an unsigned integer is a logical shift + return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f)) + + case token.INSTANCEOF: + rightValue := right.resolve() + if !rightValue.IsObject() { + panic(self.panicTypeError("Expecting a function in instanceof check, but got: %v", rightValue)) + } + return toValue_bool(rightValue._object().hasInstance(leftValue)) + + case token.IN: + rightValue := right.resolve() + if !rightValue.IsObject() { + panic(self.panicTypeError()) + } + return toValue_bool(rightValue._object().hasProperty(leftValue.string())) + } + + panic(hereBeDragons(operator)) +} + +func valueKindDispatchKey(left _valueKind, right _valueKind) int { + return (int(left) << 2) + int(right) +} + +var equalDispatch map[int](func(Value, Value) bool) = makeEqualDispatch() + +func makeEqualDispatch() map[int](func(Value, Value) bool) { + key := valueKindDispatchKey + return map[int](func(Value, Value) bool){ + + key(valueNumber, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() }, + key(valueString, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() }, + key(valueObject, valueNumber): func(x Value, y Value) bool { return x.float64() == y.float64() }, + key(valueObject, valueString): func(x Value, y Value) bool { return x.float64() == y.float64() }, + } +} + +type _lessThanResult int + +const ( + lessThanFalse _lessThanResult = iota + lessThanTrue + lessThanUndefined +) + +func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult { + + x := Value{} + y := x + + if leftFirst { + x = toNumberPrimitive(left) + y = toNumberPrimitive(right) + } else { + y = toNumberPrimitive(right) + x = toNumberPrimitive(left) + } + + result := false + if x.kind != valueString || y.kind != valueString { + x, y := x.float64(), y.float64() + if math.IsNaN(x) || math.IsNaN(y) { + return lessThanUndefined + } + result = x < y + } else { + x, y := x.string(), y.string() + result = x < y + } + + if result { + return lessThanTrue + } + + return lessThanFalse +} + +// FIXME Probably a map is not the most efficient way to do this +var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){ + // < + map[_lessThanResult]bool{ + lessThanFalse: false, + lessThanTrue: true, + lessThanUndefined: false, + }, + + // > + map[_lessThanResult]bool{ + lessThanFalse: false, + lessThanTrue: true, + lessThanUndefined: false, + }, + + // <= + map[_lessThanResult]bool{ + lessThanFalse: true, + lessThanTrue: false, + lessThanUndefined: false, + }, + + // >= + map[_lessThanResult]bool{ + lessThanFalse: true, + lessThanTrue: false, + lessThanUndefined: false, + }, +} + +func (self *_runtime) calculateComparison(comparator token.Token, left Value, right Value) bool { + + // FIXME Use strictEqualityComparison? + // TODO This might be redundant now (with regards to evaluateComparison) + x := left.resolve() + y := right.resolve() + + kindEqualKind := false + result := true + negate := false + + switch comparator { + case token.LESS: + result = lessThanTable[0][calculateLessThan(x, y, true)] + case token.GREATER: + result = lessThanTable[1][calculateLessThan(y, x, false)] + case token.LESS_OR_EQUAL: + result = lessThanTable[2][calculateLessThan(y, x, false)] + case token.GREATER_OR_EQUAL: + result = lessThanTable[3][calculateLessThan(x, y, true)] + case token.STRICT_NOT_EQUAL: + negate = true + fallthrough + case token.STRICT_EQUAL: + if x.kind != y.kind { + result = false + } else { + kindEqualKind = true + } + case token.NOT_EQUAL: + negate = true + fallthrough + case token.EQUAL: + if x.kind == y.kind { + kindEqualKind = true + } else if x.kind <= valueNull && y.kind <= valueNull { + result = true + } else if x.kind <= valueNull || y.kind <= valueNull { + result = false + } else if x.kind <= valueString && y.kind <= valueString { + result = x.float64() == y.float64() + } else if x.kind == valueBoolean { + result = self.calculateComparison(token.EQUAL, toValue_float64(x.float64()), y) + } else if y.kind == valueBoolean { + result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.float64())) + } else if x.kind == valueObject { + result = self.calculateComparison(token.EQUAL, toPrimitive(x), y) + } else if y.kind == valueObject { + result = self.calculateComparison(token.EQUAL, x, toPrimitive(y)) + } else { + panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y)) + } + default: + panic(fmt.Errorf("Unknown comparator %s", comparator.String())) + } + + if kindEqualKind { + switch x.kind { + case valueUndefined, valueNull: + result = true + case valueNumber: + x := x.float64() + y := y.float64() + if math.IsNaN(x) || math.IsNaN(y) { + result = false + } else { + result = x == y + } + case valueString: + result = x.string() == y.string() + case valueBoolean: + result = x.bool() == y.bool() + case valueObject: + result = x._object() == y._object() + default: + goto ERROR + } + } + + if negate { + result = !result + } + + return result + +ERROR: + panic(hereBeDragons("%v (%v) %s %v (%v)", x, x.kind, comparator, y, y.kind)) +} diff --git a/vendor/github.com/robertkrimen/otto/file/README.markdown b/vendor/github.com/robertkrimen/otto/file/README.markdown new file mode 100644 index 00000000..79757baa --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/file/README.markdown @@ -0,0 +1,110 @@ +# file +-- + import "github.com/robertkrimen/otto/file" + +Package file encapsulates the file abstractions used by the ast & parser. + +## Usage + +#### type File + +```go +type File struct { +} +``` + + +#### func NewFile + +```go +func NewFile(filename, src string, base int) *File +``` + +#### func (*File) Base + +```go +func (fl *File) Base() int +``` + +#### func (*File) Name + +```go +func (fl *File) Name() string +``` + +#### func (*File) Source + +```go +func (fl *File) Source() string +``` + +#### type FileSet + +```go +type FileSet struct { +} +``` + +A FileSet represents a set of source files. + +#### func (*FileSet) AddFile + +```go +func (self *FileSet) AddFile(filename, src string) int +``` +AddFile adds a new file with the given filename and src. + +This an internal method, but exported for cross-package use. + +#### func (*FileSet) File + +```go +func (self *FileSet) File(idx Idx) *File +``` + +#### func (*FileSet) Position + +```go +func (self *FileSet) Position(idx Idx) *Position +``` +Position converts an Idx in the FileSet into a Position. + +#### type Idx + +```go +type Idx int +``` + +Idx is a compact encoding of a source position within a file set. It can be +converted into a Position for a more convenient, but much larger, +representation. + +#### type Position + +```go +type Position struct { + Filename string // The filename where the error occurred, if any + Offset int // The src offset + Line int // The line number, starting at 1 + Column int // The column number, starting at 1 (The character count) + +} +``` + +Position describes an arbitrary source position including the filename, line, +and column location. + +#### func (*Position) String + +```go +func (self *Position) String() string +``` +String returns a string in one of several forms: + + file:line:column A valid position with filename + line:column A valid position without filename + file An invalid position with filename + - An invalid position without filename + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/robertkrimen/otto/file/file.go b/vendor/github.com/robertkrimen/otto/file/file.go new file mode 100644 index 00000000..9093206e --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/file/file.go @@ -0,0 +1,164 @@ +// Package file encapsulates the file abstractions used by the ast & parser. +// +package file + +import ( + "fmt" + "strings" + + "gopkg.in/sourcemap.v1" +) + +// Idx is a compact encoding of a source position within a file set. +// It can be converted into a Position for a more convenient, but much +// larger, representation. +type Idx int + +// Position describes an arbitrary source position +// including the filename, line, and column location. +type Position struct { + Filename string // The filename where the error occurred, if any + Offset int // The src offset + Line int // The line number, starting at 1 + Column int // The column number, starting at 1 (The character count) + +} + +// A Position is valid if the line number is > 0. + +func (self *Position) isValid() bool { + return self.Line > 0 +} + +// String returns a string in one of several forms: +// +// file:line:column A valid position with filename +// line:column A valid position without filename +// file An invalid position with filename +// - An invalid position without filename +// +func (self *Position) String() string { + str := self.Filename + if self.isValid() { + if str != "" { + str += ":" + } + str += fmt.Sprintf("%d:%d", self.Line, self.Column) + } + if str == "" { + str = "-" + } + return str +} + +// FileSet + +// A FileSet represents a set of source files. +type FileSet struct { + files []*File + last *File +} + +// AddFile adds a new file with the given filename and src. +// +// This an internal method, but exported for cross-package use. +func (self *FileSet) AddFile(filename, src string) int { + base := self.nextBase() + file := &File{ + name: filename, + src: src, + base: base, + } + self.files = append(self.files, file) + self.last = file + return base +} + +func (self *FileSet) nextBase() int { + if self.last == nil { + return 1 + } + return self.last.base + len(self.last.src) + 1 +} + +func (self *FileSet) File(idx Idx) *File { + for _, file := range self.files { + if idx <= Idx(file.base+len(file.src)) { + return file + } + } + return nil +} + +// Position converts an Idx in the FileSet into a Position. +func (self *FileSet) Position(idx Idx) *Position { + for _, file := range self.files { + if idx <= Idx(file.base+len(file.src)) { + return file.Position(idx - Idx(file.base)) + } + } + + return nil +} + +type File struct { + name string + src string + base int // This will always be 1 or greater + sm *sourcemap.Consumer +} + +func NewFile(filename, src string, base int) *File { + return &File{ + name: filename, + src: src, + base: base, + } +} + +func (fl *File) WithSourceMap(sm *sourcemap.Consumer) *File { + fl.sm = sm + return fl +} + +func (fl *File) Name() string { + return fl.name +} + +func (fl *File) Source() string { + return fl.src +} + +func (fl *File) Base() int { + return fl.base +} + +func (fl *File) Position(idx Idx) *Position { + position := &Position{} + + offset := int(idx) - fl.base + + if offset >= len(fl.src) || offset < 0 { + return nil + } + + src := fl.src[:offset] + + position.Filename = fl.name + position.Offset = offset + position.Line = strings.Count(src, "\n") + 1 + + if index := strings.LastIndex(src, "\n"); index >= 0 { + position.Column = offset - index + } else { + position.Column = len(src) + 1 + } + + if fl.sm != nil { + if f, _, l, c, ok := fl.sm.Source(position.Line, position.Column); ok { + position.Filename, position.Line, position.Column = f, l, c + } + } + + return position +} diff --git a/vendor/github.com/robertkrimen/otto/function_stack_test.go b/vendor/github.com/robertkrimen/otto/function_stack_test.go new file mode 100644 index 00000000..83302df1 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/function_stack_test.go @@ -0,0 +1,55 @@ +package otto + +import ( + "testing" +) + +// this is its own file because the tests in it rely on the line numbers of +// some of the functions defined here. putting it in with the rest of the +// tests would probably be annoying. + +func TestFunction_stack(t *testing.T) { + tt(t, func() { + vm := New() + + s, _ := vm.Compile("fake.js", `function X(fn1, fn2, fn3) { fn1(fn2, fn3); }`) + vm.Run(s) + + expected := []_frame{ + {native: true, nativeFile: "function_stack_test.go", nativeLine: 30, offset: 0, callee: "github.com/robertkrimen/otto.TestFunction_stack.func1.2"}, + {native: true, nativeFile: "function_stack_test.go", nativeLine: 25, offset: 0, callee: "github.com/robertkrimen/otto.TestFunction_stack.func1.1"}, + {native: false, nativeFile: "", nativeLine: 0, offset: 29, callee: "X", file: s.program.file}, + {native: false, nativeFile: "", nativeLine: 0, offset: 29, callee: "X", file: s.program.file}, + } + + vm.Set("A", func(c FunctionCall) Value { + c.Argument(0).Call(UndefinedValue()) + return UndefinedValue() + }) + + vm.Set("B", func(c FunctionCall) Value { + depth := 0 + for scope := c.Otto.runtime.scope; scope != nil; scope = scope.outer { + // these properties are tested explicitly so that we don't test `.fn`, + // which will differ from run to run + is(scope.frame.native, expected[depth].native) + is(scope.frame.nativeFile, expected[depth].nativeFile) + is(scope.frame.nativeLine, expected[depth].nativeLine) + is(scope.frame.offset, expected[depth].offset) + is(scope.frame.callee, expected[depth].callee) + is(scope.frame.file, expected[depth].file) + depth++ + } + + is(depth, 4) + + return UndefinedValue() + }) + + x, _ := vm.Get("X") + a, _ := vm.Get("A") + b, _ := vm.Get("B") + + x.Call(UndefinedValue(), x, a, b) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/function_test.go b/vendor/github.com/robertkrimen/otto/function_test.go new file mode 100644 index 00000000..d823127c --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/function_test.go @@ -0,0 +1,345 @@ +package otto + +import ( + "testing" +) + +func TestFunction(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = Object.getOwnPropertyDescriptor(Function, "prototype"); + [ [ typeof Function.prototype, typeof Function.prototype.length, Function.prototype.length ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "function,number,0,false,false,false") + }) +} + +func Test_argumentList2parameterList(t *testing.T) { + tt(t, func() { + is(argumentList2parameterList([]Value{toValue("abc, def"), toValue("ghi")}), []string{"abc", "def", "ghi"}) + }) +} + +func TestFunction_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + new Function({}); + `, "SyntaxError: Unexpected identifier") + + test(` + var abc = Function("def, ghi", "jkl", "return def+ghi+jkl"); + [ typeof abc, abc instanceof Function, abc("ab", "ba", 1) ]; + `, "function,true,abba1") + + test(`raise: + var abc = { + toString: function() { throw 1; } + }; + var def = { + toString: function() { throw 2; } + }; + var ghi = new Function(abc, def); + ghi; + `, "1") + + // S15.3.2.1_A3_T10 + test(`raise: + var abc = { + toString: function() { return "z;x"; } + }; + var def = "return this"; + var ghi = new Function(abc, def); + ghi; + `, "SyntaxError: Unexpected token ;") + + test(`raise: + var abc; + var def = "return true"; + var ghi = new Function(null, def); + ghi; + `, "SyntaxError: Unexpected token null") + }) +} + +func TestFunction_apply(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Function.prototype.apply.length`, 2) + test(`String.prototype.substring.apply("abc", [1, 11])`, "bc") + }) +} + +func TestFunction_call(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Function.prototype.call.length`, 1) + test(`String.prototype.substring.call("abc", 1, 11)`, "bc") + }) +} + +func TestFunctionArguments(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Should not be able to delete arguments + test(` + function abc(def, arguments){ + delete def; + return def; + } + abc(1); + `, 1) + + // Again, should not be able to delete arguments + test(` + function abc(def){ + delete def; + return def; + } + abc(1); + `, 1) + + // Test typeof of a function argument + test(` + function abc(def, ghi, jkl){ + return typeof jkl + } + abc("1st", "2nd", "3rd", "4th", "5th"); + `, "string") + + test(` + function abc(def, ghi, jkl){ + arguments[0] = 3.14; + arguments[1] = 'Nothing happens'; + arguments[2] = 42; + if (3.14 === def && 'Nothing happens' === ghi && 42 === jkl) + return true; + } + abc(-1, 4.2, 314); + `, true) + }) +} + +func TestFunctionDeclarationInFunction(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Function declarations happen AFTER parameter/argument declarations + // That is, a function declared within a function will shadow/overwrite + // declared parameters + + test(` + function abc(def){ + return def; + function def(){ + return 1; + } + } + typeof abc(); + `, "function") + }) +} + +func TestArguments_defineOwnProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc; + var def = true; + var ghi = {}; + (function (a, b, c) { + Object.defineProperty(arguments, "0", { + value: 42, + writable: false, + enumerable: false, + configurable: false + }); + Object.defineProperty(arguments, "1", { + value: 3.14, + configurable: true, + enumerable: true + }); + abc = Object.getOwnPropertyDescriptor(arguments, "0"); + for (var name in arguments) { + ghi[name] = (ghi[name] || 0) + 1; + if (name === "0") { + def = false; + } + } + }(0, 1, 2)); + [ abc.value, abc.writable, abc.enumerable, abc.configurable, def, ghi["1"] ]; + `, "42,false,false,false,true,1") + }) +} + +func TestFunction_bind(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + abc = function(){ + return "abc"; + }; + def = abc.bind(); + [ typeof def.prototype, typeof def.hasOwnProperty, def.hasOwnProperty("caller"), def.hasOwnProperty("arguments"), def() ]; + `, "object,function,true,true,abc") + + test(` + abc = function(){ + return arguments[1]; + }; + def = abc.bind(undefined, "abc"); + ghi = abc.bind(undefined, "abc", "ghi"); + [ def(), def("def"), ghi("def") ]; + `, ",def,ghi") + + test(` + var abc = function () {}; + var ghi; + try { + Object.defineProperty(Function.prototype, "xyzzy", { + value: 1001, + writable: true, + enumerable: true, + configurable: true + }); + var def = abc.bind({}); + ghi = !def.hasOwnProperty("xyzzy") && ghi.xyzzy === 1001; + } finally { + delete Function.prototype.xyzzy; + } + [ ghi ]; + `, "true") + + test(` + var abc = function (def, ghi) {}; + var jkl = abc.bind({}); + var mno = abc.bind({}, 1, 2); + [ jkl.length, mno.length ]; + `, "2,0") + + test(`raise: + Math.bind(); + `, "TypeError: 'bind' is not a function") + + test(` + function construct(fn, arguments) { + var bound = Function.prototype.bind.apply(fn, [null].concat(arguments)); + return new bound(); + } + var abc = construct(Date, [1957, 4, 27]); + Object.prototype.toString.call(abc); + `, "[object Date]") + + test(` + var fn = function (x, y, z) { + var result = {}; + result.abc = x + y + z; + result.def = arguments[0] === "a" && arguments.length === 3; + return result; + }; + var newFn = Function.prototype.bind.call(fn, {}, "a", "b", "c"); + var result = new newFn(); + [ result.hasOwnProperty("abc"), result.hasOwnProperty("def"), result.abc, result.def ]; + `, "true,true,abc,true") + + test(` + abc = function(){ + return "abc"; + }; + def = abc.bind(); + def.toString(); + `, "function () { [native code] }") + }) +} + +func TestFunction_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Function.prototype.toString.call(undefined); + `, "TypeError") + + test(` + abc = function() { return -1 ; +} + 1; + abc.toString(); + `, "function() { return -1 ;\n}") + }) +} + +func TestFunction_length(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`function a(x, y) {}; a.length`, 2) + }) +} + +func TestFunction_name(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`function a() {}; a.name`, "a") + + test(`function a() {}; var b = a.bind(); b.name`, "bound a") + }) +} + +func TestFunction_caller(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("n", func(v Value) Value { + r, err := v.Call(UndefinedValue()) + if err != nil { + panic(err) + } + + return r + }) + + test(` + function a() { return a.caller; }; + a() + `, NullValue()) + + test(` + function a() { return a.caller === b; }; + function b() { return a(); } + b() + `, true) + + test(` + function a() { return a.caller === b && b.caller === c; } + function b() { return a(); } + function c() { return b(); } + c(); + `, true) + + test(` + function a() { return a.caller === b && b.caller === n && n.caller === c; } + function b() { return a(); } + function c() { return n(b); } + c() + `, true) + + test(` + function e() { return e.caller === g && f.caller === g && g.caller === f; } + function f(n) { return g(n - 1); } + function g(n) { return n > 0 ? f(n) : e(); } + f(2); + `, true) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/functional_benchmark_test.go b/vendor/github.com/robertkrimen/otto/functional_benchmark_test.go new file mode 100644 index 00000000..8b8847ce --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/functional_benchmark_test.go @@ -0,0 +1,733 @@ +package otto + +import ( + "testing" + "math/rand" + "fmt" + "strings" +) + +func TestGoSliceQuickSort(t *testing.T) { + testGoSliceSort("quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, t) +} + +func TestGoSliceHeapSort(t *testing.T) { + testGoSliceSort("heapSort(testSlice)", jsHeapSort, t) +} + +func TestJsArrayQuicksort(t *testing.T) { + testJsArraySort("quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, t) +} + +func TestJsArrayHeapSort(t *testing.T) { + testJsArraySort("heapSort(testSlice)", jsHeapSort, t) +} + +func TestJsArrayMergeSort(t *testing.T) { + testJsArraySort("testSlice = mergeSort(testSlice)", jsMergeSort, t) +} + +func TestCryptoAes(t *testing.T) { + tt(t, func() { + _, vm := test() + _, err := vm.Run(jsCryptoAES) + is(err, nil) + }) +} + +func BenchmarkGoSliceQuickSort100000000(b *testing.B) { + benchmarkGoSliceSort(100000000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, b) +} + +func BenchmarkGoSliceHeapSort100000000(b *testing.B) { + benchmarkGoSliceSort(100000000, "heapSort(testSlice);", jsHeapSort, b) +} + +func BenchmarkJsArrayQuickSort10000(b *testing.B) { + benchmarkJsArraySort(10000, "quickSort(testSlice, 0, testSlice.length-1);", jsQuickSort, b) +} + +func BenchmarkJsArrayMergeSort10000(b *testing.B) { + benchmarkJsArraySort(10000, "mergeSort(testSlice);", jsMergeSort, b) +} + +func BenchmarkJsArrayHeapSort10000(b *testing.B) { + benchmarkJsArraySort(10000, "heapSort(testSlice);", jsHeapSort, b) +} + +func BenchmarkCryptoAES(b *testing.B) { + + vm := New() + + // Make sure VM creation time is not counted in runtime test + b.ResetTimer() + + for i := 0; i < b.N; i++ { + vm.Run(jsCryptoAES) + } +} + +func testGoSliceSort(sortFuncCall string, sortCode string, t *testing.T) { + tt(t, func() { + test, vm := test() + + // inject quicksort code + _, err := vm.Run(sortCode) + is(err, nil) + + testSlice := []int{5, 3, 2, 4, 1} + vm.Set("testSlice", testSlice) + _, err = vm.Run(sortFuncCall) + is(err, nil) + + is(test(`testSlice[0]`).export(), 1) + is(test(`testSlice[1]`).export(), 2) + is(test(`testSlice[2]`).export(), 3) + is(test(`testSlice[3]`).export(), 4) + is(test(`testSlice[4]`).export(), 5) + + is(testSlice[0], 1) + is(testSlice[1], 2) + is(testSlice[2], 3) + is(testSlice[3], 4) + is(testSlice[4], 5) + }) +} + +func testJsArraySort(sortFuncCall string, sortCode string, t *testing.T) { + tt(t, func() { + test, vm := test() + + // inject quicksort code + vm.Run(sortCode) + + vm.Run("var testSlice = [5, 3, 2, 4, 1];") + _, err := vm.Run(sortFuncCall) + is(err, nil) + + is(test(`testSlice[0]`).export(), 1) + is(test(`testSlice[1]`).export(), 2) + is(test(`testSlice[2]`).export(), 3) + is(test(`testSlice[3]`).export(), 4) + is(test(`testSlice[4]`).export(), 5) + }) +} + +func benchmarkGoSliceSort(size int, sortFuncCall string, sortCode string, b *testing.B) { + // generate arbitrary slice of 'size' + testSlice := make([]int, size) + for i := 0; i < size; i++ { + testSlice[i] = rand.Int() + } + + vm := New() + + // inject the sorting code + vm.Run(sortCode) + + // Reset timer - everything until this point may have taken a long time + b.ResetTimer() + + for i := 0; i < b.N; i++ { + vm.Run(sortFuncCall) + } +} + +func benchmarkJsArraySort(size int, sortFuncCall string, sortCode string, b *testing.B) { + // generate arbitrary slice of 'size' + testSlice := make([]string, size) + for i, _ := range testSlice { + testSlice[i] = fmt.Sprintf("%d", rand.Int()) + } + + jsArrayString := "[" + strings.Join(testSlice, ",") + "]" + + vm := New() + + // inject the test array + vm.Run("testSlice = " + jsArrayString) + + // inject the sorting code + vm.Run(sortCode) + + // Reset timer - everything until this point may have taken a long time + b.ResetTimer() + + for i := 0; i < b.N; i++ { + vm.Run(sortFuncCall) + } +} + + + + +/**********************************************************************************************************************/ +// Appendix - all the Javascript algorithm code constants + +const jsQuickSort = ` +function quickSort(arr, left, right){ + var len = arr.length, + pivot, + partitionIndex; + + + if(left < right){ + pivot = right; + partitionIndex = partition(arr, pivot, left, right); + + // sort left and right + quickSort(arr, left, partitionIndex - 1); + quickSort(arr, partitionIndex + 1, right); + } + return arr; +} + +function partition(arr, pivot, left, right){ + var pivotValue = arr[pivot], + partitionIndex = left; + + for(var i = left; i < right; i++){ + if(arr[i] < pivotValue){ + swap(arr, i, partitionIndex); + partitionIndex++; + } + } + swap(arr, right, partitionIndex); + return partitionIndex; +} + + + +function swap(arr, i, j){ + var temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; +} + + ` + + +const jsMergeSort = ` + +function mergeSort(arr){ + var len = arr.length; + if(len <2) + return arr; + var mid = Math.floor(len/2), + left = arr.slice(0,mid), + right =arr.slice(mid); + // send left and right to the mergeSort to broke it down into pieces + // then merge those + return merge(mergeSort(left),mergeSort(right)); +} + + +function merge(left, right){ + var result = [], + lLen = left.length, + rLen = right.length, + l = 0, + r = 0; + while(l < lLen && r < rLen){ + if(left[l] < right[r]){ + result.push(left[l++]); + } + else{ + result.push(right[r++]); + } + } + // remaining part needs to be addred to the result + return result.concat(left.slice(l)).concat(right.slice(r)); +} + +` + + +const jsHeapSort = ` + +function heapSort(arr){ + var len = arr.length, + end = len-1; + + heapify(arr, len); + + while(end > 0){ + swap(arr, end--, 0); + siftDown(arr, 0, end); + } + return arr; +} + + +function heapify(arr, len){ + // break the array into root + two sides, to create tree (heap) + var mid = Math.floor((len-2)/2); + while(mid >= 0){ + siftDown(arr, mid--, len-1); + } +} + +function siftDown(arr, start, end){ + var root = start, + child = root*2 + 1, + toSwap = root; + while(child <= end){ + if(arr[toSwap] < arr[child]){ + swap(arr, toSwap, child); + } + if(child+1 <= end && arr[toSwap] < arr[child+1]){ + swap(arr, toSwap, child+1) + } + if(toSwap != root){ + swap(arr, root, toSwap); + root = toSwap; + } + else{ + return; + } + toSwap = root; + child = root*2+1 + } +} + + +function swap(arr, i, j){ + var temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; +} + + +` + + +// Copied from JetStream benchmarking suite +// http://browserbench.org/JetStream/sources/crypto-aes.js +const jsCryptoAES = ` +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * AES Cipher function: encrypt 'input' with Rijndael algorithm + * + * takes byte-array 'input' (16 bytes) + * 2D byte-array key schedule 'w' (Nr+1 x Nb bytes) + * + * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage + * + * returns byte-array encrypted value (16 bytes) + */ +function Cipher(input, w) { // main Cipher function [§5.1] + var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) + var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys + + var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] + for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; + + state = AddRoundKey(state, w, 0, Nb); + + for (var round=1; round 6 && i%Nk == 4) { + temp = SubWord(temp); + } + for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; + } + + return w; +} + +function SubWord(w) { // apply SBox to 4-byte word w + for (var i=0; i<4; i++) w[i] = Sbox[w[i]]; + return w; +} + +function RotWord(w) { // rotate 4-byte word w left by one byte + w[4] = w[0]; + for (var i=0; i<4; i++) w[i] = w[i+1]; + return w; +} + + +// Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] +var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, + 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, + 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, + 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, + 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, + 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, + 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, + 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, + 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, + 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, + 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, + 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, + 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, + 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, + 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, + 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; + +// Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] +var Rcon = [ [0x00, 0x00, 0x00, 0x00], + [0x01, 0x00, 0x00, 0x00], + [0x02, 0x00, 0x00, 0x00], + [0x04, 0x00, 0x00, 0x00], + [0x08, 0x00, 0x00, 0x00], + [0x10, 0x00, 0x00, 0x00], + [0x20, 0x00, 0x00, 0x00], + [0x40, 0x00, 0x00, 0x00], + [0x80, 0x00, 0x00, 0x00], + [0x1b, 0x00, 0x00, 0x00], + [0x36, 0x00, 0x00, 0x00] ]; + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * Use AES to encrypt 'plaintext' with 'password' using 'nBits' key, in 'Counter' mode of operation + * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * for each block + * - outputblock = cipher(counter, key) + * - cipherblock = plaintext xor outputblock + */ +function AESEncryptCtr(plaintext, password, nBits) { + if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys + + // for this example script, generate the key by applying Cipher to 1st 16/24/32 chars of password; + // for real-world applications, a more secure approach would be to hash the password e.g. with SHA-1 + var nBytes = nBits/8; // no bytes in key + var pwBytes = new Array(nBytes); + for (var i=0; i>> i*8) & 0xff; + for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff; + + // generate key schedule - an expansion of the key into distinct Key Rounds for each round + var keySchedule = KeyExpansion(key); + + var blockCount = Math.ceil(plaintext.length/blockSize); + var ciphertext = new Array(blockCount); // ciphertext as array of strings + + for (var b=0; b>> c*8) & 0xff; + for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8) + + var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block -- + + // calculate length of final block: + var blockLength = b>> c*8) & 0xff; + for (var c=0; c<4; c++) counterBlock[15-c-4] = ((b/0x100000000-1) >>> c*8) & 0xff; + + var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block + + ciphertext[b] = unescCtrlChars(ciphertext[b]); + + var pt = ''; + for (var i=0; i>18 & 0x3f; + h2 = bits>>12 & 0x3f; + h3 = bits>>6 & 0x3f; + h4 = bits & 0x3f; + + // end of string? index to '=' in b64 + if (isNaN(o3)) h4 = 64; + if (isNaN(o2)) h3 = 64; + + // use hexets to index into b64, and append result to encoded string + enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); + } while (i < str.length); + + return enc; +} + +function decodeBase64(str) { + var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc=''; + + do { // unpack four hexets into three octets using index points in b64 + h1 = b64.indexOf(str.charAt(i++)); + h2 = b64.indexOf(str.charAt(i++)); + h3 = b64.indexOf(str.charAt(i++)); + h4 = b64.indexOf(str.charAt(i++)); + + bits = h1<<18 | h2<<12 | h3<<6 | h4; + + o1 = bits>>16 & 0xff; + o2 = bits>>8 & 0xff; + o3 = bits & 0xff; + + if (h3 == 64) enc += String.fromCharCode(o1); + else if (h4 == 64) enc += String.fromCharCode(o1, o2); + else enc += String.fromCharCode(o1, o2, o3); + } while (i < str.length); + + return decodeUTF8(enc); // decode UTF-8 byte-array back to Unicode +} + +function encodeUTF8(str) { // encode multi-byte string into utf-8 multiple single-byte characters + str = str.replace( + /[\u0080-\u07ff]/g, // U+0080 - U+07FF = 2-byte chars + function(c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } + ); + str = str.replace( + /[\u0800-\uffff]/g, // U+0800 - U+FFFF = 3-byte chars + function(c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } + ); + return str; +} + +function decodeUTF8(str) { // decode utf-8 encoded string back into multi-byte characters + str = str.replace( + /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars + function(c) { + var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; + return String.fromCharCode(cc); } + ); + str = str.replace( + /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars + function(c) { + var cc = (c.charCodeAt(0)&0x0f)<<12 | (c.charCodeAt(1)&0x3f<<6) | c.charCodeAt(2)&0x3f; + return String.fromCharCode(cc); } + ); + return str; +} + + +function byteArrayToHexStr(b) { // convert byte array to hex string for displaying test vectors + var s = ''; + for (var i=0; iprint(<<_END_); +package otto + +import ( + "math" +) + +func _newContext(runtime *_runtime) { +@{[ join "\n", $self->newContext() ]} +} + +func newConsoleObject(runtime *_runtime) *_object { +@{[ join "\n", $self->newConsoleObject() ]} +} +_END_ + +for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64/) { + $fmt->print(<<_END_); + +func toValue_$_(value $_) Value { + return Value{ + kind: valueNumber, + value: value, + } +} +_END_ +} + +$fmt->print(<<_END_); + +func toValue_string(value string) Value { + return Value{ + kind: valueString, + value: value, + } +} + +func toValue_string16(value []uint16) Value { + return Value{ + kind: valueString, + value: value, + } +} + +func toValue_bool(value bool) Value { + return Value{ + kind: valueBoolean, + value: value, + } +} + +func toValue_object(value *_object) Value { + return Value{ + kind: valueObject, + value: value, + } +} +_END_ + +close $fmt; + +sub newConsoleObject { + my $self = shift; + + return + $self->block(sub { + my $class = "Console"; + my @got = $self->functionDeclare( + $class, + "log", 0, + "debug:log", 0, + "info:log", 0, + "error", 0, + "warn:error", 0, + "dir", 0, + "time", 0, + "timeEnd", 0, + "trace", 0, + "assert", 0, + ); + return + "return @{[ $self->newObject(@got) ]}" + }), + ; +} + +sub newContext { + my $self = shift; + return + # ObjectPrototype + $self->block(sub { + my $class = "Object"; + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + undef, + "prototypeValueObject", + ), + }), + + # FunctionPrototype + $self->block(sub { + my $class = "Function"; + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueFunction", + ), + }), + + # ObjectPrototype + $self->block(sub { + my $class = "Object"; + my @got = $self->functionDeclare( + $class, + "valueOf", 0, + "toString", 0, + "toLocaleString", 0, + "hasOwnProperty", 1, + "isPrototypeOf", 1, + "propertyIsEnumerable", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->property("constructor", undef), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + ".${class}Prototype.property =", @propertyMap, + ".${class}Prototype.propertyOrder =", $propertyOrder, + }), + + # FunctionPrototype + $self->block(sub { + my $class = "Function"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "apply", 2, + "call", 1, + "bind", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->property("constructor", undef), + $self->property("length", $self->numberValue(0), "0"), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + ".${class}Prototype.property =", @propertyMap, + ".${class}Prototype.propertyOrder =", $propertyOrder, + }), + + # Object + $self->block(sub { + my $class = "Object"; + return + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "getPrototypeOf", 1, + "getOwnPropertyDescriptor", 2, + "defineProperty", 3, + "defineProperties", 2, + "create", 2, + "isExtensible", 1, + "preventExtensions", 1, + "isSealed", 1, + "seal", 1, + "isFrozen", 1, + "freeze", 1, + "keys", 1, + "getOwnPropertyNames", 1, + ), + ), + }), + + # Function + $self->block(sub { + my $class = "Function"; + return + "Function :=", + $self->globalFunction( + $class, + 1, + ), + ".$class = Function", + }), + + # Array + $self->block(sub { + my $class = "Array"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "toLocaleString", 0, + "concat", 1, + "join", 1, + "splice", 2, + "shift", 0, + "pop", 0, + "push", 1, + "slice", 2, + "unshift", 1, + "reverse", 0, + "sort", 1, + "indexOf", 1, + "lastIndexOf", 1, + "every", 1, + "some", 1, + "forEach", 1, + "map", 1, + "filter", 1, + "reduce", 1, + "reduceRight", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classArray", + ".ObjectPrototype", + undef, + $self->property("length", $self->numberValue("uint32(0)"), "0100"), + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "isArray", 1, + ), + ), + }), + + # String + $self->block(sub { + my $class = "String"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + "charAt", 1, + "charCodeAt", 1, + "concat", 1, + "indexOf", 1, + "lastIndexOf", 1, + "match", 1, + "replace", 2, + "search", 1, + "split", 2, + "slice", 2, + "substring", 2, + "toLowerCase", 0, + "toUpperCase", 0, + "substr", 2, + "trim", 0, + "trimLeft", 0, + "trimRight", 0, + "localeCompare", 1, + "toLocaleLowerCase", 0, + "toLocaleUpperCase", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classString", + ".ObjectPrototype", + "prototypeValueString", + $self->property("length", $self->numberValue("int(0)"), "0"), + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "fromCharCode", 1, + ), + ), + }), + + # Boolean + $self->block(sub { + my $class = "Boolean"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueBoolean", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }), + + # Number + $self->block(sub { + my $class = "Number"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + "toFixed", 1, + "toExponential", 1, + "toPrecision", 1, + "toLocaleString", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueNumber", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + $self->numberConstantDeclare( + "MAX_VALUE", "math.MaxFloat64", + "MIN_VALUE", "math.SmallestNonzeroFloat64", + "NaN", "math.NaN()", + "NEGATIVE_INFINITY", "math.Inf(-1)", + "POSITIVE_INFINITY", "math.Inf(+1)", + ), + ), + }), + + # Math + $self->block(sub { + my $class = "Math"; + return + ".$class =", + $self->globalObject( + $class, + $self->functionDeclare( + $class, + "abs", 1, + "acos", 1, + "asin", 1, + "atan", 1, + "atan2", 1, + "ceil", 1, + "cos", 1, + "exp", 1, + "floor", 1, + "log", 1, + "max", 2, + "min", 2, + "pow", 2, + "random", 0, + "round", 1, + "sin", 1, + "sqrt", 1, + "tan", 1, + ), + $self->numberConstantDeclare( + "E", "math.E", + "LN10", "math.Ln10", + "LN2", "math.Ln2", + "LOG2E", "math.Log2E", + "LOG10E", "math.Log10E", + "PI", "math.Pi", + "SQRT1_2", "sqrt1_2", + "SQRT2", "math.Sqrt2", + ) + ), + }), + + # Date + $self->block(sub { + my $class = "Date"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "toDateString", 0, + "toTimeString", 0, + "toUTCString", 0, + "toISOString", 0, + "toJSON", 1, + "toGMTString", 0, + "toLocaleString", 0, + "toLocaleDateString", 0, + "toLocaleTimeString", 0, + "valueOf", 0, + "getTime", 0, + "getYear", 0, + "getFullYear", 0, + "getUTCFullYear", 0, + "getMonth", 0, + "getUTCMonth", 0, + "getDate", 0, + "getUTCDate", 0, + "getDay", 0, + "getUTCDay", 0, + "getHours", 0, + "getUTCHours", 0, + "getMinutes", 0, + "getUTCMinutes", 0, + "getSeconds", 0, + "getUTCSeconds", 0, + "getMilliseconds", 0, + "getUTCMilliseconds", 0, + "getTimezoneOffset", 0, + "setTime", 1, + "setMilliseconds", 1, + "setUTCMilliseconds", 1, + "setSeconds", 2, + "setUTCSeconds", 2, + "setMinutes", 3, + "setUTCMinutes", 3, + "setHours", 4, + "setUTCHours", 4, + "setDate", 1, + "setUTCDate", 1, + "setMonth", 2, + "setUTCMonth", 2, + "setYear", 1, + "setFullYear", 3, + "setUTCFullYear", 3, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueDate", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 7, + $self->functionDeclare( + $class, + "parse", 1, + "UTC", 7, + "now", 0, + ), + ), + }), + + # RegExp + $self->block(sub { + my $class = "RegExp"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "exec", 1, + "test", 1, + "compile", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueRegExp", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 2, + $self->functionDeclare( + $class, + ), + ), + }), + + # Error + $self->block(sub { + my $class = "Error"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + undef, + @got, + $self->property("name", $self->stringValue("Error")), + $self->property("message", $self->stringValue("")), + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }), + + (map { + my $class = "${_}Error"; + $self->block(sub { + my @got = $self->functionDeclare( + $class, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ErrorPrototype", + undef, + @got, + $self->property("name", $self->stringValue($class)), + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }); + } qw/Eval Type Range Reference Syntax URI/), + + # JSON + $self->block(sub { + my $class = "JSON"; + return + ".$class =", + $self->globalObject( + $class, + $self->functionDeclare( + $class, + "parse", 2, + "stringify", 3, + ), + ), + }), + + # Global + $self->block(sub { + my $class = "Global"; + my @got = $self->functionDeclare( + $class, + "eval", 1, + "parseInt", 2, + "parseFloat", 1, + "isNaN", 1, + "isFinite", 1, + "decodeURI", 1, + "decodeURIComponent", 1, + "encodeURI", 1, + "encodeURIComponent", 1, + "escape", 1, + "unescape", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->globalDeclare( + "Object", + "Function", + "Array", + "String", + "Boolean", + "Number", + "Math", + "Date", + "RegExp", + "Error", + "EvalError", + "TypeError", + "RangeError", + "ReferenceError", + "SyntaxError", + "URIError", + "JSON", + ), + $self->property("undefined", $self->undefinedValue(), "0"), + $self->property("NaN", $self->numberValue("math.NaN()"), "0"), + $self->property("Infinity", $self->numberValue("math.Inf(+1)"), "0"), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + "runtime.globalObject.property =", + @propertyMap, + "runtime.globalObject.propertyOrder =", + $propertyOrder, + ; + }), + ; +} + +sub propertyMap { + my $self = shift; + return "map[string]_property{", (join ",\n", @_, ""), "}", +} + +our (@preblock, @postblock); +sub block { + my $self = shift; + local @preblock = (); + local @postblock = (); + my @input = $_[0]->(); + my @output; + while (@input) { + local $_ = shift @input; + if (m/^\./) { + $_ = "runtime.global$_"; + } + if (m/ :?=$/) { + $_ .= shift @input; + } + push @output, $_; + } + return + "{", + @preblock, + @output, + @postblock, + "}", + ; +} + +sub numberConstantDeclare { + my $self = shift; + my @got; + while (@_) { + my $name = shift; + my $value = shift; + push @got, $self->property($name, $self->numberValue($value), "0"), + } + return @got; +} + +sub functionDeclare { + my $self = shift; + my $class = shift; + my $builtin = "builtin${class}"; + my @got; + while (@_) { + my $name = shift; + my $length = shift; + $name = $self->newFunction($name, "${builtin}_", $length); + push @got, $self->functionProperty($name), + } + return @got; +} + +sub globalDeclare { + my $self = shift; + my @got; + while (@_) { + my $name = shift; + push @got, $self->property($name, $self->objectValue("runtime.global.$name"), "0101"), + } + return @got; +} + +sub propertyOrder { + my $self = shift; + my $propertyMap = join "", @_; + + my (@keys) = $propertyMap =~ m/("\w+"):/g; + my $propertyOrder = + join "\n", "propertyOrder: []string{", (join ",\n", @keys, ""), "}"; + return $propertyOrder; +} + +sub globalObject { + my $self = shift; + my $name = shift; + + my $propertyMap = ""; + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$name", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + $propertyMap +} +_END_ +} + +sub globalFunction { + my $self = shift; + my $name = shift; + my $length = shift; + + my $builtin = "builtin${name}"; + my $builtinNew = "builtinNew${name}"; + my $prototype = "runtime.global.${name}Prototype"; + my $propertyMap = ""; + unshift @_, + $self->property("length", $self->numberValue($length), "0"), + $self->property("prototype", $self->objectValue($prototype), "0"), + ; + + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + push @postblock, $self->statement( + "$prototype.property[\"constructor\"] =", + $self->property(undef, $self->objectValue("runtime.global.${name}"), "0101"), + ); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: @{[ $self->nativeFunctionOf($name, $builtin, $builtinNew) ]}, + $propertyMap +} +_END_ +} + +sub nativeCallFunction { + my $self = shift; + my $name = shift; + my $func = shift; + return trim <<_END_; +_nativeCallFunction{ "$name", $func } +_END_ +} + +sub globalPrototype { + my $self = shift; + my $class = shift; + my $classObject = shift; + my $prototype = shift; + my $value = shift; + + if (!defined $prototype) { + $prototype = "nil"; + } + + if (!defined $value) { + $value = "nil"; + } + + if ($prototype =~ m/^\./) { + $prototype = "runtime.global$prototype"; + } + + my $propertyMap = ""; + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$class", + objectClass: $classObject, + prototype: $prototype, + extensible: true, + value: $value, + $propertyMap +} +_END_ +} + +sub newFunction { + my $self = shift; + my $name = shift; + my $func = shift; + my $length = shift; + + my @name = ($name, $name); + if ($name =~ m/^(\w+):(\w+)$/) { + @name = ($1, $2); + $name = $name[0]; + } + + if ($func =~ m/^builtin\w+_$/) { + $func = "$func$name[1]"; + } + + my $propertyOrder = ""; + my @propertyMap = ( + $self->property("length", $self->numberValue($length), "0"), + ); + + if (@propertyMap) { + $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder = "$propertyOrder,"; + } + + my $label = functionLabel($name); + push @preblock, $self->statement( + "$label := @{[ trim <<_END_ ]}", +&_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: @{[ join "\n", $self->propertyMap(@propertyMap) ]}, + $propertyOrder + value: @{[ $self->nativeFunctionOf($name, $func) ]}, +} +_END_ + ); + + return $name; +} + +sub newObject { + my $self = shift; + + my $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "Object", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + property: $propertyMap, + $propertyOrder, +} +_END_ +} + +sub newPrototypeObject { + my $self = shift; + my $class = shift; + my $objectClass = shift; + my $value = shift; + if (defined $value) { + $value = "value: $value,"; + } + + my $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$class", + objectClass: $objectClass, + prototype: runtime.global.ObjectPrototype, + extensible: true, + property: $propertyMap, + $propertyOrder, + $value +} +_END_ +} + +sub functionProperty { + my $self = shift; + my $name = shift; + + return $self->property( + $name, + $self->objectValue(functionLabel($name)) + ); +} + +sub statement { + my $self = shift; + return join "\n", @_; +} + +sub functionOf { + my $self = shift; + my $call = shift; + my $construct = shift; + if ($construct) { + $construct = "construct: $construct,"; + } else { + $construct = ""; + } + + return trim <<_END_ +_functionObject{ + call: $call, + $construct +} +_END_ +} + +sub nativeFunctionOf { + my $self = shift; + my $name = shift; + my $call = shift; + my $construct = shift; + if ($construct) { + $construct = "construct: $construct,"; + } else { + $construct = ""; + } + + return trim <<_END_ +_nativeFunctionObject{ + name: "$name", + call: $call, + $construct +} +_END_ +} + +sub nameProperty { + my $self = shift; + my $name = shift; + my $value = shift; + + return trim <<_END_; +"$name": _property{ + mode: 0101, + value: $value, +} +_END_ +} + +sub numberValue { + my $self = shift; + my $value = shift; + return trim <<_END_; +Value{ + kind: valueNumber, + value: $value, +} +_END_ +} + +sub property { + my $self = shift; + my $name = shift; + my $value = shift; + my $mode = shift; + $mode = "0101" unless defined $mode; + if (! defined $value) { + $value = "Value{}"; + } + if (defined $name) { + return trim <<_END_; +"$name": _property{ + mode: $mode, + value: $value, +} +_END_ + } else { + return trim <<_END_; +_property{ + mode: $mode, + value: $value, +} +_END_ + } + +} + +sub objectProperty { + my $self = shift; + my $name = shift; + my $value = shift; + + return trim <<_END_; +"$name": _property{ + mode: 0101, + value: @{[ $self->objectValue($value)]}, +} +_END_ +} + +sub objectValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + kind: valueObject, + value: $value, +} +_END_ +} + +sub stringValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + kind: valueString, + value: "$value", +} +_END_ +} + +sub booleanValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + kind: valueBoolean, + value: $value, +} +_END_ +} + +sub undefinedValue { + my $self = shift; + return trim <<_END_ +Value{ + kind: valueUndefined, +} +_END_ +} diff --git a/vendor/github.com/robertkrimen/otto/json_test.go b/vendor/github.com/robertkrimen/otto/json_test.go new file mode 100644 index 00000000..4dd2ed7b --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/json_test.go @@ -0,0 +1,183 @@ +package otto + +import ( + "testing" +) + +func BenchmarkJSON_parse(b *testing.B) { + vm := New() + for i := 0; i < b.N; i++ { + vm.Run(`JSON.parse("1")`) + vm.Run(`JSON.parse("[1,2,3]")`) + vm.Run(`JSON.parse('{"a":{"x":100,"y":110},"b":[10,20,30],"c":"zazazaza"}')`) + vm.Run(`JSON.parse("[1,2,3]", function(k, v) { return undefined })`) + } +} + +func TestJSON_parse(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + JSON.parse("1"); + `, 1) + + test(` + JSON.parse("null"); + `, "null") // TODO Can we make this nil? + + test(` + var abc = JSON.parse('"a\uFFFFbc"'); + [ abc[0], abc[2], abc[3], abc.length ]; + `, "a,b,c,4") + + test(` + JSON.parse("[1, 2, 3]"); + `, "1,2,3") + + test(` + JSON.parse('{ "abc": 1, "def":2 }').abc; + `, 1) + + test(` + JSON.parse('{ "abc": { "x": 100, "y": 110 }, "def": [ 10, 20 ,30 ], "ghi": "zazazaza" }').def; + `, "10,20,30") + + test(`raise: + JSON.parse("12\t\r\n 34"); + `, "SyntaxError: invalid character '3' after top-level value") + + test(` + JSON.parse("[1, 2, 3]", function() { return undefined }); + `, "undefined") + + test(`raise: + JSON.parse(""); + `, "SyntaxError: unexpected end of JSON input") + + test(`raise: + JSON.parse("[1, 2, 3"); + `, "SyntaxError: unexpected end of JSON input") + + test(`raise: + JSON.parse("[1, 2, ; abc=10"); + `, "SyntaxError: invalid character ';' looking for beginning of value") + + test(`raise: + JSON.parse("[1, 2, function(){}]"); + `, "SyntaxError: invalid character 'u' in literal false (expecting 'a')") + }) +} + +func TestJSON_stringify(t *testing.T) { + tt(t, func() { + test, _ := test() + + defer mockUTC()() + + test(` + JSON.stringify(function(){}); + `, "undefined") + + test(` + JSON.stringify(new Boolean(false)); + `, "false") + + test(` + JSON.stringify({a1: {b1: [1,2,3,4], b2: {c1: 1, c2: 2}}, a2: 'a2'}, null, -5); + `, `{"a1":{"b1":[1,2,3,4],"b2":{"c1":1,"c2":2}},"a2":"a2"}`) + + test(` + JSON.stringify(undefined); + `, "undefined") + + test(` + JSON.stringify(1); + `, "1") + + test(` + JSON.stringify("abc def"); + `, "\"abc def\"") + + test(` + JSON.stringify(3.14159); + `, "3.14159") + + test(` + JSON.stringify([]); + `, "[]") + + test(` + JSON.stringify([1, 2, 3]); + `, "[1,2,3]") + + test(` + JSON.stringify([true, false, null]); + `, "[true,false,null]") + + test(` + JSON.stringify({ + abc: { x: 100, y: 110 }, + def: [ 10, 20, 30 ], + ghi: "zazazaza" + }); + `, `{"abc":{"x":100,"y":110},"def":[10,20,30],"ghi":"zazazaza"}`) + + test(` + JSON.stringify([ + 'e', + {pluribus: 'unum'} + ], null, '\t'); + `, "[\n\t\"e\",\n\t{\n\t\t\"pluribus\": \"unum\"\n\t}\n]") + + test(` + JSON.stringify(new Date(0)); + `, `"1970-01-01T00:00:00.000Z"`) + + test(` + JSON.stringify([ new Date(0) ], function(key, value){ + return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value + }); + `, `["Date(Thu, 01 Jan 1970 00:00:00 UTC)"]`) + + test(` + JSON.stringify({ + abc: 1, + def: 2, + ghi: 3 + }, ['abc','def']); + `, `{"abc":1,"def":2}`) + + test(`raise: + var abc = { + def: null + }; + abc.def = abc; + JSON.stringify(abc) + `, "TypeError: Converting circular structure to JSON") + + test(`raise: + var abc= [ null ]; + abc[0] = abc; + JSON.stringify(abc); + `, "TypeError: Converting circular structure to JSON") + + test(`raise: + var abc = { + def: {} + }; + abc.def.ghi = abc; + JSON.stringify(abc) + `, "TypeError: Converting circular structure to JSON") + + test(` + var ghi = { "pi": 3.14159 }; + var abc = { + def: {} + }; + abc.ghi = ghi; + abc.def.ghi = ghi; + JSON.stringify(abc); + `, `{"def":{"ghi":{"pi":3.14159}},"ghi":{"pi":3.14159}}`) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/math_test.go b/vendor/github.com/robertkrimen/otto/math_test.go new file mode 100644 index 00000000..499998b1 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/math_test.go @@ -0,0 +1,303 @@ +package otto + +import ( + "math" + "testing" +) + +var _NaN = math.NaN() +var _Infinity = math.Inf(1) + +func TestMath_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.toString()`, "[object Math]") + }) +} + +func TestMath_abs(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.abs(NaN)`, _NaN) + test(`Math.abs(2)`, 2) + test(`Math.abs(-2)`, 2) + test(`Math.abs(-Infinity)`, _Infinity) + + test(`Math.acos(0.5)`, 1.0471975511965976) + + test(`Math.abs('-1')`, 1) + test(`Math.abs(-2)`, 2) + test(`Math.abs(null)`, 0) + test(`Math.abs("string")`, _NaN) + test(`Math.abs()`, _NaN) + }) +} + +func TestMath_acos(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.acos(NaN)`, _NaN) + test(`Math.acos(2)`, _NaN) + test(`Math.acos(-2)`, _NaN) + test(`1/Math.acos(1)`, _Infinity) + + test(`Math.acos(0.5)`, 1.0471975511965976) + }) +} + +func TestMath_asin(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.asin(NaN)`, _NaN) + test(`Math.asin(2)`, _NaN) + test(`Math.asin(-2)`, _NaN) + test(`1/Math.asin(0)`, _Infinity) + test(`1/Math.asin(-0)`, -_Infinity) + + test(`Math.asin(0.5)`, 0.5235987755982989) + }) +} + +func TestMath_atan(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.atan(NaN)`, _NaN) + test(`1/Math.atan(0)`, _Infinity) + test(`1/Math.atan(-0)`, -_Infinity) + test(`Math.atan(Infinity)`, 1.5707963267948966) + test(`Math.atan(-Infinity)`, -1.5707963267948966) + + // freebsd/386 1.03 => 0.4636476090008061 + // darwin 1.03 => 0.46364760900080604 + test(`Math.atan(0.5).toPrecision(10)`, "0.463647609") + }) +} + +func TestMath_atan2(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.atan2()`, _NaN) + test(`Math.atan2(NaN)`, _NaN) + test(`Math.atan2(0, NaN)`, _NaN) + + test(`Math.atan2(1, 0)`, 1.5707963267948966) + test(`Math.atan2(1, -0)`, 1.5707963267948966) + + test(`1/Math.atan2(0, 1)`, _Infinity) + test(`1/Math.atan2(0, 0)`, _Infinity) + test(`Math.atan2(0, -0)`, 3.141592653589793) + test(`Math.atan2(0, -1)`, 3.141592653589793) + + test(`1/Math.atan2(-0, 1)`, -_Infinity) + test(`1/Math.atan2(-0, 0)`, -_Infinity) + test(`Math.atan2(-0, -0)`, -3.141592653589793) + test(`Math.atan2(-0, -1)`, -3.141592653589793) + + test(`Math.atan2(-1, 0)`, -1.5707963267948966) + test(`Math.atan2(-1, -0)`, -1.5707963267948966) + + test(`1/Math.atan2(1, Infinity)`, _Infinity) + test(`Math.atan2(1, -Infinity)`, 3.141592653589793) + test(`1/Math.atan2(-1, Infinity)`, -_Infinity) + test(`Math.atan2(-1, -Infinity)`, -3.141592653589793) + + test(`Math.atan2(Infinity, 1)`, 1.5707963267948966) + test(`Math.atan2(-Infinity, 1)`, -1.5707963267948966) + + test(`Math.atan2(Infinity, Infinity)`, 0.7853981633974483) + test(`Math.atan2(Infinity, -Infinity)`, 2.356194490192345) + test(`Math.atan2(-Infinity, Infinity)`, -0.7853981633974483) + test(`Math.atan2(-Infinity, -Infinity)`, -2.356194490192345) + }) +} + +func TestMath_ceil(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.ceil(NaN)`, _NaN) + test(`Math.ceil(+0)`, 0) + test(`1/Math.ceil(-0)`, -_Infinity) + test(`Math.ceil(Infinity)`, _Infinity) + test(`Math.ceil(-Infinity)`, -_Infinity) + test(`1/Math.ceil(-0.5)`, -_Infinity) + + test(`Math.ceil(-11)`, -11) + test(`Math.ceil(-0.5)`, 0) + test(`Math.ceil(1.5)`, 2) + }) +} + +func TestMath_cos(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.cos(NaN)`, _NaN) + test(`Math.cos(+0)`, 1) + test(`Math.cos(-0)`, 1) + test(`Math.cos(Infinity)`, _NaN) + test(`Math.cos(-Infinity)`, _NaN) + + test(`Math.cos(0.5)`, 0.8775825618903728) + }) +} + +func TestMath_exp(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.exp(NaN)`, _NaN) + test(`Math.exp(+0)`, 1) + test(`Math.exp(-0)`, 1) + test(`Math.exp(Infinity)`, _Infinity) + test(`Math.exp(-Infinity)`, 0) + }) +} + +func TestMath_floor(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.floor(NaN)`, _NaN) + test(`Math.floor(+0)`, 0) + test(`1/Math.floor(-0)`, -_Infinity) + test(`Math.floor(Infinity)`, _Infinity) + test(`Math.floor(-Infinity)`, -_Infinity) + + test(`Math.floor(-11)`, -11) + test(`Math.floor(-0.5)`, -1) + test(`Math.floor(1.5)`, 1) + }) +} + +func TestMath_log(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.log(NaN)`, _NaN) + test(`Math.log(-1)`, _NaN) + test(`Math.log(+0)`, -_Infinity) + test(`Math.log(-0)`, -_Infinity) + test(`1/Math.log(1)`, _Infinity) + test(`Math.log(Infinity)`, _Infinity) + + test(`Math.log(0.5)`, -0.6931471805599453) + }) +} + +func TestMath_max(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.max(-11, -1, 0, 1, 2, 3, 11)`, 11) + }) +} + +func TestMath_min(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.min(-11, -1, 0, 1, 2, 3, 11)`, -11) + }) +} + +func TestMath_pow(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.pow(0, NaN)`, _NaN) + test(`Math.pow(0, 0)`, 1) + test(`Math.pow(NaN, 0)`, 1) + test(`Math.pow(0, -0)`, 1) + test(`Math.pow(NaN, -0)`, 1) + test(`Math.pow(NaN, 1)`, _NaN) + test(`Math.pow(2, Infinity)`, _Infinity) + test(`1/Math.pow(2, -Infinity)`, _Infinity) + test(`Math.pow(1, Infinity)`, _NaN) + test(`Math.pow(1, -Infinity)`, _NaN) + test(`1/Math.pow(0.1, Infinity)`, _Infinity) + test(`Math.pow(0.1, -Infinity)`, _Infinity) + test(`Math.pow(Infinity, 1)`, _Infinity) + test(`1/Math.pow(Infinity, -1)`, _Infinity) + test(`Math.pow(-Infinity, 1)`, -_Infinity) + test(`Math.pow(-Infinity, 2)`, _Infinity) + test(`1/Math.pow(-Infinity, -1)`, -_Infinity) + test(`1/Math.pow(-Infinity, -2)`, _Infinity) + test(`1/Math.pow(0, 1)`, _Infinity) + test(`Math.pow(0, -1)`, _Infinity) + test(`1/Math.pow(-0, 1)`, -_Infinity) + test(`1/Math.pow(-0, 2)`, _Infinity) + test(`Math.pow(-0, -1)`, -_Infinity) + test(`Math.pow(-0, -2)`, _Infinity) + test(`Math.pow(-1, 0.1)`, _NaN) + + test(` + [ Math.pow(-1, +Infinity), Math.pow(1, Infinity) ]; + `, "NaN,NaN") + }) +} + +func TestMath_round(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.round(NaN)`, _NaN) + test(`1/Math.round(0)`, _Infinity) + test(`1/Math.round(-0)`, -_Infinity) + test(`Math.round(Infinity)`, _Infinity) + test(`Math.round(-Infinity)`, -_Infinity) + test(`1/Math.round(0.1)`, _Infinity) + test(`1/Math.round(-0.1)`, -_Infinity) + + test(`Math.round(3.5)`, 4) + test(`Math.round(-3.5)`, -3) + }) +} + +func TestMath_sin(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.sin(NaN)`, _NaN) + test(`1/Math.sin(+0)`, _Infinity) + test(`1/Math.sin(-0)`, -_Infinity) + test(`Math.sin(Infinity)`, _NaN) + test(`Math.sin(-Infinity)`, _NaN) + + test(`Math.sin(0.5)`, 0.479425538604203) + }) +} + +func TestMath_sqrt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.sqrt(NaN)`, _NaN) + test(`Math.sqrt(-1)`, _NaN) + test(`1/Math.sqrt(+0)`, _Infinity) + test(`1/Math.sqrt(-0)`, -_Infinity) + test(`Math.sqrt(Infinity)`, _Infinity) + + test(`Math.sqrt(2)`, 1.4142135623730951) + }) +} + +func TestMath_tan(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Math.tan(NaN)`, _NaN) + test(`1/Math.tan(+0)`, _Infinity) + test(`1/Math.tan(-0)`, -_Infinity) + test(`Math.tan(Infinity)`, _NaN) + test(`Math.tan(-Infinity)`, _NaN) + + test(`Math.tan(0.5)`, 0.5463024898437905) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/native_stack_test.go b/vendor/github.com/robertkrimen/otto/native_stack_test.go new file mode 100644 index 00000000..649358e6 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/native_stack_test.go @@ -0,0 +1,111 @@ +package otto + +import ( + "testing" +) + +func TestNativeStackFrames(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.Compile("input.js", ` + function A() { ext1(); } + function B() { ext2(); } + A(); + `) + if err != nil { + panic(err) + } + + vm.Set("ext1", func(c FunctionCall) Value { + if _, err := c.Otto.Eval("B()"); err != nil { + panic(err) + } + + return UndefinedValue() + }) + + vm.Set("ext2", func(c FunctionCall) Value { + { + // no limit, include innermost native frames + ctx := c.Otto.ContextSkip(-1, false) + + is(ctx.Stacktrace, []string{ + "github.com/robertkrimen/otto.TestNativeStackFrames.func1.2 (native_stack_test.go:28)", + "B (input.js:3:22)", + "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", + "A (input.js:2:22)", "input.js:4:7", + }) + + is(ctx.Callee, "github.com/robertkrimen/otto.TestNativeStackFrames.func1.2") + is(ctx.Filename, "native_stack_test.go") + is(ctx.Line, 28) + is(ctx.Column, 0) + } + + { + // no limit, skip innermost native frames + ctx := c.Otto.ContextSkip(-1, true) + + is(ctx.Stacktrace, []string{ + "B (input.js:3:22)", + "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", + "A (input.js:2:22)", "input.js:4:7", + }) + + is(ctx.Callee, "B") + is(ctx.Filename, "input.js") + is(ctx.Line, 3) + is(ctx.Column, 22) + } + + if _, err := c.Otto.Eval("ext3()"); err != nil { + panic(err) + } + + return UndefinedValue() + }) + + vm.Set("ext3", func(c FunctionCall) Value { + { + // no limit, include innermost native frames + ctx := c.Otto.ContextSkip(-1, false) + + is(ctx.Stacktrace, []string{ + "github.com/robertkrimen/otto.TestNativeStackFrames.func1.3 (native_stack_test.go:69)", + "github.com/robertkrimen/otto.TestNativeStackFrames.func1.2 (native_stack_test.go:28)", + "B (input.js:3:22)", + "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", + "A (input.js:2:22)", "input.js:4:7", + }) + + is(ctx.Callee, "github.com/robertkrimen/otto.TestNativeStackFrames.func1.3") + is(ctx.Filename, "native_stack_test.go") + is(ctx.Line, 69) + is(ctx.Column, 0) + } + + { + // no limit, skip innermost native frames + ctx := c.Otto.ContextSkip(-1, true) + + is(ctx.Stacktrace, []string{ + "B (input.js:3:22)", + "github.com/robertkrimen/otto.TestNativeStackFrames.func1.1 (native_stack_test.go:20)", + "A (input.js:2:22)", "input.js:4:7", + }) + + is(ctx.Callee, "B") + is(ctx.Filename, "input.js") + is(ctx.Line, 3) + is(ctx.Column, 22) + } + + return UndefinedValue() + }) + + if _, err := vm.Run(s); err != nil { + panic(err) + } + }) +} diff --git a/vendor/github.com/robertkrimen/otto/number_test.go b/vendor/github.com/robertkrimen/otto/number_test.go new file mode 100644 index 00000000..070cf157 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/number_test.go @@ -0,0 +1,165 @@ +package otto + +import ( + "testing" +) + +func TestNumber(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = Object.getOwnPropertyDescriptor(Number, "prototype"); + [ [ typeof Number.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestNumber_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + new Number(451).toString(); + `, "451") + + test(` + new Number(451).toString(10); + `, "451") + + test(` + new Number(451).toString(8); + `, "703") + + test(`raise: + new Number(451).toString(1); + `, "RangeError: toString() radix must be between 2 and 36") + + test(`raise: + new Number(451).toString(Infinity); + `, "RangeError: toString() radix must be between 2 and 36") + + test(` + new Number(NaN).toString() + `, "NaN") + + test(` + new Number(Infinity).toString() + `, "Infinity") + + test(` + new Number(Infinity).toString(16) + `, "Infinity") + + test(` + [ + Number.prototype.toString(undefined), + new Number().toString(undefined), + new Number(0).toString(undefined), + new Number(-1).toString(undefined), + new Number(1).toString(undefined), + new Number(Number.NaN).toString(undefined), + new Number(Number.POSITIVE_INFINITY).toString(undefined), + new Number(Number.NEGATIVE_INFINITY).toString(undefined) + ] + `, "0,0,0,-1,1,NaN,Infinity,-Infinity") + }) +} + +func TestNumber_toFixed(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new Number(451).toFixed(2)`, "451.00") + test(`12345.6789.toFixed()`, "12346") + test(`12345.6789.toFixed(1)`, "12345.7") + test(`12345.6789.toFixed(6)`, "12345.678900") + test(`(1.23e-20).toFixed(2)`, "0.00") + test(`2.34.toFixed(1)`, "2.3") // FIXME Wtf? "2.3" + test(`-2.34.toFixed(1)`, -2.3) // FIXME Wtf? -2.3 + test(`(-2.34).toFixed(1)`, "-2.3") + + test(`raise: + new Number("a").toFixed(Number.POSITIVE_INFINITY); + `, "RangeError: toFixed() precision must be between 0 and 20") + + test(` + [ + new Number(1e21).toFixed(), + new Number(1e21).toFixed(0), + new Number(1e21).toFixed(1), + new Number(1e21).toFixed(1.1), + new Number(1e21).toFixed(0.9), + new Number(1e21).toFixed("1"), + new Number(1e21).toFixed("1.1"), + new Number(1e21).toFixed("0.9"), + new Number(1e21).toFixed(Number.NaN), + new Number(1e21).toFixed("some string") + ]; + `, "1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21,1e+21") + + test(`raise: + new Number(1e21).toFixed(Number.POSITIVE_INFINITY); + `, "RangeError: toFixed() precision must be between 0 and 20") + }) +} + +func TestNumber_toExponential(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new Number(451).toExponential(2)`, "4.51e+02") + test(`77.1234.toExponential()`, "7.71234e+01") + test(`77.1234.toExponential(4)`, "7.7123e+01") + test(`77.1234.toExponential(2)`, "7.71e+01") + test(`77 .toExponential()`, "7.7e+01") + }) +} + +func TestNumber_toPrecision(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new Number(451).toPrecision()`, "451") + test(`new Number(451).toPrecision(1)`, "5e+02") + test(`5.123456.toPrecision()`, "5.123456") + test(`5.123456.toPrecision(5)`, "5.1235") + test(`5.123456.toPrecision(2)`, "5.1") + test(`5.123456.toPrecision(1)`, "5") + }) +} + +func TestNumber_toLocaleString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + new Number(451).toLocaleString(), + new Number(451).toLocaleString(10), + new Number(451).toLocaleString(8) + ]; + `, "451,451,703") + }) +} + +func TestValue_number(t *testing.T) { + tt(t, func() { + nm := toValue(0.0).number() + is(nm.kind, numberInteger) + + nm = toValue(3.14159).number() + is(nm.kind, numberFloat) + }) +} + +func Test_NaN(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ NaN === NaN, NaN == NaN ]; + `, "false,false") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/object.go b/vendor/github.com/robertkrimen/otto/object.go new file mode 100644 index 00000000..849812c9 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/object.go @@ -0,0 +1,156 @@ +package otto + +type _object struct { + runtime *_runtime + + class string + objectClass *_objectClass + value interface{} + + prototype *_object + extensible bool + + property map[string]_property + propertyOrder []string +} + +func newObject(runtime *_runtime, class string) *_object { + self := &_object{ + runtime: runtime, + class: class, + objectClass: _classObject, + property: make(map[string]_property), + extensible: true, + } + return self +} + +// 8.12 + +// 8.12.1 +func (self *_object) getOwnProperty(name string) *_property { + return self.objectClass.getOwnProperty(self, name) +} + +// 8.12.2 +func (self *_object) getProperty(name string) *_property { + return self.objectClass.getProperty(self, name) +} + +// 8.12.3 +func (self *_object) get(name string) Value { + return self.objectClass.get(self, name) +} + +// 8.12.4 +func (self *_object) canPut(name string) bool { + return self.objectClass.canPut(self, name) +} + +// 8.12.5 +func (self *_object) put(name string, value Value, throw bool) { + self.objectClass.put(self, name, value, throw) +} + +// 8.12.6 +func (self *_object) hasProperty(name string) bool { + return self.objectClass.hasProperty(self, name) +} + +func (self *_object) hasOwnProperty(name string) bool { + return self.objectClass.hasOwnProperty(self, name) +} + +type _defaultValueHint int + +const ( + defaultValueNoHint _defaultValueHint = iota + defaultValueHintString + defaultValueHintNumber +) + +// 8.12.8 +func (self *_object) DefaultValue(hint _defaultValueHint) Value { + if hint == defaultValueNoHint { + if self.class == "Date" { + // Date exception + hint = defaultValueHintString + } else { + hint = defaultValueHintNumber + } + } + methodSequence := []string{"valueOf", "toString"} + if hint == defaultValueHintString { + methodSequence = []string{"toString", "valueOf"} + } + for _, methodName := range methodSequence { + method := self.get(methodName) + // FIXME This is redundant... + if method.isCallable() { + result := method._object().call(toValue_object(self), nil, false, nativeFrame) + if result.IsPrimitive() { + return result + } + } + } + + panic(self.runtime.panicTypeError()) +} + +func (self *_object) String() string { + return self.DefaultValue(defaultValueHintString).string() +} + +func (self *_object) defineProperty(name string, value Value, mode _propertyMode, throw bool) bool { + return self.defineOwnProperty(name, _property{value, mode}, throw) +} + +// 8.12.9 +func (self *_object) defineOwnProperty(name string, descriptor _property, throw bool) bool { + return self.objectClass.defineOwnProperty(self, name, descriptor, throw) +} + +func (self *_object) delete(name string, throw bool) bool { + return self.objectClass.delete(self, name, throw) +} + +func (self *_object) enumerate(all bool, each func(string) bool) { + self.objectClass.enumerate(self, all, each) +} + +func (self *_object) _exists(name string) bool { + _, exists := self.property[name] + return exists +} + +func (self *_object) _read(name string) (_property, bool) { + property, exists := self.property[name] + return property, exists +} + +func (self *_object) _write(name string, value interface{}, mode _propertyMode) { + if value == nil { + value = Value{} + } + _, exists := self.property[name] + self.property[name] = _property{value, mode} + if !exists { + self.propertyOrder = append(self.propertyOrder, name) + } +} + +func (self *_object) _delete(name string) { + _, exists := self.property[name] + delete(self.property, name) + if exists { + for index, property := range self.propertyOrder { + if name == property { + if index == len(self.propertyOrder)-1 { + self.propertyOrder = self.propertyOrder[:index] + } else { + self.propertyOrder = append(self.propertyOrder[:index], self.propertyOrder[index+1:]...) + } + } + } + } +} diff --git a/vendor/github.com/robertkrimen/otto/object_class.go b/vendor/github.com/robertkrimen/otto/object_class.go new file mode 100644 index 00000000..d18b9ced --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/object_class.go @@ -0,0 +1,493 @@ +package otto + +import ( + "encoding/json" +) + +type _objectClass struct { + getOwnProperty func(*_object, string) *_property + getProperty func(*_object, string) *_property + get func(*_object, string) Value + canPut func(*_object, string) bool + put func(*_object, string, Value, bool) + hasProperty func(*_object, string) bool + hasOwnProperty func(*_object, string) bool + defineOwnProperty func(*_object, string, _property, bool) bool + delete func(*_object, string, bool) bool + enumerate func(*_object, bool, func(string) bool) + clone func(*_object, *_object, *_clone) *_object + marshalJSON func(*_object) json.Marshaler +} + +func objectEnumerate(self *_object, all bool, each func(string) bool) { + for _, name := range self.propertyOrder { + if all || self.property[name].enumerable() { + if !each(name) { + return + } + } + } +} + +var ( + _classObject, + _classArray, + _classString, + _classArguments, + _classGoStruct, + _classGoMap, + _classGoArray, + _classGoSlice, + _ *_objectClass +) + +func init() { + _classObject = &_objectClass{ + objectGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + objectDefineOwnProperty, + objectDelete, + objectEnumerate, + objectClone, + nil, + } + + _classArray = &_objectClass{ + objectGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + arrayDefineOwnProperty, + objectDelete, + objectEnumerate, + objectClone, + nil, + } + + _classString = &_objectClass{ + stringGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + objectDefineOwnProperty, + objectDelete, + stringEnumerate, + objectClone, + nil, + } + + _classArguments = &_objectClass{ + argumentsGetOwnProperty, + objectGetProperty, + argumentsGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + argumentsDefineOwnProperty, + argumentsDelete, + objectEnumerate, + objectClone, + nil, + } + + _classGoStruct = &_objectClass{ + goStructGetOwnProperty, + objectGetProperty, + objectGet, + goStructCanPut, + goStructPut, + objectHasProperty, + objectHasOwnProperty, + objectDefineOwnProperty, + objectDelete, + goStructEnumerate, + objectClone, + goStructMarshalJSON, + } + + _classGoMap = &_objectClass{ + goMapGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + goMapDefineOwnProperty, + goMapDelete, + goMapEnumerate, + objectClone, + nil, + } + + _classGoArray = &_objectClass{ + goArrayGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + goArrayDefineOwnProperty, + goArrayDelete, + goArrayEnumerate, + objectClone, + nil, + } + + _classGoSlice = &_objectClass{ + goSliceGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + goSliceDefineOwnProperty, + goSliceDelete, + goSliceEnumerate, + objectClone, + nil, + } +} + +// Allons-y + +// 8.12.1 +func objectGetOwnProperty(self *_object, name string) *_property { + // Return a _copy_ of the property + property, exists := self._read(name) + if !exists { + return nil + } + return &property +} + +// 8.12.2 +func objectGetProperty(self *_object, name string) *_property { + property := self.getOwnProperty(name) + if property != nil { + return property + } + if self.prototype != nil { + return self.prototype.getProperty(name) + } + return nil +} + +// 8.12.3 +func objectGet(self *_object, name string) Value { + property := self.getProperty(name) + if property != nil { + return property.get(self) + } + return Value{} +} + +// 8.12.4 +func objectCanPut(self *_object, name string) bool { + canPut, _, _ := _objectCanPut(self, name) + return canPut +} + +func _objectCanPut(self *_object, name string) (canPut bool, property *_property, setter *_object) { + property = self.getOwnProperty(name) + if property != nil { + switch propertyValue := property.value.(type) { + case Value: + canPut = property.writable() + return + case _propertyGetSet: + setter = propertyValue[1] + canPut = setter != nil + return + default: + panic(self.runtime.panicTypeError()) + } + } + + if self.prototype == nil { + return self.extensible, nil, nil + } + + property = self.prototype.getProperty(name) + if property == nil { + return self.extensible, nil, nil + } + + switch propertyValue := property.value.(type) { + case Value: + if !self.extensible { + return false, nil, nil + } + return property.writable(), nil, nil + case _propertyGetSet: + setter = propertyValue[1] + canPut = setter != nil + return + default: + panic(self.runtime.panicTypeError()) + } +} + +// 8.12.5 +func objectPut(self *_object, name string, value Value, throw bool) { + + if true { + // Shortcut... + // + // So, right now, every class is using objectCanPut and every class + // is using objectPut. + // + // If that were to no longer be the case, we would have to have + // something to detect that here, so that we do not use an + // incompatible canPut routine + canPut, property, setter := _objectCanPut(self, name) + if !canPut { + self.runtime.typeErrorResult(throw) + } else if setter != nil { + setter.call(toValue(self), []Value{value}, false, nativeFrame) + } else if property != nil { + property.value = value + self.defineOwnProperty(name, *property, throw) + } else { + self.defineProperty(name, value, 0111, throw) + } + return + } + + // The long way... + // + // Right now, code should never get here, see above + if !self.canPut(name) { + self.runtime.typeErrorResult(throw) + return + } + + property := self.getOwnProperty(name) + if property == nil { + property = self.getProperty(name) + if property != nil { + if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor { + getSet[1].call(toValue(self), []Value{value}, false, nativeFrame) + return + } + } + self.defineProperty(name, value, 0111, throw) + } else { + switch propertyValue := property.value.(type) { + case Value: + property.value = value + self.defineOwnProperty(name, *property, throw) + case _propertyGetSet: + if propertyValue[1] != nil { + propertyValue[1].call(toValue(self), []Value{value}, false, nativeFrame) + return + } + if throw { + panic(self.runtime.panicTypeError()) + } + default: + panic(self.runtime.panicTypeError()) + } + } +} + +// 8.12.6 +func objectHasProperty(self *_object, name string) bool { + return self.getProperty(name) != nil +} + +func objectHasOwnProperty(self *_object, name string) bool { + return self.getOwnProperty(name) != nil +} + +// 8.12.9 +func objectDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + property, exists := self._read(name) + { + if !exists { + if !self.extensible { + goto Reject + } + if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor { + if newGetSet[0] == &_nilGetSetObject { + newGetSet[0] = nil + } + if newGetSet[1] == &_nilGetSetObject { + newGetSet[1] = nil + } + descriptor.value = newGetSet + } + self._write(name, descriptor.value, descriptor.mode) + return true + } + if descriptor.isEmpty() { + return true + } + + // TODO Per 8.12.9.6 - We should shortcut here (returning true) if + // the current and new (define) properties are the same + + configurable := property.configurable() + if !configurable { + if descriptor.configurable() { + goto Reject + } + // Test that, if enumerable is set on the property descriptor, then it should + // be the same as the existing property + if descriptor.enumerateSet() && descriptor.enumerable() != property.enumerable() { + goto Reject + } + } + value, isDataDescriptor := property.value.(Value) + getSet, _ := property.value.(_propertyGetSet) + if descriptor.isGenericDescriptor() { + // GenericDescriptor + } else if isDataDescriptor != descriptor.isDataDescriptor() { + // DataDescriptor <=> AccessorDescriptor + if !configurable { + goto Reject + } + } else if isDataDescriptor && descriptor.isDataDescriptor() { + // DataDescriptor <=> DataDescriptor + if !configurable { + if !property.writable() && descriptor.writable() { + goto Reject + } + if !property.writable() { + if descriptor.value != nil && !sameValue(value, descriptor.value.(Value)) { + goto Reject + } + } + } + } else { + // AccessorDescriptor <=> AccessorDescriptor + newGetSet, _ := descriptor.value.(_propertyGetSet) + presentGet, presentSet := true, true + if newGetSet[0] == &_nilGetSetObject { + // Present, but nil + newGetSet[0] = nil + } else if newGetSet[0] == nil { + // Missing, not even nil + newGetSet[0] = getSet[0] + presentGet = false + } + if newGetSet[1] == &_nilGetSetObject { + // Present, but nil + newGetSet[1] = nil + } else if newGetSet[1] == nil { + // Missing, not even nil + newGetSet[1] = getSet[1] + presentSet = false + } + if !configurable { + if (presentGet && (getSet[0] != newGetSet[0])) || (presentSet && (getSet[1] != newGetSet[1])) { + goto Reject + } + } + descriptor.value = newGetSet + } + { + // This section will preserve attributes of + // the original property, if necessary + value1 := descriptor.value + if value1 == nil { + value1 = property.value + } else if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor { + if newGetSet[0] == &_nilGetSetObject { + newGetSet[0] = nil + } + if newGetSet[1] == &_nilGetSetObject { + newGetSet[1] = nil + } + value1 = newGetSet + } + mode1 := descriptor.mode + if mode1&0222 != 0 { + // TODO Factor this out into somewhere testable + // (Maybe put into switch ...) + mode0 := property.mode + if mode1&0200 != 0 { + if descriptor.isDataDescriptor() { + mode1 &= ^0200 // Turn off "writable" missing + mode1 |= (mode0 & 0100) + } + } + if mode1&020 != 0 { + mode1 |= (mode0 & 010) + } + if mode1&02 != 0 { + mode1 |= (mode0 & 01) + } + mode1 &= 0311 // 0311 to preserve the non-setting on "writable" + } + self._write(name, value1, mode1) + } + return true + } +Reject: + if throw { + panic(self.runtime.panicTypeError()) + } + return false +} + +func objectDelete(self *_object, name string, throw bool) bool { + property_ := self.getOwnProperty(name) + if property_ == nil { + return true + } + if property_.configurable() { + self._delete(name) + return true + } + return self.runtime.typeErrorResult(throw) +} + +func objectClone(in *_object, out *_object, clone *_clone) *_object { + *out = *in + + out.runtime = clone.runtime + if out.prototype != nil { + out.prototype = clone.object(in.prototype) + } + out.property = make(map[string]_property, len(in.property)) + out.propertyOrder = make([]string, len(in.propertyOrder)) + copy(out.propertyOrder, in.propertyOrder) + for index, property := range in.property { + out.property[index] = clone.property(property) + } + + switch value := in.value.(type) { + case _nativeFunctionObject: + out.value = value + case _bindFunctionObject: + out.value = _bindFunctionObject{ + target: clone.object(value.target), + this: clone.value(value.this), + argumentList: clone.valueArray(value.argumentList), + } + case _nodeFunctionObject: + out.value = _nodeFunctionObject{ + node: value.node, + stash: clone.stash(value.stash), + } + case _argumentsObject: + out.value = value.clone(clone) + } + + return out +} diff --git a/vendor/github.com/robertkrimen/otto/object_test.go b/vendor/github.com/robertkrimen/otto/object_test.go new file mode 100644 index 00000000..d1e90680 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/object_test.go @@ -0,0 +1,639 @@ +package otto + +import ( + "testing" +) + +func TestObject_(t *testing.T) { + tt(t, func() { + test, _ := test() + + object := newObject(nil, "") + is(object != nil, true) + + object.put("xyzzy", toValue("Nothing happens."), true) + is(object.get("xyzzy"), "Nothing happens.") + + test(` + var abc = Object.getOwnPropertyDescriptor(Object, "prototype"); + [ [ typeof Object.prototype, abc.writable, abc.enumerable, abc.configurable ], + ]; + `, "object,false,false,false") + }) +} + +func TestStringObject(t *testing.T) { + tt(t, func() { + object := New().runtime.newStringObject(toValue("xyzzy")) + is(object.get("1"), "y") + is(object.get("10"), "undefined") + is(object.get("2"), "z") + }) +} + +func TestObject_getPrototypeOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = {}; + def = Object.getPrototypeOf(abc); + ghi = Object.getPrototypeOf(def); + [abc,def,ghi,ghi+""]; + `, "[object Object],[object Object],,null") + + test(` + abc = Object.getOwnPropertyDescriptor(Object, "getPrototypeOf"); + [ abc.value === Object.getPrototypeOf, abc.writable, abc.enumerable, abc.configurable ]; + `, "true,true,false,true") + }) +} + +func TestObject_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ new Object("abc"), new Object(2+2) ]; + `, "abc,4") + }) +} + +func TestObject_create(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.create()`, "TypeError") + + test(` + var abc = Object.create(null) + var def = Object.create({x: 10, y: 20}) + var ghi = Object.create(Object.prototype) + + var jkl = Object.create({x: 10, y: 20}, { + z: { + value: 30, + writable: true + }, + // sum: { + // get: function() { + // return this.x + this.y + this.z + // } + // } + }); + [ abc.prototype, def.x, def.y, ghi, jkl.x, jkl.y, jkl.z ] + `, ",10,20,[object Object],10,20,30") + + test(` + var properties = {}; + Object.defineProperty(properties, "abc", { + value: {}, + enumerable: false + }); + var mno = Object.create({}, properties); + mno.hasOwnProperty("abc"); + `, false) + }) +} + +func TestObject_toLocaleString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + ({}).toLocaleString(); + `, "[object Object]") + + test(` + object = { + toString: function() { + return "Nothing happens."; + } + }; + object.toLocaleString(); + `, "Nothing happens.") + }) +} + +func TestObject_isExtensible(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Object.isExtensible(); + `, "TypeError") + + // FIXME terst, Why raise? + test(`raise: + Object.isExtensible({}); + `, true) + + test(`Object.isExtensible.length`, 1) + test(`Object.isExtensible.prototype`, "undefined") + }) +} + +func TestObject_preventExtensions(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Object.preventExtensions() + `, "TypeError") + + test(`raise: + var abc = { def: true }; + var ghi = Object.preventExtensions(abc); + [ ghi.def === true, Object.isExtensible(abc), Object.isExtensible(ghi) ]; + `, "true,false,false") + + test(` + var abc = new String(); + var def = Object.isExtensible(abc); + Object.preventExtensions(abc); + var ghi = false; + try { + Object.defineProperty(abc, "0", { value: "~" }); + } catch (err) { + ghi = err instanceof TypeError; + } + [ def, ghi, abc.hasOwnProperty("0"), typeof abc[0] ]; + `, "true,true,false,undefined") + + test(`Object.preventExtensions.length`, 1) + test(`Object.preventExtensions.prototype`, "undefined") + }) +} + +func TestObject_isSealed(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Object.isSealed.length`, 1) + test(`Object.isSealed.prototype`, "undefined") + }) +} + +func TestObject_seal(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.seal()`, "TypeError") + + test(` + var abc = {a:1,b:1,c:3}; + var sealed = Object.isSealed(abc); + Object.seal(abc); + [sealed, Object.isSealed(abc)]; + `, "false,true") + + test(` + var abc = {a:1,b:1,c:3}; + var sealed = Object.isSealed(abc); + var caught = false; + Object.seal(abc); + abc.b = 5; + Object.defineProperty(abc, "a", {value:4}); + try { + Object.defineProperty(abc, "a", {value:42,enumerable:false}); + } catch (e) { + caught = e instanceof TypeError; + } + [sealed, Object.isSealed(abc), caught, abc.a, abc.b]; + `, "false,true,true,4,5") + + test(`Object.seal.length`, 1) + test(`Object.seal.prototype`, "undefined") + }) +} + +func TestObject_isFrozen(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.isFrozen()`, "TypeError") + test(`Object.isFrozen(Object.preventExtensions({a:1}))`, false) + test(`Object.isFrozen({})`, false) + + test(` + var abc = {}; + Object.defineProperty(abc, "def", { + value: "def", + writable: true, + configurable: false + }); + Object.preventExtensions(abc); + !Object.isFrozen(abc); + `, true) + + test(`Object.isFrozen.length`, 1) + test(`Object.isFrozen.prototype`, "undefined") + }) +} + +func TestObject_freeze(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: Object.freeze()`, "TypeError") + + test(` + var abc = {a:1,b:2,c:3}; + var frozen = Object.isFrozen(abc); + Object.freeze(abc); + abc.b = 5; + [frozen, Object.isFrozen(abc), abc.b]; + `, "false,true,2") + + test(` + var abc = {a:1,b:2,c:3}; + var frozen = Object.isFrozen(abc); + var caught = false; + Object.freeze(abc); + abc.b = 5; + try { + Object.defineProperty(abc, "a", {value:4}); + } catch (e) { + caught = e instanceof TypeError; + } + [frozen, Object.isFrozen(abc), caught, abc.a, abc.b]; + `, "false,true,true,1,2") + + test(`Object.freeze.length`, 1) + test(`Object.freeze.prototype`, "undefined") + }) +} + +func TestObject_defineProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + (function(abc, def, ghi){ + Object.defineProperty(arguments, "0", { + enumerable: false + }); + return true; + })(0, 1, 2); + `, true) + + test(` + var abc = {}; + abc.def = 3.14; // Default: writable: true, enumerable: true, configurable: true + + Object.defineProperty(abc, "def", { + value: 42 + }); + + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ ghi.value, ghi.writable, ghi.enumerable, ghi.configurable ]; + `, "42,true,true,true") + + // Test that we handle the case of DefineOwnProperty + // where [[Writable]] is something but [[Value]] is not + test(` + var abc = []; + Object.defineProperty(abc, "0", { writable: false }); + 0 in abc; + `, true) + + // Test that we handle the case of DefineOwnProperty + // where [[Writable]] is something but [[Value]] is not + // (and the property originally had something for [[Value]] + test(` + abc = { + def: 42 + }; + Object.defineProperty(abc, "def", { writable: false }); + abc.def; + `, 42) + }) +} + +func TestObject_keys(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Object.keys({ abc:undefined, def:undefined })`, "abc,def") + + test(` + function abc() { + this.abc = undefined; + this.def = undefined; + } + Object.keys(new abc()) + `, "abc,def") + + test(` + function def() { + this.ghi = undefined; + } + def.prototype = new abc(); + Object.keys(new def()); + `, "ghi") + + test(` + var ghi = Object.create( + { + abc: undefined, + def: undefined + }, + { + ghi: { value: undefined, enumerable: true }, + jkl: { value: undefined, enumerable: false } + } + ); + Object.keys(ghi); + `, "ghi") + + test(` + (function(abc, def, ghi){ + return Object.keys(arguments) + })(undefined, undefined); + `, "0,1") + + test(` + (function(abc, def, ghi){ + return Object.keys(arguments) + })(undefined, undefined, undefined, undefined); + `, "0,1,2,3") + }) +} + +func TestObject_getOwnPropertyNames(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`Object.getOwnPropertyNames({ abc:undefined, def:undefined })`, "abc,def") + + test(` + var ghi = Object.create( + { + abc: undefined, + def: undefined + }, + { + ghi: { value: undefined, enumerable: true }, + jkl: { value: undefined, enumerable: false } + } + ); + Object.getOwnPropertyNames(ghi) + `, "ghi,jkl") + }) +} + +func TestObjectGetterSetter(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + Object.create({}, { + abc: { + get: function(){ + return "true"; + }, + writable: true + } + }).abc; + `, "TypeError") + + test(`raise: + Object.create({}, { + abc: { + get: function(){ + return "true"; + }, + writable: false + } + }).abc; + `, "TypeError") + + test(` + Object.create({}, { + abc: { + get: function(){ + return "true"; + } + } + }).abc; + `, "true") + + test(` + Object.create({xyz:true},{abc:{get:function(){return this.xyx}}}).abc; + Object.create({ + xyz: true + }, { + abc: { + get: function(){ + return this.xyz; + } + } + }).abc; + `, true) + + test(` + var abc = false; + var def = Object.create({}, { + xyz: { + set: function(value) { + abc = value; + } + } + }); + def.xyz = true; + [ abc ]; + `, "true") + + test(` + var abc = {}; + Object.defineProperty(abc, "def", { + value: "xyzzy", + configurable: true + }); + Object.preventExtensions(abc); + Object.defineProperty(abc, "def", { + get: function() { + return 5; + } + }); + var def = Object.getOwnPropertyDescriptor(abc, "def"); + [ abc.def, typeof def.get, typeof def.set, typeof def.value, def.configurable, def.enumerable, typeof def.writable ]; + `, "5,function,undefined,undefined,true,false,undefined") + + test(` + var abc = {}; + Object.defineProperty(abc, "def", { + get: function() { + return 5; + } + configurable: true + }); + Object.preventExtensions(abc); + Object.defineProperty(abc, "def", { + value: "xyzzy", + }); + var def = Object.getOwnPropertyDescriptor(abc, "def"); + [ abc.def, typeof def.get, typeof def.set, def.value, def.configurable, def.enumerable, def.writable ]; + `, "xyzzy,undefined,undefined,xyzzy,true,false,false") + + test(` + var abc = {}; + + function _get0() { + return 10; + } + + function _set(value) { + abc.def = value; + } + + Object.defineProperty(abc, "ghi", { + get: _get0, + set: _set, + configurable: true + }); + + function _get1() { + return 20; + } + + Object.defineProperty(abc, "ghi", { + get: _get0 + }); + + var descriptor = Object.getOwnPropertyDescriptor(abc, "ghi"); + [ typeof descriptor.set ]; + `, "function") + + test(`raise: + var abc = []; + Object.defineProperty(abc, "length", { + get: function () { + return 2; + } + }); + `, "TypeError") + + test(` + var abc = {}; + + var getter = function() { + return 1; + } + + Object.defineProperty(abc, "def", { + get: getter, + configurable: false + }); + + var jkl = undefined; + try { + Object.defineProperty(abc, "def", { + get: undefined + }); + } + catch (err) { + jkl = err; + } + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ jkl instanceof TypeError, ghi.get === getter, ghi.configurable, ghi.enumerable ]; + `, "true,true,false,false") + + test(` + var abc = {}; + + var getter = function() { + return 1; + }; + + Object.defineProperty(abc, "def", { + get: getter + }); + + Object.defineProperty(abc, "def", { + set: undefined + }); + + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ ghi.get === getter, ghi.set === undefined, ghi.configurable, ghi.enumerable ]; + `, "true,true,false,false") + + test(` + var abc = {}; + + var getter = function() { + return 1; + }; + + Object.defineProperty(abc, "def", { + get: getter + }); + + var jkl = undefined; + try { + Object.defineProperty(abc, "def", { + set: function() {} + }); + } + catch (err) { + jkl = err; + } + + var ghi = Object.getOwnPropertyDescriptor(abc, "def"); + [ jkl instanceof TypeError, ghi.get === getter, ghi.set, ghi.configurable, ghi.enumerable ]; + `, "true,true,,false,false") + + test(` + var abc = {}; + var def = "xyzzy"; + + Object.defineProperty(abc, "ghi", { + get: undefined, + set: function(value) { + def = value; + }, + enumerable: true, + configurable: true + }); + + var hasOwn = abc.hasOwnProperty("ghi"); + var descriptor = Object.getOwnPropertyDescriptor(abc, "ghi"); + + [ hasOwn, typeof descriptor.get ]; + `, "true,undefined") + + test(` + var abc = "xyzzy"; + Object.defineProperty(Array.prototype, "abc", { + get: function () { + return abc; + }, + set: function (value) { + abc = value; + }, + enumerable: true, + configurable: true + }); + var def = []; + def.abc = 3.14159; + [ def.hasOwnProperty("abc"), def.abc, abc ]; + `, "false,3.14159,3.14159") + }) +} + +func TestProperty(t *testing.T) { + tt(t, func() { + property := _property{} + property.writeOn() + is(property.writeSet(), true) + + property.writeClear() + is(property.writeSet(), false) + + property.writeOff() + is(property.writeSet(), true) + + property.writeClear() + is(property.writeSet(), false) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/otto.go b/vendor/github.com/robertkrimen/otto/otto.go new file mode 100644 index 00000000..b5b528d5 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/otto.go @@ -0,0 +1,770 @@ +/* +Package otto is a JavaScript parser and interpreter written natively in Go. + +http://godoc.org/github.com/robertkrimen/otto + + import ( + "github.com/robertkrimen/otto" + ) + +Run something in the VM + + vm := otto.New() + vm.Run(` + abc = 2 + 2; + console.log("The value of abc is " + abc); // 4 + `) + +Get a value out of the VM + + value, err := vm.Get("abc") + value, _ := value.ToInteger() + } + +Set a number + + vm.Set("def", 11) + vm.Run(` + console.log("The value of def is " + def); + // The value of def is 11 + `) + +Set a string + + vm.Set("xyzzy", "Nothing happens.") + vm.Run(` + console.log(xyzzy.length); // 16 + `) + +Get the value of an expression + + value, _ = vm.Run("xyzzy.length") + { + // value is an int64 with a value of 16 + value, _ := value.ToInteger() + } + +An error happens + + value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length") + if err != nil { + // err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined + // If there is an error, then value.IsUndefined() is true + ... + } + +Set a Go function + + vm.Set("sayHello", func(call otto.FunctionCall) otto.Value { + fmt.Printf("Hello, %s.\n", call.Argument(0).String()) + return otto.Value{} + }) + +Set a Go function that returns something useful + + vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value { + right, _ := call.Argument(0).ToInteger() + result, _ := vm.ToValue(2 + right) + return result + }) + +Use the functions in JavaScript + + result, _ = vm.Run(` + sayHello("Xyzzy"); // Hello, Xyzzy. + sayHello(); // Hello, undefined + + result = twoPlus(2.0); // 4 + `) + +Parser + +A separate parser is available in the parser package if you're just interested in building an AST. + +http://godoc.org/github.com/robertkrimen/otto/parser + +Parse and return an AST + + filename := "" // A filename is optional + src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); + ` + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, filename, src, 0) + +otto + +You can run (Go) JavaScript from the commandline with: http://github.com/robertkrimen/otto/tree/master/otto + + $ go get -v github.com/robertkrimen/otto/otto + +Run JavaScript by entering some source on stdin or by giving otto a filename: + + $ otto example.js + +underscore + +Optionally include the JavaScript utility-belt library, underscore, with this import: + + import ( + "github.com/robertkrimen/otto" + _ "github.com/robertkrimen/otto/underscore" + ) + + // Now every otto runtime will come loaded with underscore + +For more information: http://github.com/robertkrimen/otto/tree/master/underscore + +Caveat Emptor + +The following are some limitations with otto: + + * "use strict" will parse, but does nothing. + * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. + * Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. + +Regular Expression Incompatibility + +Go translates JavaScript-style regular expressions into something that is "regexp" compatible via `parser.TransformRegExp`. +Unfortunately, RegExp requires backtracking for some patterns, and backtracking is not supported by the standard Go engine: https://code.google.com/p/re2/wiki/Syntax + +Therefore, the following syntax is incompatible: + + (?=) // Lookahead (positive), currently a parsing error + (?!) // Lookahead (backhead), currently a parsing error + \1 // Backreference (\1, \2, \3, ...), currently a parsing error + +A brief discussion of these limitations: "Regexp (?!re)" https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E + +More information about re2: https://code.google.com/p/re2/ + +In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ]. +The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. + +Halting Problem + +If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this: + + package main + + import ( + "errors" + "fmt" + "os" + "time" + + "github.com/robertkrimen/otto" + ) + + var halt = errors.New("Stahp") + + func main() { + runUnsafe(`var abc = [];`) + runUnsafe(` + while (true) { + // Loop forever + }`) + } + + func runUnsafe(unsafe string) { + start := time.Now() + defer func() { + duration := time.Since(start) + if caught := recover(); caught != nil { + if caught == halt { + fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration) + return + } + panic(caught) // Something else happened, repanic! + } + fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration) + }() + + vm := otto.New() + vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking + + go func() { + time.Sleep(2 * time.Second) // Stop after two seconds + vm.Interrupt <- func() { + panic(halt) + } + }() + + vm.Run(unsafe) // Here be dragons (risky code) + } + +Where is setTimeout/setInterval? + +These timing functions are not actually part of the ECMA-262 specification. Typically, they belong to the `windows` object (in the browser). +It would not be difficult to provide something like these via Go, but you probably want to wrap otto in an event loop in that case. + +For an example of how this could be done in Go with otto, see natto: + +http://github.com/robertkrimen/natto + +Here is some more discussion of the issue: + +* http://book.mixu.net/node/ch2.html + +* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 + +* http://aaroncrane.co.uk/2009/02/perl_safe_signals/ + +*/ +package otto + +import ( + "fmt" + "strings" + + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/registry" +) + +// Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace. +type Otto struct { + // Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example. + // See "Halting Problem" for more information. + Interrupt chan func() + runtime *_runtime +} + +// New will allocate a new JavaScript runtime +func New() *Otto { + self := &Otto{ + runtime: newContext(), + } + self.runtime.otto = self + self.runtime.traceLimit = 10 + self.Set("console", self.runtime.newConsole()) + + registry.Apply(func(entry registry.Entry) { + self.Run(entry.Source()) + }) + + return self +} + +func (otto *Otto) clone() *Otto { + self := &Otto{ + runtime: otto.runtime.clone(), + } + self.runtime.otto = self + return self +} + +// Run will allocate a new JavaScript runtime, run the given source +// on the allocated runtime, and return the runtime, resulting value, and +// error (if any). +// +// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. +// +// src may also be a Script. +// +// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. +// +func Run(src interface{}) (*Otto, Value, error) { + otto := New() + value, err := otto.Run(src) // This already does safety checking + return otto, value, err +} + +// Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any) +// +// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. +// +// If the runtime is unable to parse source, then this function will return undefined and the parse error (nothing +// will be evaluated in this case). +// +// src may also be a Script. +// +// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. +// +func (self Otto) Run(src interface{}) (Value, error) { + value, err := self.runtime.cmpl_run(src, nil) + if !value.safe() { + value = Value{} + } + return value, err +} + +// Eval will do the same thing as Run, except without leaving the current scope. +// +// By staying in the same scope, the code evaluated has access to everything +// already defined in the current stack frame. This is most useful in, for +// example, a debugger call. +func (self Otto) Eval(src interface{}) (Value, error) { + if self.runtime.scope == nil { + self.runtime.enterGlobalScope() + defer self.runtime.leaveScope() + } + + value, err := self.runtime.cmpl_eval(src, nil) + if !value.safe() { + value = Value{} + } + return value, err +} + +// Get the value of the top-level binding of the given name. +// +// If there is an error (like the binding does not exist), then the value +// will be undefined. +func (self Otto) Get(name string) (Value, error) { + value := Value{} + err := catchPanic(func() { + value = self.getValue(name) + }) + if !value.safe() { + value = Value{} + } + return value, err +} + +func (self Otto) getValue(name string) Value { + return self.runtime.globalStash.getBinding(name, false) +} + +// Set the top-level binding of the given name to the given value. +// +// Set will automatically apply ToValue to the given value in order +// to convert it to a JavaScript value (type Value). +// +// If there is an error (like the binding is read-only, or the ToValue conversion +// fails), then an error is returned. +// +// If the top-level binding does not exist, it will be created. +func (self Otto) Set(name string, value interface{}) error { + { + value, err := self.ToValue(value) + if err != nil { + return err + } + err = catchPanic(func() { + self.setValue(name, value) + }) + return err + } +} + +func (self Otto) setValue(name string, value Value) { + self.runtime.globalStash.setValue(name, value, false) +} + +func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) { + self.runtime.debugger = fn +} + +func (self Otto) SetRandomSource(fn func() float64) { + self.runtime.random = fn +} + +// SetStackDepthLimit sets an upper limit to the depth of the JavaScript +// stack. In simpler terms, this limits the number of "nested" function calls +// you can make in a particular interpreter instance. +// +// Note that this doesn't take into account the Go stack depth. If your +// JavaScript makes a call to a Go function, otto won't keep track of what +// happens outside the interpreter. So if your Go function is infinitely +// recursive, you're still in trouble. +func (self Otto) SetStackDepthLimit(limit int) { + self.runtime.stackLimit = limit +} + +// SetStackTraceLimit sets an upper limit to the number of stack frames that +// otto will use when formatting an error's stack trace. By default, the limit +// is 10. This is consistent with V8 and SpiderMonkey. +// +// TODO: expose via `Error.stackTraceLimit` +func (self Otto) SetStackTraceLimit(limit int) { + self.runtime.traceLimit = limit +} + +// MakeCustomError creates a new Error object with the given name and message, +// returning it as a Value. +func (self Otto) MakeCustomError(name, message string) Value { + return self.runtime.toValue(self.runtime.newError(name, self.runtime.toValue(message), 0)) +} + +// MakeRangeError creates a new RangeError object with the given message, +// returning it as a Value. +func (self Otto) MakeRangeError(message string) Value { + return self.runtime.toValue(self.runtime.newRangeError(self.runtime.toValue(message))) +} + +// MakeSyntaxError creates a new SyntaxError object with the given message, +// returning it as a Value. +func (self Otto) MakeSyntaxError(message string) Value { + return self.runtime.toValue(self.runtime.newSyntaxError(self.runtime.toValue(message))) +} + +// MakeTypeError creates a new TypeError object with the given message, +// returning it as a Value. +func (self Otto) MakeTypeError(message string) Value { + return self.runtime.toValue(self.runtime.newTypeError(self.runtime.toValue(message))) +} + +// Context is a structure that contains information about the current execution +// context. +type Context struct { + Filename string + Line int + Column int + Callee string + Symbols map[string]Value + This Value + Stacktrace []string +} + +// Context returns the current execution context of the vm, traversing up to +// ten stack frames, and skipping any innermost native function stack frames. +func (self Otto) Context() Context { + return self.ContextSkip(10, true) +} + +// ContextLimit returns the current execution context of the vm, with a +// specific limit on the number of stack frames to traverse, skipping any +// innermost native function stack frames. +func (self Otto) ContextLimit(limit int) Context { + return self.ContextSkip(limit, true) +} + +// ContextSkip returns the current execution context of the vm, with a +// specific limit on the number of stack frames to traverse, optionally +// skipping any innermost native function stack frames. +func (self Otto) ContextSkip(limit int, skipNative bool) (ctx Context) { + // Ensure we are operating in a scope + if self.runtime.scope == nil { + self.runtime.enterGlobalScope() + defer self.runtime.leaveScope() + } + + scope := self.runtime.scope + frame := scope.frame + + for skipNative && frame.native && scope.outer != nil { + scope = scope.outer + frame = scope.frame + } + + // Get location information + ctx.Filename = "" + ctx.Callee = frame.callee + + switch { + case frame.native: + ctx.Filename = frame.nativeFile + ctx.Line = frame.nativeLine + ctx.Column = 0 + case frame.file != nil: + ctx.Filename = "" + + if p := frame.file.Position(file.Idx(frame.offset)); p != nil { + ctx.Line = p.Line + ctx.Column = p.Column + + if p.Filename != "" { + ctx.Filename = p.Filename + } + } + } + + // Get the current scope this Value + ctx.This = toValue_object(scope.this) + + // Build stacktrace (up to 10 levels deep) + ctx.Symbols = make(map[string]Value) + ctx.Stacktrace = append(ctx.Stacktrace, frame.location()) + for limit != 0 { + // Get variables + stash := scope.lexical + for { + for _, name := range getStashProperties(stash) { + if _, ok := ctx.Symbols[name]; !ok { + ctx.Symbols[name] = stash.getBinding(name, true) + } + } + stash = stash.outer() + if stash == nil || stash.outer() == nil { + break + } + } + + scope = scope.outer + if scope == nil { + break + } + if scope.frame.offset >= 0 { + ctx.Stacktrace = append(ctx.Stacktrace, scope.frame.location()) + } + limit-- + } + + return +} + +// Call the given JavaScript with a given this and arguments. +// +// If this is nil, then some special handling takes place to determine the proper +// this value, falling back to a "standard" invocation if necessary (where this is +// undefined). +// +// If source begins with "new " (A lowercase new followed by a space), then +// Call will invoke the function constructor rather than performing a function call. +// In this case, the this argument has no effect. +// +// // value is a String object +// value, _ := vm.Call("Object", nil, "Hello, World.") +// +// // Likewise... +// value, _ := vm.Call("new Object", nil, "Hello, World.") +// +// // This will perform a concat on the given array and return the result +// // value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ] +// value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") +// +func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) { + + thisValue := Value{} + + construct := false + if strings.HasPrefix(source, "new ") { + source = source[4:] + construct = true + } + + // FIXME enterGlobalScope + self.runtime.enterGlobalScope() + defer func() { + self.runtime.leaveScope() + }() + + if !construct && this == nil { + program, err := self.runtime.cmpl_parse("", source+"()", nil) + if err == nil { + if node, ok := program.body[0].(*_nodeExpressionStatement); ok { + if node, ok := node.expression.(*_nodeCallExpression); ok { + var value Value + err := catchPanic(func() { + value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList) + }) + if err != nil { + return Value{}, err + } + return value, nil + } + } + } + } else { + value, err := self.ToValue(this) + if err != nil { + return Value{}, err + } + thisValue = value + } + + { + this := thisValue + + fn, err := self.Run(source) + if err != nil { + return Value{}, err + } + + if construct { + result, err := fn.constructSafe(self.runtime, this, argumentList...) + if err != nil { + return Value{}, err + } + return result, nil + } + + result, err := fn.Call(this, argumentList...) + if err != nil { + return Value{}, err + } + return result, nil + } +} + +// Object will run the given source and return the result as an object. +// +// For example, accessing an existing object: +// +// object, _ := vm.Object(`Number`) +// +// Or, creating a new object: +// +// object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`) +// +// Or, creating and assigning an object: +// +// object, _ := vm.Object(`xyzzy = {}`) +// object.Set("volume", 11) +// +// If there is an error (like the source does not result in an object), then +// nil and an error is returned. +func (self Otto) Object(source string) (*Object, error) { + value, err := self.runtime.cmpl_run(source, nil) + if err != nil { + return nil, err + } + if value.IsObject() { + return value.Object(), nil + } + return nil, fmt.Errorf("value is not an object") +} + +// ToValue will convert an interface{} value to a value digestible by otto/JavaScript. +func (self Otto) ToValue(value interface{}) (Value, error) { + return self.runtime.safeToValue(value) +} + +// Copy will create a copy/clone of the runtime. +// +// Copy is useful for saving some time when creating many similar runtimes. +// +// This method works by walking the original runtime and cloning each object, scope, stash, +// etc. into a new runtime. +// +// Be on the lookout for memory leaks or inadvertent sharing of resources. +func (in *Otto) Copy() *Otto { + out := &Otto{ + runtime: in.runtime.clone(), + } + out.runtime.otto = out + return out +} + +// Object{} + +// Object is the representation of a JavaScript object. +type Object struct { + object *_object + value Value +} + +func _newObject(object *_object, value Value) *Object { + // value MUST contain object! + return &Object{ + object: object, + value: value, + } +} + +// Call a method on the object. +// +// It is essentially equivalent to: +// +// var method, _ := object.Get(name) +// method.Call(object, argumentList...) +// +// An undefined value and an error will result if: +// +// 1. There is an error during conversion of the argument list +// 2. The property is not actually a function +// 3. An (uncaught) exception is thrown +// +func (self Object) Call(name string, argumentList ...interface{}) (Value, error) { + // TODO: Insert an example using JavaScript below... + // e.g., Object("JSON").Call("stringify", ...) + + function, err := self.Get(name) + if err != nil { + return Value{}, err + } + return function.Call(self.Value(), argumentList...) +} + +// Value will return self as a value. +func (self Object) Value() Value { + return self.value +} + +// Get the value of the property with the given name. +func (self Object) Get(name string) (Value, error) { + value := Value{} + err := catchPanic(func() { + value = self.object.get(name) + }) + if !value.safe() { + value = Value{} + } + return value, err +} + +// Set the property of the given name to the given value. +// +// An error will result if the setting the property triggers an exception (i.e. read-only), +// or there is an error during conversion of the given value. +func (self Object) Set(name string, value interface{}) error { + { + value, err := self.object.runtime.safeToValue(value) + if err != nil { + return err + } + err = catchPanic(func() { + self.object.put(name, value, true) + }) + return err + } +} + +// Keys gets the keys for the given object. +// +// Equivalent to calling Object.keys on the object. +func (self Object) Keys() []string { + var keys []string + self.object.enumerate(false, func(name string) bool { + keys = append(keys, name) + return true + }) + return keys +} + +// KeysByParent gets the keys (and those of the parents) for the given object, +// in order of "closest" to "furthest". +func (self Object) KeysByParent() [][]string { + var a [][]string + + for o := self.object; o != nil; o = o.prototype { + var l []string + + o.enumerate(false, func(name string) bool { + l = append(l, name) + return true + }) + + a = append(a, l) + } + + return a +} + +// Class will return the class string of the object. +// +// The return value will (generally) be one of: +// +// Object +// Function +// Array +// String +// Number +// Boolean +// Date +// RegExp +// +func (self Object) Class() string { + return self.object.class +} diff --git a/vendor/github.com/robertkrimen/otto/otto/Makefile b/vendor/github.com/robertkrimen/otto/otto/Makefile new file mode 100644 index 00000000..246738f9 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/otto/Makefile @@ -0,0 +1,4 @@ +.PHONY: build + +build: + go build -a diff --git a/vendor/github.com/robertkrimen/otto/otto/main.go b/vendor/github.com/robertkrimen/otto/otto/main.go new file mode 100644 index 00000000..f379e42a --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/otto/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + + "github.com/robertkrimen/otto" + "github.com/robertkrimen/otto/underscore" +) + +var flag_underscore *bool = flag.Bool("underscore", true, "Load underscore into the runtime environment") + +func readSource(filename string) ([]byte, error) { + if filename == "" || filename == "-" { + return ioutil.ReadAll(os.Stdin) + } + return ioutil.ReadFile(filename) +} + +func main() { + flag.Parse() + + if !*flag_underscore { + underscore.Disable() + } + + err := func() error { + src, err := readSource(flag.Arg(0)) + if err != nil { + return err + } + + vm := otto.New() + _, err = vm.Run(src) + return err + }() + if err != nil { + switch err := err.(type) { + case *otto.Error: + fmt.Print(err.String()) + default: + fmt.Println(err) + } + os.Exit(64) + } +} diff --git a/vendor/github.com/robertkrimen/otto/otto_.go b/vendor/github.com/robertkrimen/otto/otto_.go new file mode 100644 index 00000000..304a8315 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/otto_.go @@ -0,0 +1,178 @@ +package otto + +import ( + "fmt" + "regexp" + runtime_ "runtime" + "strconv" + "strings" +) + +var isIdentifier_Regexp *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z\$][a-zA-Z0-9\$]*$`) + +func isIdentifier(string_ string) bool { + return isIdentifier_Regexp.MatchString(string_) +} + +func (self *_runtime) toValueArray(arguments ...interface{}) []Value { + length := len(arguments) + if length == 1 { + if valueArray, ok := arguments[0].([]Value); ok { + return valueArray + } + return []Value{self.toValue(arguments[0])} + } + + valueArray := make([]Value, length) + for index, value := range arguments { + valueArray[index] = self.toValue(value) + } + + return valueArray +} + +func stringToArrayIndex(name string) int64 { + index, err := strconv.ParseInt(name, 10, 64) + if err != nil { + return -1 + } + if index < 0 { + return -1 + } + if index >= maxUint32 { + // The value 2^32 (or above) is not a valid index because + // you cannot store a uint32 length for an index of uint32 + return -1 + } + return index +} + +func isUint32(value int64) bool { + return value >= 0 && value <= maxUint32 +} + +func arrayIndexToString(index int64) string { + return strconv.FormatInt(index, 10) +} + +func valueOfArrayIndex(array []Value, index int) Value { + value, _ := getValueOfArrayIndex(array, index) + return value +} + +func getValueOfArrayIndex(array []Value, index int) (Value, bool) { + if index >= 0 && index < len(array) { + value := array[index] + if !value.isEmpty() { + return value, true + } + } + return Value{}, false +} + +// A range index can be anything from 0 up to length. It is NOT safe to use as an index +// to an array, but is useful for slicing and in some ECMA algorithms. +func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int64 { + index := indexValue.number().int64 + if negativeIsZero { + if index < 0 { + index = 0 + } + // minimum(index, length) + if index >= length { + index = length + } + return index + } + + if index < 0 { + index += length + if index < 0 { + index = 0 + } + } else { + if index > length { + index = length + } + } + return index +} + +func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end int64) { + start = valueToRangeIndex(valueOfArrayIndex(array, 0), size, negativeIsZero) + if len(array) == 1 { + // If there is only the start argument, then end = size + end = size + return + } + + // Assuming the argument is undefined... + end = size + endValue := valueOfArrayIndex(array, 1) + if !endValue.IsUndefined() { + // Which it is not, so get the value as an array index + end = valueToRangeIndex(endValue, size, negativeIsZero) + } + return +} + +func rangeStartLength(source []Value, size int64) (start, length int64) { + start = valueToRangeIndex(valueOfArrayIndex(source, 0), size, false) + + // Assume the second argument is missing or undefined + length = int64(size) + if len(source) == 1 { + // If there is only the start argument, then length = size + return + } + + lengthValue := valueOfArrayIndex(source, 1) + if !lengthValue.IsUndefined() { + // Which it is not, so get the value as an array index + length = lengthValue.number().int64 + } + return +} + +func boolFields(input string) (result map[string]bool) { + result = map[string]bool{} + for _, word := range strings.Fields(input) { + result[word] = true + } + return result +} + +func hereBeDragons(arguments ...interface{}) string { + pc, _, _, _ := runtime_.Caller(1) + name := runtime_.FuncForPC(pc).Name() + message := fmt.Sprintf("Here be dragons -- %s", name) + if len(arguments) > 0 { + message += ": " + argument0 := fmt.Sprintf("%s", arguments[0]) + if len(arguments) == 1 { + message += argument0 + } else { + message += fmt.Sprintf(argument0, arguments[1:]...) + } + } else { + message += "." + } + return message +} + +func throwHereBeDragons(arguments ...interface{}) { + panic(hereBeDragons(arguments...)) +} + +func eachPair(list []interface{}, fn func(_0, _1 interface{})) { + for len(list) > 0 { + var _0, _1 interface{} + _0 = list[0] + list = list[1:] // Pop off first + if len(list) > 0 { + _1 = list[0] + list = list[1:] // Pop off second + } + fn(_0, _1) + } +} diff --git a/vendor/github.com/robertkrimen/otto/otto_error_test.go b/vendor/github.com/robertkrimen/otto/otto_error_test.go new file mode 100644 index 00000000..5ce35881 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/otto_error_test.go @@ -0,0 +1,48 @@ +package otto + +import ( + "testing" +) + +func TestOttoError(t *testing.T) { + tt(t, func() { + vm := New() + + _, err := vm.Run(`throw "Xyzzy"`) + is(err, "Xyzzy") + + _, err = vm.Run(`throw new TypeError()`) + is(err, "TypeError") + + _, err = vm.Run(`throw new TypeError("Nothing happens.")`) + is(err, "TypeError: Nothing happens.") + + _, err = ToValue([]byte{}) + is(err, "TypeError: invalid value (slice): missing runtime: [] ([]uint8)") + + _, err = vm.Run(` + (function(){ + return abcdef.length + })() + `) + is(err, "ReferenceError: 'abcdef' is not defined") + + _, err = vm.Run(` + function start() { + } + + start() + + xyzzy() + `) + is(err, "ReferenceError: 'xyzzy' is not defined") + + _, err = vm.Run(` + // Just a comment + + xyzzy + `) + is(err, "ReferenceError: 'xyzzy' is not defined") + + }) +} diff --git a/vendor/github.com/robertkrimen/otto/otto_test.go b/vendor/github.com/robertkrimen/otto/otto_test.go new file mode 100644 index 00000000..ad70a757 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/otto_test.go @@ -0,0 +1,1749 @@ +package otto + +import ( + "bytes" + "io" + "testing" + + "github.com/robertkrimen/otto/parser" +) + +func TestOtto(t *testing.T) { + tt(t, func() { + test, _ := test() + + test("xyzzy = 2", 2) + + test("xyzzy + 2", 4) + + test("xyzzy += 16", 18) + + test("xyzzy", 18) + + test(` + (function(){ + return 1 + })() + `, 1) + + test(` + (function(){ + return 1 + }).call(this) + `, 1) + + test(` + (function(){ + var result + (function(){ + result = -1 + })() + return result + })() + `, -1) + + test(` + var abc = 1 + abc || (abc = -1) + abc + `, 1) + + test(` + var abc = (function(){ 1 === 1 })(); + abc; + `, "undefined") + }) +} + +func TestFunction__(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + function abc() { + return 1; + }; + abc(); + `, 1) + }) +} + +func TestIf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = undefined; + def = undefined; + if (true) abc = 1 + else abc = 2; + if (false) { + def = 3; + } + else def = 4; + + [ abc, def ]; + `, "1,4") + + test(` + if (1) { + abc = 1; + } + else { + abc = 0; + } + abc; + `, 1) + + test(` + if (0) { + abc = 1; + } + else { + abc = 0; + } + abc; + `, 0) + + test(` + abc = 0; + if (0) { + abc = 1; + } + abc; + `, 0) + + test(` + abc = 0; + if (abc) { + abc = 1; + } + abc; + `, 0) + }) +} + +func TestSequence(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + 1, 2, 3; + `, 3) + }) +} + +func TestCall(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + Math.pow(3, 2); + `, 9) + }) +} + +func TestRunFunctionWithSetArguments(t *testing.T) { + tt(t, func() { + vm := New() + vm.Run(`var sillyFunction = function(record){record.silly = true; record.answer *= -1};`) + record := map[string]interface{}{"foo": "bar", "answer": 42} + // Set performs a conversion that allows the map to be addressed as a Javascript object + vm.Set("argument", record) + _, err := vm.Run("sillyFunction(argument)") + + is(err, nil) + is(record["answer"].(float64), -42) + is(record["silly"].(bool), true) + }) +} + +func TestRunFunctionWithArgumentsPassedToCall(t *testing.T) { + tt(t, func() { + vm := New() + vm.Run(`var sillyFunction = function(record){record.silly = true; record.answer *= -1};`) + record := map[string]interface{}{"foo": "bar", "answer": 42} + _, err := vm.Call("sillyFunction", nil, record) + + is(err, nil) + is(record["answer"].(float64), -42) + is(record["silly"].(bool), true) + }) +} + +func TestMember(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = [ 0, 1, 2 ]; + def = { + "abc": 0, + "def": 1, + "ghi": 2, + }; + [ abc[2], def.abc, abc[1], def.def ]; + `, "2,0,1,1") + }) +} + +func Test_this(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + typeof this; + `, "object") + }) +} + +func TestWhile(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + limit = 4 + abc = 0 + while (limit) { + abc = abc + 1 + limit = limit - 1 + } + abc; + `, 4) + }) +} + +func TestSwitch_break(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = true; + var ghi = "Xyzzy"; + while (abc) { + switch ('def') { + case 'def': + break; + } + ghi = "Nothing happens."; + abc = false; + } + ghi; + `, "Nothing happens.") + + test(` + var abc = true; + var ghi = "Xyzzy"; + WHILE: + while (abc) { + switch ('def') { + case 'def': + break WHILE; + } + ghi = "Nothing happens." + abc = false + } + ghi; + `, "Xyzzy") + + test(` + var ghi = "Xyzzy"; + FOR: + for (;;) { + switch ('def') { + case 'def': + break FOR; + ghi = ""; + } + ghi = "Nothing happens."; + } + ghi; + `, "Xyzzy") + + test(` + var ghi = "Xyzzy"; + FOR: + for (var jkl in {}) { + switch ('def') { + case 'def': + break FOR; + ghi = "Something happens."; + } + ghi = "Nothing happens."; + } + ghi; + `, "Xyzzy") + + test(` + var ghi = "Xyzzy"; + function jkl() { + switch ('def') { + case 'def': + break; + ghi = ""; + } + ghi = "Nothing happens."; + } + while (abc) { + jkl(); + abc = false; + ghi = "Something happens."; + } + ghi; + `, "Something happens.") + }) +} + +func TestTryFinally(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc; + try { + abc = 1; + } + finally { + abc = 2; + } + abc; + `, 2) + + test(` + var abc = false, def = 0; + do { + def += 1; + if (def > 100) { + break; + } + try { + continue; + } + finally { + abc = true; + } + } + while(!abc && def < 10) + def; + `, 1) + + test(` + var abc = false, def = 0, ghi = 0; + do { + def += 1; + if (def > 100) { + break; + } + try { + throw 0; + } + catch (jkl) { + continue; + } + finally { + abc = true; + ghi = 11; + } + ghi -= 1; + } + while(!abc && def < 10) + ghi; + `, 11) + + test(` + var abc = 0, def = 0; + do { + try { + abc += 1; + throw "ghi"; + } + finally { + def = 1; + continue; + } + def -= 1; + } + while (abc < 2) + [ abc, def ]; + `, "2,1") + }) +} + +func TestTryCatch(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 1; + try { + throw 4; + abc = -1; + } + catch (xyzzy) { + abc += xyzzy + 1; + } + abc; + `, 6) + + test(` + abc = 1; + var def; + try { + try { + throw 4; + abc = -1; + } + catch (xyzzy) { + abc += xyzzy + 1; + throw 64; + } + } + catch (xyzzy) { + def = xyzzy; + abc = -2; + } + [ def, abc ]; + `, "64,-2") + }) +} + +func TestWith(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var def; + with({ abc: 9 }) { + def = abc; + } + def; + `, 9) + + test(` + var def; + with({ abc: function(){ + return 11; + } }) { + def = abc(); + } + def; + `, 11) + }) +} + +func TestSwitch(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0; + switch (0) { + default: + abc += 1; + case 1: + abc += 2; + case 2: + abc += 4; + case 3: + abc += 8; + } + abc; + `, 15) + + test(` + abc = 0; + switch (3) { + default: + abc += 1; + case 1: + abc += 2; + case 2: + abc += 4; + case 3: + abc += 8; + } + abc; + `, 8) + + test(` + abc = 0; + switch (60) { + case 1: + abc += 2; + case 2: + abc += 4; + case 3: + abc += 8; + } + abc; + `, 0) + }) +} + +func TestForIn(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc; + for (property in { a: 1 }) { + abc = property; + } + abc; + `, "a") + + test(` + var ghi; + for (property in new String("xyzzy")) { + ghi = property; + } + ghi; + `, "4") + }) +} + +func TestFor(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 7; + for (i = 0; i < 3; i += 1) { + abc += 1; + } + abc; + `, 10) + + test(` + abc = 7; + for (i = 0; i < 3; i += 1) { + abc += 1; + if (i == 1) { + break; + } + } + abc; + `, 9) + + test(` + abc = 7; + for (i = 0; i < 3; i += 1) { + if (i == 2) { + continue; + } + abc += 1; + } + abc; + `, 9) + + test(` + abc = 0; + for (;;) { + abc += 1; + if (abc == 3) + break; + } + abc; + `, 3) + + test(` + for (abc = 0; ;) { + abc += 1; + if (abc == 3) + break; + } + abc; + `, 3) + + test(` + for (abc = 0; ; abc += 1) { + abc += 1; + if (abc == 3) + break; + } + abc; + `, 3) + }) +} + +func TestLabelled(t *testing.T) { + tt(t, func() { + test, _ := test() + + // TODO Add emergency break + + test(` + xyzzy: for (var abc = 0; abc <= 0; abc++) { + for (var def = 0; def <= 1; def++) { + if (def === 0) { + continue xyzzy; + } else { + } + } + } + `) + + test(` + abc = 0 + def: + while (true) { + while (true) { + abc = abc + 1 + if (abc > 11) { + break def; + } + } + } + abc; + `, 12) + + test(` + abc = 0 + def: + do { + do { + abc = abc + 1 + if (abc > 11) { + break def; + } + } while (true) + } while (true) + abc; + `, 12) + }) +} + +func TestConditional(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ true ? false : true, true ? 1 : 0, false ? 3.14159 : "abc" ]; + `, "false,1,abc") + }) +} + +func TestArrayLiteral(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ 1, , 3.14159 ]; + `, "1,,3.14159") + }) +} + +func TestAssignment(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 1; + abc; + `, 1) + + test(` + abc += 2; + abc; + `, 3) + }) +} + +func TestBinaryOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`0 == 1`, false) + test(`1 == "1"`, true) + test(`0 === 1`, false) + test(`1 === "1"`, false) + test(`"1" === "1"`, true) + }) +} + +func Test_typeof(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`typeof abc`, "undefined") + test(`typeof abc === 'undefined'`, true) + test(`typeof {}`, "object") + test(`typeof null`, "object") + }) +} + +func Test_PrimitiveValueObjectValue(t *testing.T) { + tt(t, func() { + test, _ := test() + + Number11 := test(`new Number(11)`) + is(Number11.float64(), 11) + }) +} + +func Test_eval(t *testing.T) { + tt(t, func() { + test, _ := test() + + // FIXME terst, Is this correct? + test(` + var abc = 1; + `, "undefined") + + test(` + eval("abc += 1"); + `, 2) + + test(` + (function(){ + var abc = 11; + eval("abc += 1"); + return abc; + })(); + `, 12) + test(`abc`, 2) + + test(` + (function(){ + try { + eval("var prop = \\u2029;"); + return false; + } catch (abc) { + return [ abc instanceof SyntaxError, abc.toString() ]; + } + })(); + `, "true,SyntaxError: Unexpected token ILLEGAL") + + test(` + function abc(){ + this.THIS = eval("this"); + } + var def = new abc(); + def === def.THIS; + `, true) + }) +} + +func Test_evalDirectIndirect(t *testing.T) { + tt(t, func() { + test, _ := test() + + // (function () {return this;}()).abc = "global"; + test(` + var abc = "global"; + (function(){ + try { + var _eval = eval; + var abc = "function"; + return [ + _eval("\'global\' === abc"), // eval (Indirect) + eval("\'function\' === abc"), // eval (Direct) + ]; + } finally { + delete this.abc; + } + })(); + `, "true,true") + }) +} + +func TestError_URIError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`new URIError() instanceof URIError`, true) + + test(` + var abc + try { + decodeURI("http://example.com/ _^#%") + } + catch (def) { + abc = def instanceof URIError + } + abc + `, true) + }) +} + +func TestTo(t *testing.T) { + tt(t, func() { + test, _ := test() + + { + value, _ := test(`"11"`).ToFloat() + is(value, float64(11)) + } + + { + value, _ := test(`"11"`).ToInteger() + is(value, int64(11)) + + value, _ = test(`1.1`).ToInteger() + is(value, int64(1)) + } + }) +} + +func TestShouldError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`raise: + xyzzy + throw new TypeError("Nothing happens.") + `, "ReferenceError: 'xyzzy' is not defined") + }) +} + +func TestAPI(t *testing.T) { + tt(t, func() { + test, vm := test() + + test(` + String.prototype.xyzzy = function(){ + return this.length + 11 + (arguments[0] || 0) + } + abc = new String("xyzzy") + def = "Nothing happens." + abc.xyzzy() + `, 16) + abc, _ := vm.Get("abc") + def, _ := vm.Get("def") + object := abc.Object() + result, _ := object.Call("xyzzy") + is(result, 16) + result, _ = object.Call("xyzzy", 1) + is(result, 17) + value, _ := object.Get("xyzzy") + result, _ = value.Call(def) + is(result, 27) + result, _ = value.Call(def, 3) + is(result, 30) + object = value.Object() // Object xyzzy + result, _ = object.Value().Call(def, 3) + is(result, 30) + + test(` + abc = { + 'abc': 1, + 'def': false, + 3.14159: NaN, + }; + abc['abc']; + `, 1) + abc, err := vm.Get("abc") + is(err, nil) + object = abc.Object() // Object abc + value, err = object.Get("abc") + is(err, nil) + is(value, 1) + is(object.Keys(), []string{"abc", "def", "3.14159"}) + + test(` + abc = [ 0, 1, 2, 3.14159, "abc", , ]; + abc.def = true; + `) + abc, err = vm.Get("abc") + is(err, nil) + object = abc.Object() // Object abc + is(object.Keys(), []string{"0", "1", "2", "3", "4", "def"}) + }) +} + +func TestObjectKeys(t *testing.T) { + tt(t, func() { + vm := New() + vm.Eval(`var x = Object.create(null); x.a = 1`) + vm.Eval(`var y = Object.create(x); y.b = 2`) + + o1, _ := vm.Object("x") + is(o1.Keys(), []string{"a"}) + is(o1.KeysByParent(), [][]string{{"a"}}) + + o2, _ := vm.Object("y") + is(o2.Keys(), []string{"b"}) + is(o2.KeysByParent(), [][]string{{"b"}, {"a"}}) + }) +} + +func TestUnicode(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`var abc = eval("\"a\uFFFFa\"");`, "undefined") + + test(`abc.length`, 3) + + test(`abc != "aa"`, true) + + test("abc[1] === \"\uFFFF\"", true) + }) +} + +func TestDotMember(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = { + ghi: 11, + } + abc.def = "Xyzzy" + abc.null = "Nothing happens." + `) + test(`abc.def`, "Xyzzy") + test(`abc.null`, "Nothing happens.") + test(`abc.ghi`, 11) + + test(` + abc = { + null: 11, + } + `) + test(`abc.def`, "undefined") + test(`abc.null`, 11) + test(`abc.ghi`, "undefined") + }) +} + +func Test_stringToFloat(t *testing.T) { + tt(t, func() { + + is(parseNumber("10e10000"), _Infinity) + is(parseNumber("10e10_."), _NaN) + }) +} + +func Test_delete(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + delete 42; + `, true) + + test(` + var abc = delete $_undefined_$; + abc = abc && delete ($_undefined_$); + abc; + `, true) + + // delete should not trigger get() + test(` + var abc = { + get def() { + throw "Test_delete: delete should not trigger get()" + } + }; + delete abc.def + `, true) + }) +} + +func TestObject_defineOwnProperty(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var object = {}; + + var descriptor = new Boolean(false); + descriptor.configurable = true; + + Object.defineProperties(object, { + property: descriptor + }); + + var abc = object.hasOwnProperty("property"); + delete object.property; + var def = object.hasOwnProperty("property"); + + [ abc, def ]; + `, "true,false") + + test(` + var object = [0, 1, 2]; + Object.defineProperty(object, "0", { + value: 42, + writable: false, + enumerable: false, + configurable: false + }); + var abc = Object.getOwnPropertyDescriptor(object, "0"); + [ abc.value, abc.writable, abc.enumerable, abc.configurable ]; + `, "42,false,false,false") + + test(` + var abc = { "xyzzy": 42 }; + var def = Object.defineProperties(abc, ""); + abc === def; + `, true) + }) +} + +func Test_assignmentEvaluationOrder(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0; + ((abc = 1) & abc); + `, 1) + + test(` + var abc = 0; + (abc & (abc = 1)); + `, 0) + }) +} + +func TestOttoCall(t *testing.T) { + tt(t, func() { + vm := New() + + _, err := vm.Run(` + var abc = { + ghi: 1, + def: function(def){ + var ghi = 0; + if (this.ghi) { + ghi = this.ghi; + } + return "def: " + (def + 3.14159 + ghi); + } + }; + function structFunc(s) { + return s.Val; + } + `) + is(err, nil) + + value, err := vm.Call(`abc.def`, nil, 2) + is(err, nil) + is(value, "def: 6.14159") + + value, err = vm.Call(`abc.def`, "", 2) + is(err, nil) + is(value, "def: 5.14159") + + // Do not attempt to do a ToValue on a this of nil + value, err = vm.Call(`jkl.def`, nil, 1, 2, 3) + is(err, "!=", nil) + is(value, "undefined") + + value, err = vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") + is(err, nil) + is(value, "1,2,3,,4,5,6,7,abc") + + s := struct{ Val int }{Val: 10} + value, err = vm.Call("structFunc", nil, s) + is(err, nil) + is(value, 10) + }) +} + +func TestOttoCall_new(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("abc", func(call FunctionCall) Value { + value, err := call.Otto.Call(`new Object`, nil, "Nothing happens.") + is(err, nil) + return value + }) + test(` + def = abc(); + [ def, def instanceof String ]; + `, "Nothing happens.,true") + }) +} + +func TestOttoCall_newWithBrackets(t *testing.T) { + tt(t, func() { + test, vm := test() + + _, err := vm.Run(`var a = {default: function B(x) { this.x = x; } }`) + is(err, nil) + + test(`(new a['default'](1)).x`, 1) + }) +} + +func TestOttoCall_throw(t *testing.T) { + // FIXME? (Been broken for a while) + // Looks like this has been broken for a while... what + // behavior do we want here? + + if true { + return + } + + tt(t, func() { + test, vm := test() + + vm.Set("abc", func(call FunctionCall) Value { + if false { + call.Otto.Call(`throw eval`, nil, "({ def: 3.14159 })") + } + call.Otto.Call(`throw Error`, nil, "abcdef") + return Value{} + }) + // TODO try { abc(); } catch (err) { error = err } + // Possible unrelated error case: + // If error is not declared beforehand, is later referencing it a ReferenceError? + // Should the catch { } declare error in the outer scope? + test(` + var error; + try { + abc(); + } + catch (err) { + error = err; + } + [ error instanceof Error, error.message, error.def ]; + `, "true,abcdef,") + + vm.Set("def", func(call FunctionCall) Value { + call.Otto.Call(`throw new Object`, nil, 3.14159) + return UndefinedValue() + }) + test(` + try { + def(); + } + catch (err) { + error = err; + } + [ error instanceof Error, error.message, error.def, typeof error, error, error instanceof Number ]; + `, "false,,,object,3.14159,true") + }) +} + +func TestOttoCopy(t *testing.T) { + tt(t, func() { + vm0 := New() + vm0.Run(` + var abc = function() { + return "Xyzzy"; + }; + + function def() { + return abc() + (0 + {}); + } + `) + + value, err := vm0.Run(` + def(); + `) + is(err, nil) + is(value, "Xyzzy0[object Object]") + + vm1 := vm0.Copy() + value, err = vm1.Run(` + def(); + `) + is(err, nil) + is(value, "Xyzzy0[object Object]") + + vm1.Run(` + abc = function() { + return 3.14159; + }; + `) + value, err = vm1.Run(` + def(); + `) + is(err, nil) + is(value, "3.141590[object Object]") + + value, err = vm0.Run(` + def(); + `) + is(err, nil) + is(value, "Xyzzy0[object Object]") + + { + vm0 := New() + vm0.Run(` + var global = (function () {return this;}()) + var abc = 0; + var vm = "vm0"; + + var def = (function(){ + var jkl = 0; + var abc = function() { + global.abc += 1; + jkl += 1; + return 1; + }; + + return function() { + return [ vm, global.abc, jkl, abc() ]; + }; + })(); + `) + + value, err := vm0.Run(` + def(); + `) + is(err, nil) + is(value, "vm0,0,0,1") + + vm1 := vm0.Copy() + vm1.Set("vm", "vm1") + value, err = vm1.Run(` + def(); + `) + is(err, nil) + is(value, "vm1,1,1,1") + + value, err = vm0.Run(` + def(); + `) + is(err, nil) + is(value, "vm0,1,1,1") + + value, err = vm1.Run(` + def(); + `) + is(err, nil) + is(value, "vm1,2,2,1") + } + }) +} + +func TestOttoCall_clone(t *testing.T) { + tt(t, func() { + vm := New().clone() + rt := vm.runtime + + { + // FIXME terst, Check how this comparison is done + is(rt.global.Array.prototype, rt.global.FunctionPrototype) + is(rt.global.ArrayPrototype, "!=", nil) + is(rt.global.Array.runtime, rt) + is(rt.global.Array.prototype.runtime, rt) + is(rt.global.Array.get("prototype")._object().runtime, rt) + } + + { + value, err := vm.Run(`[ 1, 2, 3 ].toString()`) + is(err, nil) + is(value, "1,2,3") + } + + { + value, err := vm.Run(`[ 1, 2, 3 ]`) + is(err, nil) + is(value, "1,2,3") + object := value._object() + is(object, "!=", nil) + is(object.prototype, rt.global.ArrayPrototype) + + value, err = vm.Run(`Array.prototype`) + is(err, nil) + object = value._object() + is(object.runtime, rt) + is(object, "!=", nil) + is(object, rt.global.ArrayPrototype) + } + + { + otto1 := New() + _, err := otto1.Run(` + var abc = 1; + var def = 2; + `) + is(err, nil) + + otto2 := otto1.clone() + value, err := otto2.Run(`abc += 1; abc;`) + is(err, nil) + is(value, 2) + + value, err = otto1.Run(`abc += 4; abc;`) + is(err, nil) + is(value, 5) + } + + { + vm1 := New() + _, err := vm1.Run(` + var abc = 1; + var def = function(value) { + abc += value; + return abc; + } + `) + is(err, nil) + + vm2 := vm1.clone() + value, err := vm2.Run(`def(1)`) + is(err, nil) + is(value, 2) + + value, err = vm1.Run(`def(4)`) + is(err, nil) + is(value, 5) + } + + { + vm1 := New() + _, err := vm1.Run(` + var abc = { + ghi: 1, + jkl: function(value) { + this.ghi += value; + return this.ghi; + } + }; + var def = { + abc: abc + }; + `) + is(err, nil) + + otto2 := vm1.clone() + value, err := otto2.Run(`def.abc.jkl(1)`) + is(err, nil) + is(value, 2) + + value, err = vm1.Run(`def.abc.jkl(4)`) + is(err, nil) + is(value, 5) + } + + { + vm1 := New() + _, err := vm1.Run(` + var abc = function() { return "abc"; }; + var def = function() { return "def"; }; + `) + is(err, nil) + + vm2 := vm1.clone() + value, err := vm2.Run(` + [ abc.toString(), def.toString() ]; + `) + is(value, `function() { return "abc"; },function() { return "def"; }`) + + _, err = vm2.Run(` + var def = function() { return "ghi"; }; + `) + is(err, nil) + + value, err = vm1.Run(` + [ abc.toString(), def.toString() ]; + `) + is(value, `function() { return "abc"; },function() { return "def"; }`) + + value, err = vm2.Run(` + [ abc.toString(), def.toString() ]; + `) + is(value, `function() { return "abc"; },function() { return "ghi"; }`) + } + + }) +} + +func TestOttoRun(t *testing.T) { + tt(t, func() { + vm := New() + + program, err := parser.ParseFile(nil, "", "", 0) + is(err, nil) + value, err := vm.Run(program) + is(err, nil) + is(value, UndefinedValue()) + + program, err = parser.ParseFile(nil, "", "2 + 2", 0) + is(err, nil) + value, err = vm.Run(program) + is(err, nil) + is(value, 4) + value, err = vm.Run(program) + is(err, nil) + is(value, 4) + + program, err = parser.ParseFile(nil, "", "var abc; if (!abc) abc = 0; abc += 2; abc;", 0) + value, err = vm.Run(program) + is(err, nil) + is(value, 2) + value, err = vm.Run(program) + is(err, nil) + is(value, 4) + value, err = vm.Run(program) + is(err, nil) + is(value, 6) + + { + src := []byte("var abc; if (!abc) abc = 0; abc += 2; abc;") + value, err = vm.Run(src) + is(err, nil) + is(value, 8) + + value, err = vm.Run(bytes.NewBuffer(src)) + is(err, nil) + is(value, 10) + + value, err = vm.Run(io.Reader(bytes.NewBuffer(src))) + is(err, nil) + is(value, 12) + } + + { + script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) + is(err, nil) + + value, err = vm.Run(script) + is(err, nil) + is(value, 14) + + value, err = vm.Run(script) + is(err, nil) + is(value, 16) + + is(script.String(), "// \nvar abc; if (!abc) abc = 0; abc += 2; abc;") + } + }) +} + +// This generates functions to be used by the test below. The arguments are +// `src`, which is something that otto can execute, and `expected`, which is +// what the result of executing `src` should be. +func makeTestOttoEvalFunction(src, expected interface{}) func(c FunctionCall) Value { + return func(c FunctionCall) Value { + v, err := c.Otto.Eval(src) + is(err, nil) + if err != nil { + panic(err) + } + + i, err := v.Export() + is(err, nil) + if err != nil { + panic(err) + } + + is(i, expected) + + return v + } +} + +func TestOttoEval(t *testing.T) { + tt(t, func() { + vm := New() + + vm.Set("x1", makeTestOttoEvalFunction(`a`, 1)) + vm.Set("y1", makeTestOttoEvalFunction(`b`, "hello")) + vm.Set("z1", makeTestOttoEvalFunction(`c`, true)) + vm.Set("w", makeTestOttoEvalFunction(`a = 2; b = 'what'; c = false; null`, nil)) + vm.Set("x2", makeTestOttoEvalFunction(`a`, 2)) + vm.Set("y2", makeTestOttoEvalFunction(`b`, "what")) + vm.Set("z2", makeTestOttoEvalFunction(`c`, false)) + + // note that these variables are defined in the scope of function `t`, + // so would not usually be available to the functions called below. + // + // this is _not_ the recommended use case for `Eval` - instead it's + // intended to be used in `debugger` handlers. this code here is the + // equivalent of reading behind the current stack frame in C... + // technically valid, but completely insane. + // + // makes for a good test case though. + _, err := vm.Run(`(function t() { + var a = 1; + var b = 'hello'; + var c = true; + + x1(); + y1(); + z1(); + w(); + x2(); + y2(); + z2(); + }())`) + + is(err, nil) + }) + + // this test makes sure that `Eval` doesn't explode if the VM doesn't have + // a scope other than global defined. + tt(t, func() { + vm := New() + + _, err := vm.Eval("null") + is(err, nil) + + vm.Set("a", 1) + vm.Set("b", 2) + + v, err := vm.Eval("a + b") + is(err, nil) + r, err := v.Export() + is(err, nil) + is(r, 3) + }) +} + +func TestOttoContext(t *testing.T) { + // These are all the builtin global scope symbols + builtins := []string{ + "escape", + "URIError", + "RegExp", + "ReferenceError", + "parseFloat", + "parseInt", + "SyntaxError", + "decodeURIComponent", + "encodeURIComponent", + "Infinity", + "JSON", + "isNaN", + "unescape", + "decodeURI", + "Object", + "Function", + "RangeError", + "Error", + "get_context", + "eval", + "Number", + "Math", + "NaN", + "Date", + "Boolean", + "console", + "encodeURI", + "EvalError", + "Array", + "TypeError", + "String", + "isFinite", + "undefined", + } + + tt(t, func() { + vm := New() + + vm.Set("get_context", func(c FunctionCall) Value { + ctx := c.Otto.Context() + is(ctx.Callee, "f1") + is(ctx.Filename, "") + is(ctx.Line, 8) + is(ctx.Column, 5) + is(ctx.Stacktrace, []string{ + "f1 (:8:5)", + "f2 (:15:5)", + "f3 (:19:5)", + "t (:22:4)", + }) + is(len(ctx.Symbols), 9+len(builtins)) + is(ctx.Symbols["a"], 1) + is(ctx.Symbols["b"], "hello") + is(ctx.Symbols["c"], true) + is(ctx.Symbols["j"], 2) + is(ctx.Symbols["f1"].IsFunction(), true) + is(ctx.Symbols["f2"].IsFunction(), true) + is(ctx.Symbols["f3"].IsFunction(), true) + is(ctx.Symbols["t"].IsFunction(), true) + callee, _ := ctx.Symbols["arguments"].Object().Get("callee") + is(callee.IsDefined(), true) + + return Value{} + }) + + _, err := vm.Run(`(function t() { + var a = 1; + var b = 'hello'; + var c = true; + + function f1() { + var j = 2; + get_context(); + (function() { + var d = 4; + })() + } + + function f2() { + f1(); + } + + function f3() { + f2(); + } + + f3(); + + a = 2; + b = 'goodbye'; + c = false; + }())`) + + is(err, nil) + }) + + // this test makes sure that `Context` works on global scope by default, if + // there is not a current scope. + tt(t, func() { + vm := New() + + vm.Set("get_context", func(c FunctionCall) Value { + ctx := c.Otto.Context() + is(ctx.Callee, "") + is(ctx.Filename, "") + is(ctx.Line, 3) + is(ctx.Column, 4) + is(ctx.Stacktrace, []string{":3:4"}) + is(len(ctx.Symbols), 2+len(builtins)) + is(ctx.Symbols["a"], 1) + is(ctx.Symbols["b"], UndefinedValue()) + + return Value{} + }) + + _, err := vm.Run(` + var a = 1; + get_context() + var b = 2; + `) + is(err, nil) + }) + + // this test makes sure variables are shadowed correctly. + tt(t, func() { + vm := New() + + vm.Set("check_context", func(c FunctionCall) Value { + n, err := c.Argument(0).ToInteger() + is(err, nil) + + ctx := c.Otto.Context() + is(ctx.Symbols["a"], n) + + return Value{} + }) + + _, err := vm.Run(` + var a = 1; + check_context(1); + (function() { + var a = 2; + check_context(2); + }()); + (function(a) { + check_context(3); + }(3)); + (function(a) { + check_context(4); + }).call(null, 4); + check_context(1); + `) + is(err, nil) + }) +} + +func Test_objectLength(t *testing.T) { + tt(t, func() { + _, vm := test() + + value := vm.Set("abc", []string{"jkl", "mno"}) + is(objectLength(value._object()), 2) + + value, _ = vm.Run(`[1, 2, 3]`) + is(objectLength(value._object()), 3) + + value, _ = vm.Run(`new String("abcdefghi")`) + is(objectLength(value._object()), 9) + + value, _ = vm.Run(`"abcdefghi"`) + is(objectLength(value._object()), 0) + }) +} + +func Test_stackLimit(t *testing.T) { + // JavaScript stack depth before entering `a` is 5; becomes 6 after + // entering. setting the maximum stack depth to 5 should result in an + // error ocurring at that 5 -> 6 boundary. + code := ` + function a() {} + function b() { a(); } + function c() { b(); } + function d() { c(); } + function e() { d(); } + e(); + ` + + // has no error + tt(t, func() { + _, vm := test() + + _, err := vm.Run(code) + + is(err == nil, true) + }) + + // has error + tt(t, func() { + _, vm := test() + + vm.vm.SetStackDepthLimit(2) + + _, err := vm.Run(code) + + is(err == nil, false) + }) + + // has error + tt(t, func() { + _, vm := test() + + vm.vm.SetStackDepthLimit(5) + + _, err := vm.Run(code) + + is(err == nil, false) + }) + + // has no error + tt(t, func() { + _, vm := test() + + vm.vm.SetStackDepthLimit(6) + + _, err := vm.Run(code) + + is(err == nil, true) + }) + + // has no error + tt(t, func() { + _, vm := test() + + vm.vm.SetStackDepthLimit(1) + vm.vm.SetStackDepthLimit(0) + + _, err := vm.Run(code) + + is(err == nil, true) + }) +} + +func BenchmarkNew(b *testing.B) { + for i := 0; i < b.N; i++ { + New() + } +} + +func BenchmarkClone(b *testing.B) { + vm := New() + b.ResetTimer() + for i := 0; i < b.N; i++ { + vm.clone() + } +} diff --git a/vendor/github.com/robertkrimen/otto/panic_test.go b/vendor/github.com/robertkrimen/otto/panic_test.go new file mode 100644 index 00000000..06f0a64f --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/panic_test.go @@ -0,0 +1,40 @@ +package otto + +import ( + "testing" +) + +func Test_panic(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Test that property.value is set to something if writable is set + // to something + test(` + var abc = []; + Object.defineProperty(abc, "0", { writable: false }); + Object.defineProperty(abc, "0", { writable: false }); + "0" in abc; + `, true) + + test(`raise: + var abc = []; + Object.defineProperty(abc, "0", { writable: false }); + Object.defineProperty(abc, "0", { value: false, writable: false }); + `, "TypeError") + + // Test that a regular expression can contain \c0410 (CYRILLIC CAPITAL LETTER A) + // without panicking + test(` + var abc = 0x0410; + var def = String.fromCharCode(abc); + new RegExp("\\c" + def).exec(def); + `, "null") + + // Test transforming a transformable regular expression without a panic + test(` + new RegExp("\\u0000"); + new RegExp("\\undefined").test("undefined"); + `, true) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/parser/Makefile b/vendor/github.com/robertkrimen/otto/parser/Makefile new file mode 100644 index 00000000..766fd4d0 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/Makefile @@ -0,0 +1,4 @@ +.PHONY: test + +test: + go test diff --git a/vendor/github.com/robertkrimen/otto/parser/README.markdown b/vendor/github.com/robertkrimen/otto/parser/README.markdown new file mode 100644 index 00000000..c3cae5b6 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/README.markdown @@ -0,0 +1,190 @@ +# parser +-- + import "github.com/robertkrimen/otto/parser" + +Package parser implements a parser for JavaScript. + + import ( + "github.com/robertkrimen/otto/parser" + ) + +Parse and return an AST + + filename := "" // A filename is optional + src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); + ` + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, filename, src, 0) + + +### Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +## Usage + +#### func ParseFile + +```go +func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) +``` +ParseFile parses the source code of a single JavaScript/ECMAScript source file +and returns the corresponding ast.Program node. + +If fileSet == nil, ParseFile parses source without a FileSet. If fileSet != nil, +ParseFile first adds filename and src to fileSet. + +The filename argument is optional and is used for labelling errors, etc. + +src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST +always be in UTF-8. + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0) + +#### func ParseFunction + +```go +func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) +``` +ParseFunction parses a given parameter list and body as a function and returns +the corresponding ast.FunctionLiteral node. + +The parameter list, if any, should be a comma-separated list of identifiers. + +#### func ReadSource + +```go +func ReadSource(filename string, src interface{}) ([]byte, error) +``` + +#### func TransformRegExp + +```go +func TransformRegExp(pattern string) (string, error) +``` +TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern. + +re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or +backreference (\1, \2, ...) will cause an error. + +re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript +definition, on the other hand, also includes \v, Unicode "Separator, Space", +etc. + +If the pattern is invalid (not valid even in JavaScript), then this function +returns the empty string and an error. + +If the pattern is valid, but incompatible (contains a lookahead or +backreference), then this function returns the transformation (a non-empty +string) AND an error. + +#### type Error + +```go +type Error struct { + Position file.Position + Message string +} +``` + +An Error represents a parsing error. It includes the position where the error +occurred and a message/description. + +#### func (Error) Error + +```go +func (self Error) Error() string +``` + +#### type ErrorList + +```go +type ErrorList []*Error +``` + +ErrorList is a list of *Errors. + +#### func (*ErrorList) Add + +```go +func (self *ErrorList) Add(position file.Position, msg string) +``` +Add adds an Error with given position and message to an ErrorList. + +#### func (ErrorList) Err + +```go +func (self ErrorList) Err() error +``` +Err returns an error equivalent to this ErrorList. If the list is empty, Err +returns nil. + +#### func (ErrorList) Error + +```go +func (self ErrorList) Error() string +``` +Error implements the Error interface. + +#### func (ErrorList) Len + +```go +func (self ErrorList) Len() int +``` + +#### func (ErrorList) Less + +```go +func (self ErrorList) Less(i, j int) bool +``` + +#### func (*ErrorList) Reset + +```go +func (self *ErrorList) Reset() +``` +Reset resets an ErrorList to no errors. + +#### func (ErrorList) Sort + +```go +func (self ErrorList) Sort() +``` + +#### func (ErrorList) Swap + +```go +func (self ErrorList) Swap(i, j int) +``` + +#### type Mode + +```go +type Mode uint +``` + +A Mode value is a set of flags (or 0). They control optional parser +functionality. + +```go +const ( + IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking) +) +``` + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/robertkrimen/otto/parser/comments_test.go b/vendor/github.com/robertkrimen/otto/parser/comments_test.go new file mode 100644 index 00000000..72d08bb0 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/comments_test.go @@ -0,0 +1,1450 @@ +package parser + +import ( + "fmt" + "github.com/robertkrimen/otto/ast" + "reflect" + "testing" +) + +func checkComments(actual []*ast.Comment, expected []string, position ast.CommentPosition) error { + var comments []*ast.Comment + for _, c := range actual { + if c.Position == position { + comments = append(comments, c) + } + } + + if len(comments) != len(expected) { + return fmt.Errorf("the number of comments is not correct. %v != %v", len(comments), len(expected)) + } + + for i, v := range comments { + if v.Text != expected[i] { + return fmt.Errorf("comments do not match. \"%v\" != \"%v\"\n", v.Text, expected[i]) + } + if v.Position != position { + return fmt.Errorf("the comment is not %v, was %v\n", position, v.Position) + } + } + + return nil +} + +func displayStatements(statements []ast.Statement) { + fmt.Printf("Printing statements (%v)\n", len(statements)) + for k, v := range statements { + fmt.Printf("Type of line %v: %v\n", k, reflect.TypeOf(v)) + } +} + +func displayComments(m ast.CommentMap) { + fmt.Printf("Displaying comments:\n") + for n, comments := range m { + fmt.Printf("%v %v:\n", reflect.TypeOf(n), n) + for i, comment := range comments { + fmt.Printf(" [%v] %v @ %v\n", i, comment.Text, comment.Position) + } + } +} + +func TestParser_comments(t *testing.T) { + tt(t, func() { + test := func(source string, chk interface{}) (*_parser, *ast.Program) { + parser, program, err := testParseWithMode(source, StoreComments) + is(firstErr(err), chk) + + // Check unresolved comments + //is(len(parser.comments), 0) + return parser, program + } + + var err error + var parser *_parser + var program *ast.Program + + parser, program = test("q=2;// Hej\nv = 0", nil) + is(len(program.Body), 2) + err = checkComments((parser.comments.CommentMap)[program.Body[1]], []string{" Hej"}, ast.LEADING) + is(err, nil) + + // Assignment + parser, program = test("i = /*test=*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right], []string{"test="}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Conditional, before consequent + parser, program = test("i ? /*test?*/ 2 : 3", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ConditionalExpression).Consequent], []string{"test?"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Conditional, after consequent + parser, program = test("i ? 2 /*test?*/ : 3", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ConditionalExpression).Consequent], []string{"test?"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Conditional, before alternate + parser, program = test("i ? 2 : /*test:*/ 3", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ConditionalExpression).Alternate], []string{"test:"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Logical OR + parser, program = test("i || /*test||*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test||"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Logical AND + parser, program = test("i && /*test&&*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test&&"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Bitwise OR + parser, program = test("i | /*test|*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test|"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Exclusive OR + parser, program = test("i ^ /*test^*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test^"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Bitwise AND + parser, program = test("i & /*test&*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test&"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Equality + parser, program = test("i == /*test==*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test=="}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Relational, < + parser, program = test("i < /*test<*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test<"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Relational, instanceof + parser, program = test("i instanceof /*testinstanceof*/ thing", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"testinstanceof"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Shift left + parser, program = test("i << /*test<<*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test<<"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // + + parser, program = test("i + /*test+*/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test+"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // * + parser, program = test("i * /*test**/ 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"test*"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Unary prefix, ++ + parser, program = test("++/*test++*/i", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression).Operand], []string{"test++"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Unary prefix, delete + parser, program = test("delete /*testdelete*/ i", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression).Operand], []string{"testdelete"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Unary postfix, ++ + parser, program = test("i/*test++*/++", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.UnaryExpression).Operand], []string{"test++"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // + pt 2 + parser, program = test("i /*test+*/ + 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Left], []string{"test+"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Multiple comments for a single node + parser, program = test("i /*test+*/ /*test+2*/ + 2", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0]], []string{}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Left], []string{"test+", "test+2"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 2) + + // Multiple comments for multiple nodes + parser, program = test("i /*test1*/ + 2 /*test2*/ + a /*test3*/ * x /*test4*/", nil) + is(len(program.Body), 1) + is(parser.comments.CommentMap.Size(), 4) + + // Leading comment + parser, program = test("/*leadingtest*/i + 2", nil) + is(len(program.Body), 1) + + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement)], []string{"leadingtest"}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Left], []string{}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Leading comment, with semicolon + parser, program = test("/*leadingtest;*/;i + 2", nil) + is(len(program.Body), 2) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"leadingtest;"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays + parser, program = test("[1, 2 /*test2*/, 3]", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test2"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Function calls + parser, program = test("fun(a,b) //test", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.CallExpression)], []string{"test"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Function calls, pt 2 + parser, program = test("fun(a/*test1*/,b)", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.CallExpression).ArgumentList[0]], []string{"test1"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Function calls, pt 3 + parser, program = test("fun(/*test1*/a,b)", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.CallExpression).ArgumentList[0]], []string{"test1"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays pt 2 + parser, program = test(`["abc".substr(0,1)/*testa*/, + "abc.substr(0,2)"];`, nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[0]], []string{"testa"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays pt 3 + parser, program = test(`[a, //test + b];`, nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays pt 4 + parser, program = test(`[a, //test + b, c];`, nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays pt 5 + parser, program = test(` +[ + "a1", // "a" + "a2", // "ab" +]; + `, nil) + is(parser.comments.CommentMap.Size(), 2) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{" \"a\""}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral)], []string{" \"ab\""}, ast.FINAL), nil) + + // Arrays pt 6 + parser, program = test(`[a, /*test*/ b, c];`, nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[1]], []string{"test"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays pt 7 - Empty node + parser, program = test(`[a,,/*test2*/,];`, nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[2]], []string{"test2"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays pt 8 - Trailing node + parser, program = test(`[a,,,/*test2*/];`, nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral)], []string{"test2"}, ast.FINAL), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Arrays pt 9 - Leading node + parser, program = test(`[/*test2*/a,,,];`, nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.ArrayLiteral).Value[0]], []string{"test2"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Object literal + parser, program = test("obj = {a: 1, b: 2 /*test2*/, c: 3}", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[1].Value], []string{"test2"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Object literal, pt 2 + parser, program = test("obj = {/*test2*/a: 1, b: 2, c: 3}", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.KEY), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Object literal, pt 3 + parser, program = test("obj = {x/*test2*/: 1, y: 2, z: 3}", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.COLON), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Object literal, pt 4 + parser, program = test("obj = {x: /*test2*/1, y: 2, z: 3}", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.LEADING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Object literal, pt 5 + parser, program = test("obj = {x: 1/*test2*/, y: 2, z: 3}", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[0].Value], []string{"test2"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Object literal, pt 6 + parser, program = test("obj = {x: 1, y: 2, z: 3/*test2*/}", nil) + is(len(program.Body), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral).Value[2].Value], []string{"test2"}, ast.TRAILING), nil) + is(parser.comments.CommentMap.Size(), 1) + + // Object literal, pt 7 - trailing comment + parser, program = test("obj = {x: 1, y: 2, z: 3,/*test2*/}", nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.ObjectLiteral)], []string{"test2"}, ast.FINAL), nil) + + // Line breaks + parser, program = test(` +t1 = "BLA DE VLA" +/*Test*/ +t2 = "Nothing happens." + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test"}, ast.LEADING), nil) + + // Line breaks pt 2 + parser, program = test(` +t1 = "BLA DE VLA" /*Test*/ +t2 = "Nothing happens." + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.StringLiteral)], []string{"Test"}, ast.TRAILING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[1].(*ast.ExpressionStatement)], []string{}, ast.LEADING), nil) + + // Line breaks pt 3 + parser, program = test(` +t1 = "BLA DE VLA" /*Test*/ /*Test2*/ +t2 = "Nothing happens." + `, nil) + is(parser.comments.CommentMap.Size(), 2) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.StringLiteral)], []string{"Test", "Test2"}, ast.TRAILING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[1].(*ast.ExpressionStatement)], []string{}, ast.LEADING), nil) + + // Line breaks pt 4 + parser, program = test(` +t1 = "BLA DE VLA" /*Test*/ +/*Test2*/ +t2 = "Nothing happens." + `, nil) + is(parser.comments.CommentMap.Size(), 2) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.StringLiteral)], []string{"Test"}, ast.TRAILING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test2"}, ast.LEADING), nil) + + // Line breaks pt 5 + parser, program = test(` +t1 = "BLA DE VLA"; +/*Test*/ +t2 = "Nothing happens." + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test"}, ast.LEADING), nil) + + // Line breaks pt 6 + parser, program = test(` +t1 = "BLA DE VLA"; /*Test*/ +/*Test2*/ +t2 = "Nothing happens." + `, nil) + is(parser.comments.CommentMap.Size(), 2) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"Test", "Test2"}, ast.LEADING), nil) + + // Misc + parser, program = test(` +var x = Object.create({y: { +}, +// a +}); + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // Misc 2 + parser, program = test(` +var x = Object.create({y: { +}, +// a +// b +a: 2}); + `, nil) + is(parser.comments.CommentMap.Size(), 2) + + // Statement blocks + parser, program = test(` +(function() { + // Baseline setup +}) + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body], []string{" Baseline setup"}, ast.FINAL), nil) + + // Switches + parser, program = test(` +switch (switcha) { + // switch comment + case "switchb": + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0]], []string{" switch comment"}, ast.LEADING), nil) + + // Switches pt 2 + parser, program = test(` +switch (switcha) { + case /*switch comment*/ "switchb": + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Test], []string{"switch comment"}, ast.LEADING), nil) + + // Switches pt 3 + parser, program = test(` +switch (switcha) { + case "switchb" /*switch comment*/: + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Test], []string{"switch comment"}, ast.TRAILING), nil) + + // Switches pt 4 + parser, program = test(` +switch (switcha) { + case "switchb": /*switch comment*/ + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) + + // Switches pt 5 - default + parser, program = test(` +switch (switcha) { + default: /*switch comment*/ + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) + + // Switches pt 6 + parser, program = test(` +switch (switcha) { + case "switchb": + /*switch comment*/a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) + + // Switches pt 7 + parser, program = test(` +switch (switcha) { + case "switchb": /*switch comment*/ { + a + } +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) + + // Switches pt 8 + parser, program = test(` +switch (switcha) { + case "switchb": { + a + }/*switch comment*/ +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.TRAILING), nil) + + // Switches pt 9 + parser, program = test(` +switch (switcha) { + case "switchb": /*switch comment*/ { + a + } +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0]], []string{"switch comment"}, ast.LEADING), nil) + + // Switches pt 10 + parser, program = test(` +switch (switcha) { + case "switchb": { + /*switch comment*/a + } +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.SwitchStatement).Body[0].Consequent[0].(*ast.BlockStatement).List[0]], []string{"switch comment"}, ast.LEADING), nil) + + // For loops + parser, program = test(` +for(/*comment*/i = 0 ; i < 1 ; i++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Initializer.(*ast.SequenceExpression).Sequence[0].(*ast.AssignExpression).Left], []string{"comment"}, ast.LEADING), nil) + + // For loops pt 2 + parser, program = test(` +for(i/*comment*/ = 0 ; i < 1 ; i++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Initializer.(*ast.SequenceExpression).Sequence[0].(*ast.AssignExpression).Left], []string{"comment"}, ast.TRAILING), nil) + + // For loops pt 3 + parser, program = test(` +for(i = 0 ; /*comment*/i < 1 ; i++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Test.(*ast.BinaryExpression).Left], []string{"comment"}, ast.LEADING), nil) + + // For loops pt 4 + parser, program = test(` +for(i = 0 ;i /*comment*/ < 1 ; i++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Test.(*ast.BinaryExpression).Left], []string{"comment"}, ast.TRAILING), nil) + + // For loops pt 5 + parser, program = test(` +for(i = 0 ;i < 1 /*comment*/ ; i++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Test.(*ast.BinaryExpression).Right], []string{"comment"}, ast.TRAILING), nil) + + // For loops pt 6 + parser, program = test(` +for(i = 0 ;i < 1 ; /*comment*/ i++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression).Operand], []string{"comment"}, ast.LEADING), nil) + + // For loops pt 7 + parser, program = test(` +for(i = 0 ;i < 1 ; i++) /*comment*/ { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Body], []string{"comment"}, ast.LEADING), nil) + + // For loops pt 8 + parser, program = test(` +for(i = 0 ;i < 1 ; i++) { + a +}/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Body], []string{"comment"}, ast.TRAILING), nil) + + // For loops pt 9 + parser, program = test(` +for(i = 0 ;i < 1 ; /*comment*/i++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression).Operand], []string{"comment"}, ast.LEADING), nil) + + // For loops pt 10 + parser, program = test(` +for(i = 0 ;i < 1 ; i/*comment*/++) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression).Operand.(*ast.Identifier)], []string{"comment"}, ast.TRAILING), nil) + + // For loops pt 11 + parser, program = test(` +for(i = 0 ;i < 1 ; i++/*comment*/) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForStatement).Update.(*ast.UnaryExpression)], []string{"comment"}, ast.TRAILING), nil) + + // ForIn + parser, program = test(` +for(/*comment*/var i = 0 in obj) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Into], []string{"comment"}, ast.LEADING), nil) + + // ForIn pt 2 + parser, program = test(` +for(var i = 0 /*comment*/in obj) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Into.(*ast.VariableExpression).Initializer], []string{"comment"}, ast.TRAILING), nil) + + // ForIn pt 3 + parser, program = test(` +for(var i = 0 in /*comment*/ obj) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Source], []string{"comment"}, ast.LEADING), nil) + + // ForIn pt 4 + parser, program = test(` +for(var i = 0 in obj/*comment*/) { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Source], []string{"comment"}, ast.TRAILING), nil) + + // ForIn pt 5 + parser, program = test(` +for(var i = 0 in obj) /*comment*/ { + a +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Body], []string{"comment"}, ast.LEADING), nil) + + // ForIn pt 6 + parser, program = test(` +for(var i = 0 in obj) { + a +}/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ForInStatement).Body], []string{"comment"}, ast.TRAILING), nil) + + // ForIn pt 7 + parser, program = test(` +for(var i = 0 in obj) { + a +} +// comment + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program], []string{" comment"}, ast.TRAILING), nil) + + // ForIn pt 8 + parser, program = test(` +for(var i = 0 in obj) { + a +} +// comment +c + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{" comment"}, ast.LEADING), nil) + + // Block + parser, program = test(` + /*comment*/{ + a + } + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.BlockStatement)], []string{"comment"}, ast.LEADING), nil) + + // Block pt 2 + parser, program = test(` + { + a + }/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.BlockStatement)], []string{"comment"}, ast.TRAILING), nil) + + // If then else + parser, program = test(` +/*comment*/ +if(a) { + b +} else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement)], []string{"comment"}, ast.LEADING), nil) + + // If then else pt 2 + parser, program = test(` +if/*comment*/(a) { + b +} else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement)], []string{"comment"}, ast.IF), nil) + + // If then else pt 3 + parser, program = test(` +if(/*comment*/a) { + b +} else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Test], []string{"comment"}, ast.LEADING), nil) + + // If then else pt 4 + parser, program = test(` +if(a/*comment*/) { + b +} else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Test], []string{"comment"}, ast.TRAILING), nil) + + // If then else pt 4 + parser, program = test(` +if(a)/*comment*/ { + b +} else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Consequent], []string{"comment"}, ast.LEADING), nil) + + // If then else pt 5 + parser, program = test(` +if(a) { + b +} /*comment*/else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Consequent], []string{"comment"}, ast.TRAILING), nil) + + // If then else pt 6 + parser, program = test(` +if(a) { + b +} else/*comment*/ { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Alternate], []string{"comment"}, ast.LEADING), nil) + + // If then else pt 7 + parser, program = test(` +if(a) { + b +} else { + c +}/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.IfStatement).Alternate], []string{"comment"}, ast.TRAILING), nil) + + // If then else pt 8 + parser, program = test(` +if +/*comment*/ +(a) { + b +} else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // If then else pt 9 + parser, program = test(` +if +(a) + /*comment*/{ + b +} else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // If then else pt 10 + parser, program = test(` +if(a){ + b +} +/*comment*/ +else { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // Do while + parser, program = test(` +/*comment*/do { + a +} while(b) + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.LEADING), nil) + + // Do while pt 2 + parser, program = test(` +do /*comment*/ { + a +} while(b) + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.DO), nil) + + // Do while pt 3 + parser, program = test(` +do { + a +} /*comment*/ while(b) + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement).Body], []string{"comment"}, ast.TRAILING), nil) + + // Do while pt 4 + parser, program = test(` +do { + a +} while/*comment*/(b) + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.WHILE), nil) + + // Do while pt 5 + parser, program = test(` +do { + a +} while(b)/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DoWhileStatement)], []string{"comment"}, ast.TRAILING), nil) + + // While + parser, program = test(` +/*comment*/while(a) { + b +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement)], []string{"comment"}, ast.LEADING), nil) + + // While pt 2 + parser, program = test(` +while/*comment*/(a) { + b +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement)], []string{"comment"}, ast.WHILE), nil) + + // While pt 3 + parser, program = test(` +while(/*comment*/a) { + b +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Test], []string{"comment"}, ast.LEADING), nil) + + // While pt 4 + parser, program = test(` +while(a/*comment*/) { + b +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Test], []string{"comment"}, ast.TRAILING), nil) + + // While pt 5 + parser, program = test(` +while(a) /*comment*/ { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body], []string{"comment"}, ast.LEADING), nil) + + // While pt 6 + parser, program = test(` +while(a) { + c +}/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body], []string{"comment"}, ast.TRAILING), nil) + + // While pt 7 + parser, program = test(` +while(a) { + c/*comment*/ +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.ExpressionStatement).Expression.(*ast.Identifier)], []string{"comment"}, ast.TRAILING), nil) + + // While pt 7 + parser, program = test(` +while(a) { + /*comment*/ +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement)], []string{"comment"}, ast.FINAL), nil) + + // While pt 8 + parser, program = test(` +while +/*comment*/(a) { + +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // While pt 9 + parser, program = test(` +while +(a) + /*comment*/{ + +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // Break + parser, program = test(` +while(a) { + break/*comment*/; +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.BranchStatement)], []string{"comment"}, ast.TRAILING), nil) + + // Break pt 2 + parser, program = test(` +while(a) { + next/*comment*/: + break next; +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement).Label], []string{"comment"}, ast.TRAILING), nil) + + // Break pt 3 + parser, program = test(` +while(a) { + next:/*comment*/ + break next; +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement)], []string{"comment"}, ast.LEADING), nil) + + // Break pt 4 + parser, program = test(` +while(a) { + next: + break /*comment*/next; +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement).Statement.(*ast.BranchStatement).Label], []string{"comment"}, ast.LEADING), nil) + + // Break pt 5 + parser, program = test(` +while(a) { + next: + break next/*comment*/; +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WhileStatement).Body.(*ast.BlockStatement).List[0].(*ast.LabelledStatement).Statement.(*ast.BranchStatement).Label], []string{"comment"}, ast.TRAILING), nil) + + // Debugger + parser, program = test(` +debugger // comment + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.DebuggerStatement)], []string{" comment"}, ast.TRAILING), nil) + + // Debugger pt 2 + parser, program = test(` +debugger; // comment + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program], []string{" comment"}, ast.TRAILING), nil) + + // Debugger pt 3 + parser, program = test(` +debugger; +// comment + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program], []string{" comment"}, ast.TRAILING), nil) + + // With + parser, program = test(` +/*comment*/with(a) { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement)], []string{"comment"}, ast.LEADING), nil) + + // With pt 2 + parser, program = test(` +with/*comment*/(a) { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement)], []string{"comment"}, ast.WITH), nil) + + // With pt 3 + parser, program = test(` +with(/*comment*/a) { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Object], []string{"comment"}, ast.LEADING), nil) + + // With pt 4 + parser, program = test(` +with(a/*comment*/) { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Object], []string{"comment"}, ast.TRAILING), nil) + + // With pt 5 + parser, program = test(` +with(a) /*comment*/ { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Body], []string{"comment"}, ast.LEADING), nil) + + // With pt 6 + parser, program = test(` +with(a) { +}/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.WithStatement).Body], []string{"comment"}, ast.TRAILING), nil) + + // With pt 7 + parser, program = test(` +with +/*comment*/(a) { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // With pt 8 + parser, program = test(` +with +(a) + /*comment*/{ +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // Var + parser, program = test(` +/*comment*/var a + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement)], []string{"comment"}, ast.LEADING), nil) + + // Var pt 2 + parser, program = test(` +var/*comment*/ a + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[0]], []string{"comment"}, ast.LEADING), nil) + + // Var pt 3 + parser, program = test(` +var a/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[0]], []string{"comment"}, ast.TRAILING), nil) + + // Var pt 4 + parser, program = test(` +var a/*comment*/, b + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[0].(*ast.VariableExpression)], []string{"comment"}, ast.TRAILING), nil) + + // Var pt 5 + parser, program = test(` +var a, /*comment*/b + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[1].(*ast.VariableExpression)], []string{"comment"}, ast.LEADING), nil) + + // Var pt 6 + parser, program = test(` +var a, b/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.VariableStatement).List[1]], []string{"comment"}, ast.TRAILING), nil) + + // Var pt 7 + parser, program = test(` +var a, b; +/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program], []string{"comment"}, ast.TRAILING), nil) + + // Return + parser, program = test(` + function f() { +/*comment*/return o +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // Try catch + parser, program = test(` +/*comment*/try { + a +} catch(b) { + c +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement)], []string{"comment"}, ast.LEADING), nil) + + // Try catch pt 2 + parser, program = test(` +try/*comment*/ { + a +} catch(b) { + c +} finally { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Body], []string{"comment"}, ast.LEADING), nil) + + // Try catch pt 3 + parser, program = test(` +try { + a +}/*comment*/ catch(b) { + c +} finally { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Body], []string{"comment"}, ast.TRAILING), nil) + + // Try catch pt 4 + parser, program = test(` +try { + a +} catch(/*comment*/b) { + c +} finally { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Parameter], []string{"comment"}, ast.LEADING), nil) + + // Try catch pt 5 + parser, program = test(` +try { + a +} catch(b/*comment*/) { + c +} finally { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Parameter], []string{"comment"}, ast.TRAILING), nil) + + // Try catch pt 6 + parser, program = test(` +try { + a +} catch(b) /*comment*/{ + c +} finally { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Body], []string{"comment"}, ast.LEADING), nil) + + // Try catch pt 7 + parser, program = test(` +try { + a +} catch(b){ + c +} /*comment*/ finally { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Body], []string{"comment"}, ast.TRAILING), nil) + + // Try catch pt 8 + parser, program = test(` +try { + a +} catch(b){ + c +} finally /*comment*/ { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Finally], []string{"comment"}, ast.LEADING), nil) + + // Try catch pt 9 + parser, program = test(` +try { + a +} catch(b){ + c +} finally { +}/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Finally], []string{"comment"}, ast.TRAILING), nil) + + // Try catch pt 11 + parser, program = test(` +try { + a +} +/*comment*/ + catch(b){ + c +} finally { + d +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Body], []string{"comment"}, ast.TRAILING), nil) + + // Throw + parser, program = test(` +throw a/*comment*/ + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ThrowStatement).Argument], []string{"comment"}, ast.TRAILING), nil) + + // Throw pt 2 + parser, program = test(` +/*comment*/throw a + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ThrowStatement)], []string{"comment"}, ast.LEADING), nil) + + // Throw pt 3 + parser, program = test(` +throw /*comment*/a + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ThrowStatement).Argument], []string{"comment"}, ast.LEADING), nil) + + // Try catch pt 10 + parser, program = test(` +try { + a +} catch(b){ + c +} + /*comment*/finally { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Catch.Body], []string{"comment"}, ast.TRAILING), nil) + + // Try catch pt 11 + parser, program = test(` +try { + a +} catch(b){ + c +} + finally + /*comment*/ + { + d +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.TryStatement).Finally], []string{"comment"}, ast.LEADING), nil) + + // Switch / comment + parser, program = test(` +var volvo = 1 +//comment +switch(abra) { +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // Switch / comment + parser, program = test(` +f("string",{ + key: "val" + //comment +}); + `, nil) + is(parser.comments.CommentMap.Size(), 1) + + // Switch / comment + parser, program = test(` +function f() { + /*comment*/if(true){a++} +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + n := program.Body[0].(*ast.FunctionStatement).Function.Body.(*ast.BlockStatement).List[0] + is(checkComments((parser.comments.CommentMap)[n], []string{"comment"}, ast.LEADING), nil) + + // Function in function + parser, program = test(` +function f() { + /*comment*/function f2() { + } +} + `, nil) + is(parser.comments.CommentMap.Size(), 1) + n = program.Body[0].(*ast.FunctionStatement).Function.Body.(*ast.BlockStatement).List[0] + is(checkComments((parser.comments.CommentMap)[n], []string{"comment"}, ast.LEADING), nil) + + parser, program = test(` +a + /*comment1*/ +/*comment2*/ +b/*comment3*/; +/*comment4*/c + `, nil) + is(parser.comments.CommentMap.Size(), 4) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment1", "comment2"}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment3"}, ast.TRAILING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"comment4"}, ast.LEADING), nil) + + parser, program = test(` +a + /*comment1*/ +/*comment2*/ +b/*comment3*/ +/*comment4*/c + `, nil) + is(parser.comments.CommentMap.Size(), 4) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment1", "comment2"}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.BinaryExpression).Right], []string{"comment3"}, ast.TRAILING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[1]], []string{"comment4"}, ast.LEADING), nil) + + // New + parser, program = test(` +a = /*comment1*/new /*comment2*/ obj/*comment3*/() + `, nil) + is(parser.comments.CommentMap.Size(), 3) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right], []string{"comment1"}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.NewExpression).Callee], []string{"comment2"}, ast.LEADING), nil) + is(checkComments((parser.comments.CommentMap)[program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.AssignExpression).Right.(*ast.NewExpression).Callee], []string{"comment3"}, ast.TRAILING), nil) + + }) +} + +func TestParser_comments2(t *testing.T) { + tt(t, func() { + test := func(source string, chk interface{}) (*_parser, *ast.Program) { + parser, program, err := testParseWithMode(source, StoreComments) + is(firstErr(err), chk) + + // Check unresolved comments + is(len(parser.comments.Comments), 0) + return parser, program + } + + parser, program := test(` +a = /*comment1*/new /*comment2*/ obj/*comment3*/() +`, nil) + n := program.Body[0] + fmt.Printf("FOUND NODE: %v, number of comments: %v\n", reflect.TypeOf(n), len(parser.comments.CommentMap[n])) + displayComments(parser.comments.CommentMap) + + }) +} diff --git a/vendor/github.com/robertkrimen/otto/parser/dbg.go b/vendor/github.com/robertkrimen/otto/parser/dbg.go new file mode 100644 index 00000000..3c5f2f69 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/dbg.go @@ -0,0 +1,9 @@ +// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg + +package parser + +import ( + Dbg "github.com/robertkrimen/otto/dbg" +) + +var dbg, dbgf = Dbg.New() diff --git a/vendor/github.com/robertkrimen/otto/parser/error.go b/vendor/github.com/robertkrimen/otto/parser/error.go new file mode 100644 index 00000000..e0f74a5c --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/error.go @@ -0,0 +1,175 @@ +package parser + +import ( + "fmt" + "sort" + + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +const ( + err_UnexpectedToken = "Unexpected token %v" + err_UnexpectedEndOfInput = "Unexpected end of input" + err_UnexpectedEscape = "Unexpected escape" +) + +// UnexpectedNumber: 'Unexpected number', +// UnexpectedString: 'Unexpected string', +// UnexpectedIdentifier: 'Unexpected identifier', +// UnexpectedReserved: 'Unexpected reserved word', +// NewlineAfterThrow: 'Illegal newline after throw', +// InvalidRegExp: 'Invalid regular expression', +// UnterminatedRegExp: 'Invalid regular expression: missing /', +// InvalidLHSInAssignment: 'Invalid left-hand side in assignment', +// InvalidLHSInForIn: 'Invalid left-hand side in for-in', +// MultipleDefaultsInSwitch: 'More than one default clause in switch statement', +// NoCatchOrFinally: 'Missing catch or finally after try', +// UnknownLabel: 'Undefined label \'%0\'', +// Redeclaration: '%0 \'%1\' has already been declared', +// IllegalContinue: 'Illegal continue statement', +// IllegalBreak: 'Illegal break statement', +// IllegalReturn: 'Illegal return statement', +// StrictModeWith: 'Strict mode code may not include a with statement', +// StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', +// StrictVarName: 'Variable name may not be eval or arguments in strict mode', +// StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', +// StrictParamDupe: 'Strict mode function may not have duplicate parameter names', +// StrictFunctionName: 'Function name may not be eval or arguments in strict mode', +// StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', +// StrictDelete: 'Delete of an unqualified identifier in strict mode.', +// StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode', +// AccessorDataProperty: 'Object literal may not have data and accessor property with the same name', +// AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name', +// StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', +// StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', +// StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', +// StrictReservedWord: 'Use of future reserved word in strict mode' + +// A SyntaxError is a description of an ECMAScript syntax error. + +// An Error represents a parsing error. It includes the position where the error occurred and a message/description. +type Error struct { + Position file.Position + Message string +} + +// FIXME Should this be "SyntaxError"? + +func (self Error) Error() string { + filename := self.Position.Filename + if filename == "" { + filename = "(anonymous)" + } + return fmt.Sprintf("%s: Line %d:%d %s", + filename, + self.Position.Line, + self.Position.Column, + self.Message, + ) +} + +func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error { + idx := file.Idx(0) + switch place := place.(type) { + case int: + idx = self.idxOf(place) + case file.Idx: + if place == 0 { + idx = self.idxOf(self.chrOffset) + } else { + idx = place + } + default: + panic(fmt.Errorf("error(%T, ...)", place)) + } + + position := self.position(idx) + msg = fmt.Sprintf(msg, msgValues...) + self.errors.Add(position, msg) + return self.errors[len(self.errors)-1] +} + +func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error { + if chr == -1 { + return self.error(idx, err_UnexpectedEndOfInput) + } + return self.error(idx, err_UnexpectedToken, token.ILLEGAL) +} + +func (self *_parser) errorUnexpectedToken(tkn token.Token) error { + switch tkn { + case token.EOF: + return self.error(file.Idx(0), err_UnexpectedEndOfInput) + } + value := tkn.String() + switch tkn { + case token.BOOLEAN, token.NULL: + value = self.literal + case token.IDENTIFIER: + return self.error(self.idx, "Unexpected identifier") + case token.KEYWORD: + // TODO Might be a future reserved word + return self.error(self.idx, "Unexpected reserved word") + case token.NUMBER: + return self.error(self.idx, "Unexpected number") + case token.STRING: + return self.error(self.idx, "Unexpected string") + } + return self.error(self.idx, err_UnexpectedToken, value) +} + +// ErrorList is a list of *Errors. +// +type ErrorList []*Error + +// Add adds an Error with given position and message to an ErrorList. +func (self *ErrorList) Add(position file.Position, msg string) { + *self = append(*self, &Error{position, msg}) +} + +// Reset resets an ErrorList to no errors. +func (self *ErrorList) Reset() { *self = (*self)[0:0] } + +func (self ErrorList) Len() int { return len(self) } +func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] } +func (self ErrorList) Less(i, j int) bool { + x := &self[i].Position + y := &self[j].Position + if x.Filename < y.Filename { + return true + } + if x.Filename == y.Filename { + if x.Line < y.Line { + return true + } + if x.Line == y.Line { + return x.Column < y.Column + } + } + return false +} + +func (self ErrorList) Sort() { + sort.Sort(self) +} + +// Error implements the Error interface. +func (self ErrorList) Error() string { + switch len(self) { + case 0: + return "no errors" + case 1: + return self[0].Error() + } + return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1) +} + +// Err returns an error equivalent to this ErrorList. +// If the list is empty, Err returns nil. +func (self ErrorList) Err() error { + if len(self) == 0 { + return nil + } + return self +} diff --git a/vendor/github.com/robertkrimen/otto/parser/expression.go b/vendor/github.com/robertkrimen/otto/parser/expression.go new file mode 100644 index 00000000..63a169d4 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/expression.go @@ -0,0 +1,1005 @@ +package parser + +import ( + "regexp" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +func (self *_parser) parseIdentifier() *ast.Identifier { + literal := self.literal + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + } + self.next() + exp := &ast.Identifier{ + Name: literal, + Idx: idx, + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + + return exp +} + +func (self *_parser) parsePrimaryExpression() ast.Expression { + literal := self.literal + idx := self.idx + switch self.token { + case token.IDENTIFIER: + self.next() + if len(literal) > 1 { + tkn, strict := token.IsKeyword(literal) + if tkn == token.KEYWORD { + if !strict { + self.error(idx, "Unexpected reserved word") + } + } + } + return &ast.Identifier{ + Name: literal, + Idx: idx, + } + case token.NULL: + self.next() + return &ast.NullLiteral{ + Idx: idx, + Literal: literal, + } + case token.BOOLEAN: + self.next() + value := false + switch literal { + case "true": + value = true + case "false": + value = false + default: + self.error(idx, "Illegal boolean literal") + } + return &ast.BooleanLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.STRING: + self.next() + value, err := parseStringLiteral(literal[1 : len(literal)-1]) + if err != nil { + self.error(idx, err.Error()) + } + return &ast.StringLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.NUMBER: + self.next() + value, err := parseNumberLiteral(literal) + if err != nil { + self.error(idx, err.Error()) + value = 0 + } + return &ast.NumberLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.SLASH, token.QUOTIENT_ASSIGN: + return self.parseRegExpLiteral() + case token.LEFT_BRACE: + return self.parseObjectLiteral() + case token.LEFT_BRACKET: + return self.parseArrayLiteral() + case token.LEFT_PARENTHESIS: + self.expect(token.LEFT_PARENTHESIS) + expression := self.parseExpression() + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.RIGHT_PARENTHESIS) + return expression + case token.THIS: + self.next() + return &ast.ThisExpression{ + Idx: idx, + } + case token.FUNCTION: + return self.parseFunction(false) + } + + self.errorUnexpectedToken(self.token) + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} +} + +func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral { + + offset := self.chrOffset - 1 // Opening slash already gotten + if self.token == token.QUOTIENT_ASSIGN { + offset -= 1 // = + } + idx := self.idxOf(offset) + + pattern, err := self.scanString(offset) + endOffset := self.chrOffset + + self.next() + if err == nil { + pattern = pattern[1 : len(pattern)-1] + } + + flags := "" + if self.token == token.IDENTIFIER { // gim + + flags = self.literal + self.next() + endOffset = self.chrOffset - 1 + } + + var value string + // TODO 15.10 + { + // Test during parsing that this is a valid regular expression + // Sorry, (?=) and (?!) are invalid (for now) + pattern, err := TransformRegExp(pattern) + if err != nil { + if pattern == "" || self.mode&IgnoreRegExpErrors == 0 { + self.error(idx, "Invalid regular expression: %s", err.Error()) + } + } else { + _, err = regexp.Compile(pattern) + if err != nil { + // We should not get here, ParseRegExp should catch any errors + self.error(idx, "Invalid regular expression: %s", err.Error()[22:]) // Skip redundant "parse regexp error" + } else { + value = pattern + } + } + } + + literal := self.str[offset:endOffset] + + return &ast.RegExpLiteral{ + Idx: idx, + Literal: literal, + Pattern: pattern, + Flags: flags, + Value: value, + } +} + +func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.VariableExpression) ast.Expression { + + if self.token != token.IDENTIFIER { + idx := self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + + literal := self.literal + idx := self.idx + self.next() + node := &ast.VariableExpression{ + Name: literal, + Idx: idx, + } + if self.mode&StoreComments != 0 { + self.comments.SetExpression(node) + } + + if declarationList != nil { + *declarationList = append(*declarationList, node) + } + + if self.token == token.ASSIGN { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + node.Initializer = self.parseAssignmentExpression() + } + + return node +} + +func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expression { + + var declarationList []*ast.VariableExpression // Avoid bad expressions + var list []ast.Expression + + for { + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + } + decl := self.parseVariableDeclaration(&declarationList) + list = append(list, decl) + if self.token != token.COMMA { + break + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + } + + self.scope.declare(&ast.VariableDeclaration{ + Var: var_, + List: declarationList, + }) + + return list +} + +func (self *_parser) parseObjectPropertyKey() (string, string) { + idx, tkn, literal := self.idx, self.token, self.literal + value := "" + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.KEY) + } + self.next() + + switch tkn { + case token.IDENTIFIER: + value = literal + case token.NUMBER: + var err error + _, err = parseNumberLiteral(literal) + if err != nil { + self.error(idx, err.Error()) + } else { + value = literal + } + case token.STRING: + var err error + value, err = parseStringLiteral(literal[1 : len(literal)-1]) + if err != nil { + self.error(idx, err.Error()) + } + default: + // null, false, class, etc. + if matchIdentifier.MatchString(literal) { + value = literal + } + } + return literal, value +} + +func (self *_parser) parseObjectProperty() ast.Property { + literal, value := self.parseObjectPropertyKey() + if literal == "get" && self.token != token.COLON { + idx := self.idx + _, value := self.parseObjectPropertyKey() + parameterList := self.parseFunctionParameterList() + + node := &ast.FunctionLiteral{ + Function: idx, + ParameterList: parameterList, + } + self.parseFunctionBlock(node) + return ast.Property{ + Key: value, + Kind: "get", + Value: node, + } + } else if literal == "set" && self.token != token.COLON { + idx := self.idx + _, value := self.parseObjectPropertyKey() + parameterList := self.parseFunctionParameterList() + + node := &ast.FunctionLiteral{ + Function: idx, + ParameterList: parameterList, + } + self.parseFunctionBlock(node) + return ast.Property{ + Key: value, + Kind: "set", + Value: node, + } + } + + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.COLON) + } + self.expect(token.COLON) + + exp := ast.Property{ + Key: value, + Kind: "value", + Value: self.parseAssignmentExpression(), + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp.Value) + } + return exp +} + +func (self *_parser) parseObjectLiteral() ast.Expression { + var value []ast.Property + idx0 := self.expect(token.LEFT_BRACE) + for self.token != token.RIGHT_BRACE && self.token != token.EOF { + value = append(value, self.parseObjectProperty()) + if self.token == token.COMMA { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + continue + } + } + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.FINAL) + } + idx1 := self.expect(token.RIGHT_BRACE) + + return &ast.ObjectLiteral{ + LeftBrace: idx0, + RightBrace: idx1, + Value: value, + } +} + +func (self *_parser) parseArrayLiteral() ast.Expression { + idx0 := self.expect(token.LEFT_BRACKET) + var value []ast.Expression + for self.token != token.RIGHT_BRACKET && self.token != token.EOF { + if self.token == token.COMMA { + // This kind of comment requires a special empty expression node. + empty := &ast.EmptyExpression{self.idx, self.idx} + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(empty) + self.comments.Unset() + } + value = append(value, empty) + self.next() + continue + } + + exp := self.parseAssignmentExpression() + + value = append(value, exp) + if self.token != token.RIGHT_BRACKET { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COMMA) + } + } + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.FINAL) + } + idx1 := self.expect(token.RIGHT_BRACKET) + + return &ast.ArrayLiteral{ + LeftBracket: idx0, + RightBracket: idx1, + Value: value, + } +} + +func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + idx0 = self.expect(token.LEFT_PARENTHESIS) + if self.token != token.RIGHT_PARENTHESIS { + for { + exp := self.parseAssignmentExpression() + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + argumentList = append(argumentList, exp) + if self.token != token.COMMA { + break + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + } + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + idx1 = self.expect(token.RIGHT_PARENTHESIS) + return +} + +func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression { + argumentList, idx0, idx1 := self.parseArgumentList() + exp := &ast.CallExpression{ + Callee: left, + LeftParenthesis: idx0, + ArgumentList: argumentList, + RightParenthesis: idx1, + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + return exp +} + +func (self *_parser) parseDotMember(left ast.Expression) ast.Expression { + period := self.expect(token.PERIOD) + + literal := self.literal + idx := self.idx + + if !matchIdentifier.MatchString(literal) { + self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadExpression{From: period, To: self.idx} + } + + self.next() + + return &ast.DotExpression{ + Left: left, + Identifier: &ast.Identifier{ + Idx: idx, + Name: literal, + }, + } +} + +func (self *_parser) parseBracketMember(left ast.Expression) ast.Expression { + idx0 := self.expect(token.LEFT_BRACKET) + member := self.parseExpression() + idx1 := self.expect(token.RIGHT_BRACKET) + return &ast.BracketExpression{ + LeftBracket: idx0, + Left: left, + Member: member, + RightBracket: idx1, + } +} + +func (self *_parser) parseNewExpression() ast.Expression { + idx := self.expect(token.NEW) + callee := self.parseLeftHandSideExpression() + node := &ast.NewExpression{ + New: idx, + Callee: callee, + } + if self.token == token.LEFT_PARENTHESIS { + argumentList, idx0, idx1 := self.parseArgumentList() + node.ArgumentList = argumentList + node.LeftParenthesis = idx0 + node.RightParenthesis = idx1 + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(node) + } + + return node +} + +func (self *_parser) parseLeftHandSideExpression() ast.Expression { + + var left ast.Expression + if self.token == token.NEW { + left = self.parseNewExpression() + } else { + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + self.comments.MarkPrimary() + } + left = self.parsePrimaryExpression() + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(left) + } + + for { + if self.token == token.PERIOD { + left = self.parseDotMember(left) + } else if self.token == token.LEFT_BRACKET { + left = self.parseBracketMember(left) + } else { + break + } + } + + return left +} + +func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { + + allowIn := self.scope.allowIn + self.scope.allowIn = true + defer func() { + self.scope.allowIn = allowIn + }() + + var left ast.Expression + if self.token == token.NEW { + var newComments []*ast.Comment + if self.mode&StoreComments != 0 { + newComments = self.comments.FetchAll() + self.comments.MarkComments(ast.LEADING) + self.comments.MarkPrimary() + } + left = self.parseNewExpression() + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(left, newComments, ast.LEADING) + } + } else { + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + self.comments.MarkPrimary() + } + left = self.parsePrimaryExpression() + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(left) + } + + for { + if self.token == token.PERIOD { + left = self.parseDotMember(left) + } else if self.token == token.LEFT_BRACKET { + left = self.parseBracketMember(left) + } else if self.token == token.LEFT_PARENTHESIS { + left = self.parseCallExpression(left) + } else { + break + } + } + + return left +} + +func (self *_parser) parsePostfixExpression() ast.Expression { + operand := self.parseLeftHandSideExpressionAllowCall() + + switch self.token { + case token.INCREMENT, token.DECREMENT: + // Make sure there is no line terminator here + if self.implicitSemicolon { + break + } + tkn := self.token + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + switch operand.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(idx, "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + exp := &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: operand, + Postfix: true, + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + + return exp + } + + return operand +} + +func (self *_parser) parseUnaryExpression() ast.Expression { + + switch self.token { + case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT: + fallthrough + case token.DELETE, token.VOID, token.TYPEOF: + tkn := self.token + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + return &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: self.parseUnaryExpression(), + } + case token.INCREMENT, token.DECREMENT: + tkn := self.token + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + operand := self.parseUnaryExpression() + switch operand.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(idx, "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + return &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: operand, + } + } + + return self.parsePostfixExpression() +} + +func (self *_parser) parseMultiplicativeExpression() ast.Expression { + next := self.parseUnaryExpression + left := next() + + for self.token == token.MULTIPLY || self.token == token.SLASH || + self.token == token.REMAINDER { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseAdditiveExpression() ast.Expression { + next := self.parseMultiplicativeExpression + left := next() + + for self.token == token.PLUS || self.token == token.MINUS { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseShiftExpression() ast.Expression { + next := self.parseAdditiveExpression + left := next() + + for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT || + self.token == token.UNSIGNED_SHIFT_RIGHT { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseRelationalExpression() ast.Expression { + next := self.parseShiftExpression + left := next() + + allowIn := self.scope.allowIn + self.scope.allowIn = true + defer func() { + self.scope.allowIn = allowIn + }() + + switch self.token { + case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL: + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + exp := &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + Comparison: true, + } + return exp + case token.INSTANCEOF: + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + exp := &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + } + return exp + case token.IN: + if !allowIn { + return left + } + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + exp := &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + } + return exp + } + + return left +} + +func (self *_parser) parseEqualityExpression() ast.Expression { + next := self.parseRelationalExpression + left := next() + + for self.token == token.EQUAL || self.token == token.NOT_EQUAL || + self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + Comparison: true, + } + } + + return left +} + +func (self *_parser) parseBitwiseAndExpression() ast.Expression { + next := self.parseEqualityExpression + left := next() + + for self.token == token.AND { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression { + next := self.parseBitwiseAndExpression + left := next() + + for self.token == token.EXCLUSIVE_OR { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseBitwiseOrExpression() ast.Expression { + next := self.parseBitwiseExclusiveOrExpression + left := next() + + for self.token == token.OR { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseLogicalAndExpression() ast.Expression { + next := self.parseBitwiseOrExpression + left := next() + + for self.token == token.LOGICAL_AND { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseLogicalOrExpression() ast.Expression { + next := self.parseLogicalAndExpression + left := next() + + for self.token == token.LOGICAL_OR { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseConditionlExpression() ast.Expression { + left := self.parseLogicalOrExpression() + + if self.token == token.QUESTION_MARK { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + consequent := self.parseAssignmentExpression() + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COLON) + exp := &ast.ConditionalExpression{ + Test: left, + Consequent: consequent, + Alternate: self.parseAssignmentExpression(), + } + + return exp + } + + return left +} + +func (self *_parser) parseAssignmentExpression() ast.Expression { + left := self.parseConditionlExpression() + var operator token.Token + switch self.token { + case token.ASSIGN: + operator = self.token + case token.ADD_ASSIGN: + operator = token.PLUS + case token.SUBTRACT_ASSIGN: + operator = token.MINUS + case token.MULTIPLY_ASSIGN: + operator = token.MULTIPLY + case token.QUOTIENT_ASSIGN: + operator = token.SLASH + case token.REMAINDER_ASSIGN: + operator = token.REMAINDER + case token.AND_ASSIGN: + operator = token.AND + case token.AND_NOT_ASSIGN: + operator = token.AND_NOT + case token.OR_ASSIGN: + operator = token.OR + case token.EXCLUSIVE_OR_ASSIGN: + operator = token.EXCLUSIVE_OR + case token.SHIFT_LEFT_ASSIGN: + operator = token.SHIFT_LEFT + case token.SHIFT_RIGHT_ASSIGN: + operator = token.SHIFT_RIGHT + case token.UNSIGNED_SHIFT_RIGHT_ASSIGN: + operator = token.UNSIGNED_SHIFT_RIGHT + } + + if operator != 0 { + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + switch left.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(left.Idx0(), "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + + exp := &ast.AssignExpression{ + Left: left, + Operator: operator, + Right: self.parseAssignmentExpression(), + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + + return exp + } + + return left +} + +func (self *_parser) parseExpression() ast.Expression { + next := self.parseAssignmentExpression + left := next() + + if self.token == token.COMMA { + sequence := []ast.Expression{left} + for { + if self.token != token.COMMA { + break + } + self.next() + sequence = append(sequence, next()) + } + return &ast.SequenceExpression{ + Sequence: sequence, + } + } + + return left +} diff --git a/vendor/github.com/robertkrimen/otto/parser/lexer.go b/vendor/github.com/robertkrimen/otto/parser/lexer.go new file mode 100644 index 00000000..d9d69e12 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/lexer.go @@ -0,0 +1,866 @@ +package parser + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +type _chr struct { + value rune + width int +} + +var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`) + +func isDecimalDigit(chr rune) bool { + return '0' <= chr && chr <= '9' +} + +func digitValue(chr rune) int { + switch { + case '0' <= chr && chr <= '9': + return int(chr - '0') + case 'a' <= chr && chr <= 'f': + return int(chr - 'a' + 10) + case 'A' <= chr && chr <= 'F': + return int(chr - 'A' + 10) + } + return 16 // Larger than any legal digit value +} + +func isDigit(chr rune, base int) bool { + return digitValue(chr) < base +} + +func isIdentifierStart(chr rune) bool { + return chr == '$' || chr == '_' || chr == '\\' || + 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' || + chr >= utf8.RuneSelf && unicode.IsLetter(chr) +} + +func isIdentifierPart(chr rune) bool { + return chr == '$' || chr == '_' || chr == '\\' || + 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' || + '0' <= chr && chr <= '9' || + chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr)) +} + +func (self *_parser) scanIdentifier() (string, error) { + offset := self.chrOffset + parse := false + for isIdentifierPart(self.chr) { + if self.chr == '\\' { + distance := self.chrOffset - offset + self.read() + if self.chr != 'u' { + return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr)) + } + parse = true + var value rune + for j := 0; j < 4; j++ { + self.read() + decimal, ok := hex2decimal(byte(self.chr)) + if !ok { + return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr)) + } + value = value<<4 | decimal + } + if value == '\\' { + return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) + } else if distance == 0 { + if !isIdentifierStart(value) { + return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) + } + } else if distance > 0 { + if !isIdentifierPart(value) { + return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) + } + } + } + self.read() + } + literal := string(self.str[offset:self.chrOffset]) + if parse { + return parseStringLiteral(literal) + } + return literal, nil +} + +// 7.2 +func isLineWhiteSpace(chr rune) bool { + switch chr { + case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff': + return true + case '\u000a', '\u000d', '\u2028', '\u2029': + return false + case '\u0085': + return false + } + return unicode.IsSpace(chr) +} + +// 7.3 +func isLineTerminator(chr rune) bool { + switch chr { + case '\u000a', '\u000d', '\u2028', '\u2029': + return true + } + return false +} + +func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) { + + self.implicitSemicolon = false + + for { + self.skipWhiteSpace() + + idx = self.idxOf(self.chrOffset) + insertSemicolon := false + + switch chr := self.chr; { + case isIdentifierStart(chr): + var err error + literal, err = self.scanIdentifier() + if err != nil { + tkn = token.ILLEGAL + break + } + if len(literal) > 1 { + // Keywords are longer than 1 character, avoid lookup otherwise + var strict bool + tkn, strict = token.IsKeyword(literal) + + switch tkn { + + case 0: // Not a keyword + if literal == "true" || literal == "false" { + self.insertSemicolon = true + tkn = token.BOOLEAN + return + } else if literal == "null" { + self.insertSemicolon = true + tkn = token.NULL + return + } + + case token.KEYWORD: + tkn = token.KEYWORD + if strict { + // TODO If strict and in strict mode, then this is not a break + break + } + return + + case + token.THIS, + token.BREAK, + token.THROW, // A newline after a throw is not allowed, but we need to detect it + token.RETURN, + token.CONTINUE, + token.DEBUGGER: + self.insertSemicolon = true + return + + default: + return + + } + } + self.insertSemicolon = true + tkn = token.IDENTIFIER + return + case '0' <= chr && chr <= '9': + self.insertSemicolon = true + tkn, literal = self.scanNumericLiteral(false) + return + default: + self.read() + switch chr { + case -1: + if self.insertSemicolon { + self.insertSemicolon = false + self.implicitSemicolon = true + } + tkn = token.EOF + case '\r', '\n', '\u2028', '\u2029': + self.insertSemicolon = false + self.implicitSemicolon = true + self.comments.AtLineBreak() + continue + case ':': + tkn = token.COLON + case '.': + if digitValue(self.chr) < 10 { + insertSemicolon = true + tkn, literal = self.scanNumericLiteral(true) + } else { + tkn = token.PERIOD + } + case ',': + tkn = token.COMMA + case ';': + tkn = token.SEMICOLON + case '(': + tkn = token.LEFT_PARENTHESIS + case ')': + tkn = token.RIGHT_PARENTHESIS + insertSemicolon = true + case '[': + tkn = token.LEFT_BRACKET + case ']': + tkn = token.RIGHT_BRACKET + insertSemicolon = true + case '{': + tkn = token.LEFT_BRACE + case '}': + tkn = token.RIGHT_BRACE + insertSemicolon = true + case '+': + tkn = self.switch3(token.PLUS, token.ADD_ASSIGN, '+', token.INCREMENT) + if tkn == token.INCREMENT { + insertSemicolon = true + } + case '-': + tkn = self.switch3(token.MINUS, token.SUBTRACT_ASSIGN, '-', token.DECREMENT) + if tkn == token.DECREMENT { + insertSemicolon = true + } + case '*': + tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN) + case '/': + if self.chr == '/' { + if self.mode&StoreComments != 0 { + literal := string(self.readSingleLineComment()) + self.comments.AddComment(ast.NewComment(literal, self.idx)) + continue + } + self.skipSingleLineComment() + continue + } else if self.chr == '*' { + if self.mode&StoreComments != 0 { + literal = string(self.readMultiLineComment()) + self.comments.AddComment(ast.NewComment(literal, self.idx)) + continue + } + self.skipMultiLineComment() + continue + } else { + // Could be division, could be RegExp literal + tkn = self.switch2(token.SLASH, token.QUOTIENT_ASSIGN) + insertSemicolon = true + } + case '%': + tkn = self.switch2(token.REMAINDER, token.REMAINDER_ASSIGN) + case '^': + tkn = self.switch2(token.EXCLUSIVE_OR, token.EXCLUSIVE_OR_ASSIGN) + case '<': + tkn = self.switch4(token.LESS, token.LESS_OR_EQUAL, '<', token.SHIFT_LEFT, token.SHIFT_LEFT_ASSIGN) + case '>': + tkn = self.switch6(token.GREATER, token.GREATER_OR_EQUAL, '>', token.SHIFT_RIGHT, token.SHIFT_RIGHT_ASSIGN, '>', token.UNSIGNED_SHIFT_RIGHT, token.UNSIGNED_SHIFT_RIGHT_ASSIGN) + case '=': + tkn = self.switch2(token.ASSIGN, token.EQUAL) + if tkn == token.EQUAL && self.chr == '=' { + self.read() + tkn = token.STRICT_EQUAL + } + case '!': + tkn = self.switch2(token.NOT, token.NOT_EQUAL) + if tkn == token.NOT_EQUAL && self.chr == '=' { + self.read() + tkn = token.STRICT_NOT_EQUAL + } + case '&': + if self.chr == '^' { + self.read() + tkn = self.switch2(token.AND_NOT, token.AND_NOT_ASSIGN) + } else { + tkn = self.switch3(token.AND, token.AND_ASSIGN, '&', token.LOGICAL_AND) + } + case '|': + tkn = self.switch3(token.OR, token.OR_ASSIGN, '|', token.LOGICAL_OR) + case '~': + tkn = token.BITWISE_NOT + case '?': + tkn = token.QUESTION_MARK + case '"', '\'': + insertSemicolon = true + tkn = token.STRING + var err error + literal, err = self.scanString(self.chrOffset - 1) + if err != nil { + tkn = token.ILLEGAL + } + default: + self.errorUnexpected(idx, chr) + tkn = token.ILLEGAL + } + } + self.insertSemicolon = insertSemicolon + return + } +} + +func (self *_parser) switch2(tkn0, tkn1 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + return tkn0 +} + +func (self *_parser) switch3(tkn0, tkn1 token.Token, chr2 rune, tkn2 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + if self.chr == chr2 { + self.read() + return tkn2 + } + return tkn0 +} + +func (self *_parser) switch4(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + if self.chr == chr2 { + self.read() + if self.chr == '=' { + self.read() + return tkn3 + } + return tkn2 + } + return tkn0 +} + +func (self *_parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token, chr3 rune, tkn4, tkn5 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + if self.chr == chr2 { + self.read() + if self.chr == '=' { + self.read() + return tkn3 + } + if self.chr == chr3 { + self.read() + if self.chr == '=' { + self.read() + return tkn5 + } + return tkn4 + } + return tkn2 + } + return tkn0 +} + +func (self *_parser) chrAt(index int) _chr { + value, width := utf8.DecodeRuneInString(self.str[index:]) + return _chr{ + value: value, + width: width, + } +} + +func (self *_parser) _peek() rune { + if self.offset+1 < self.length { + return rune(self.str[self.offset+1]) + } + return -1 +} + +func (self *_parser) read() { + if self.offset < self.length { + self.chrOffset = self.offset + chr, width := rune(self.str[self.offset]), 1 + if chr >= utf8.RuneSelf { // !ASCII + chr, width = utf8.DecodeRuneInString(self.str[self.offset:]) + if chr == utf8.RuneError && width == 1 { + self.error(self.chrOffset, "Invalid UTF-8 character") + } + } + self.offset += width + self.chr = chr + } else { + self.chrOffset = self.length + self.chr = -1 // EOF + } +} + +// This is here since the functions are so similar +func (self *_RegExp_parser) read() { + if self.offset < self.length { + self.chrOffset = self.offset + chr, width := rune(self.str[self.offset]), 1 + if chr >= utf8.RuneSelf { // !ASCII + chr, width = utf8.DecodeRuneInString(self.str[self.offset:]) + if chr == utf8.RuneError && width == 1 { + self.error(self.chrOffset, "Invalid UTF-8 character") + } + } + self.offset += width + self.chr = chr + } else { + self.chrOffset = self.length + self.chr = -1 // EOF + } +} + +func (self *_parser) readSingleLineComment() (result []rune) { + for self.chr != -1 { + self.read() + if isLineTerminator(self.chr) { + return + } + result = append(result, self.chr) + } + + // Get rid of the trailing -1 + result = result[:len(result)-1] + + return +} + +func (self *_parser) readMultiLineComment() (result []rune) { + self.read() + for self.chr >= 0 { + chr := self.chr + self.read() + if chr == '*' && self.chr == '/' { + self.read() + return + } + + result = append(result, chr) + } + + self.errorUnexpected(0, self.chr) + + return +} + +func (self *_parser) skipSingleLineComment() { + for self.chr != -1 { + self.read() + if isLineTerminator(self.chr) { + return + } + } +} + +func (self *_parser) skipMultiLineComment() { + self.read() + for self.chr >= 0 { + chr := self.chr + self.read() + if chr == '*' && self.chr == '/' { + self.read() + return + } + } + + self.errorUnexpected(0, self.chr) +} + +func (self *_parser) skipWhiteSpace() { + for { + switch self.chr { + case ' ', '\t', '\f', '\v', '\u00a0', '\ufeff': + self.read() + continue + case '\r': + if self._peek() == '\n' { + self.comments.AtLineBreak() + self.read() + } + fallthrough + case '\u2028', '\u2029', '\n': + if self.insertSemicolon { + return + } + self.comments.AtLineBreak() + self.read() + continue + } + if self.chr >= utf8.RuneSelf { + if unicode.IsSpace(self.chr) { + self.read() + continue + } + } + break + } +} + +func (self *_parser) skipLineWhiteSpace() { + for isLineWhiteSpace(self.chr) { + self.read() + } +} + +func (self *_parser) scanMantissa(base int) { + for digitValue(self.chr) < base { + self.read() + } +} + +func (self *_parser) scanEscape(quote rune) { + + var length, base uint32 + switch self.chr { + //case '0', '1', '2', '3', '4', '5', '6', '7': + // Octal: + // length, base, limit = 3, 8, 255 + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0': + self.read() + return + case '\r', '\n', '\u2028', '\u2029': + self.scanNewline() + return + case 'x': + self.read() + length, base = 2, 16 + case 'u': + self.read() + length, base = 4, 16 + default: + self.read() // Always make progress + return + } + + var value uint32 + for ; length > 0 && self.chr != quote && self.chr >= 0; length-- { + digit := uint32(digitValue(self.chr)) + if digit >= base { + break + } + value = value*base + digit + self.read() + } +} + +func (self *_parser) scanString(offset int) (string, error) { + // " ' / + quote := rune(self.str[offset]) + + for self.chr != quote { + chr := self.chr + if chr == '\n' || chr == '\r' || chr == '\u2028' || chr == '\u2029' || chr < 0 { + goto newline + } + self.read() + if chr == '\\' { + if quote == '/' { + if self.chr == '\n' || self.chr == '\r' || self.chr == '\u2028' || self.chr == '\u2029' || self.chr < 0 { + goto newline + } + self.read() + } else { + self.scanEscape(quote) + } + } else if chr == '[' && quote == '/' { + // Allow a slash (/) in a bracket character class ([...]) + // TODO Fix this, this is hacky... + quote = -1 + } else if chr == ']' && quote == -1 { + quote = '/' + } + } + + // " ' / + self.read() + + return string(self.str[offset:self.chrOffset]), nil + +newline: + self.scanNewline() + err := "String not terminated" + if quote == '/' { + err = "Invalid regular expression: missing /" + self.error(self.idxOf(offset), err) + } + return "", errors.New(err) +} + +func (self *_parser) scanNewline() { + if self.chr == '\r' { + self.read() + if self.chr != '\n' { + return + } + } + self.read() +} + +func hex2decimal(chr byte) (value rune, ok bool) { + { + chr := rune(chr) + switch { + case '0' <= chr && chr <= '9': + return chr - '0', true + case 'a' <= chr && chr <= 'f': + return chr - 'a' + 10, true + case 'A' <= chr && chr <= 'F': + return chr - 'A' + 10, true + } + return + } +} + +func parseNumberLiteral(literal string) (value interface{}, err error) { + // TODO Is Uint okay? What about -MAX_UINT + value, err = strconv.ParseInt(literal, 0, 64) + if err == nil { + return + } + + parseIntErr := err // Save this first error, just in case + + value, err = strconv.ParseFloat(literal, 64) + if err == nil { + return + } else if err.(*strconv.NumError).Err == strconv.ErrRange { + // Infinity, etc. + return value, nil + } + + err = parseIntErr + + if err.(*strconv.NumError).Err == strconv.ErrRange { + if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') { + // Could just be a very large number (e.g. 0x8000000000000000) + var value float64 + literal = literal[2:] + for _, chr := range literal { + digit := digitValue(chr) + if digit >= 16 { + goto error + } + value = value*16 + float64(digit) + } + return value, nil + } + } + +error: + return nil, errors.New("Illegal numeric literal") +} + +func parseStringLiteral(literal string) (string, error) { + // Best case scenario... + if literal == "" { + return "", nil + } + + // Slightly less-best case scenario... + if !strings.ContainsRune(literal, '\\') { + return literal, nil + } + + str := literal + buffer := bytes.NewBuffer(make([]byte, 0, 3*len(literal)/2)) + + for len(str) > 0 { + switch chr := str[0]; { + // We do not explicitly handle the case of the quote + // value, which can be: " ' / + // This assumes we're already passed a partially well-formed literal + case chr >= utf8.RuneSelf: + chr, size := utf8.DecodeRuneInString(str) + buffer.WriteRune(chr) + str = str[size:] + continue + case chr != '\\': + buffer.WriteByte(chr) + str = str[1:] + continue + } + + if len(str) <= 1 { + panic("len(str) <= 1") + } + chr := str[1] + var value rune + if chr >= utf8.RuneSelf { + str = str[1:] + var size int + value, size = utf8.DecodeRuneInString(str) + str = str[size:] // \ + + } else { + str = str[2:] // \ + switch chr { + case 'b': + value = '\b' + case 'f': + value = '\f' + case 'n': + value = '\n' + case 'r': + value = '\r' + case 't': + value = '\t' + case 'v': + value = '\v' + case 'x', 'u': + size := 0 + switch chr { + case 'x': + size = 2 + case 'u': + size = 4 + } + if len(str) < size { + return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size) + } + for j := 0; j < size; j++ { + decimal, ok := hex2decimal(str[j]) + if !ok { + return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size]) + } + value = value<<4 | decimal + } + str = str[size:] + if chr == 'x' { + break + } + if value > utf8.MaxRune { + panic("value > utf8.MaxRune") + } + case '0': + if len(str) == 0 || '0' > str[0] || str[0] > '7' { + value = 0 + break + } + fallthrough + case '1', '2', '3', '4', '5', '6', '7': + // TODO strict + value = rune(chr) - '0' + j := 0 + for ; j < 2; j++ { + if len(str) < j+1 { + break + } + chr := str[j] + if '0' > chr || chr > '7' { + break + } + decimal := rune(str[j]) - '0' + value = (value << 3) | decimal + } + str = str[j:] + case '\\': + value = '\\' + case '\'', '"': + value = rune(chr) + case '\r': + if len(str) > 0 { + if str[0] == '\n' { + str = str[1:] + } + } + fallthrough + case '\n': + continue + default: + value = rune(chr) + } + } + buffer.WriteRune(value) + } + + return buffer.String(), nil +} + +func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) { + + offset := self.chrOffset + tkn := token.NUMBER + + if decimalPoint { + offset-- + self.scanMantissa(10) + goto exponent + } + + if self.chr == '0' { + offset := self.chrOffset + self.read() + if self.chr == 'x' || self.chr == 'X' { + // Hexadecimal + self.read() + if isDigit(self.chr, 16) { + self.read() + } else { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + self.scanMantissa(16) + + if self.chrOffset-offset <= 2 { + // Only "0x" or "0X" + self.error(0, "Illegal hexadecimal number") + } + + goto hexadecimal + } else if self.chr == '.' { + // Float + goto float + } else { + // Octal, Float + if self.chr == 'e' || self.chr == 'E' { + goto exponent + } + self.scanMantissa(8) + if self.chr == '8' || self.chr == '9' { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + goto octal + } + } + + self.scanMantissa(10) + +float: + if self.chr == '.' { + self.read() + self.scanMantissa(10) + } + +exponent: + if self.chr == 'e' || self.chr == 'E' { + self.read() + if self.chr == '-' || self.chr == '+' { + self.read() + } + if isDecimalDigit(self.chr) { + self.read() + self.scanMantissa(10) + } else { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + } + +hexadecimal: +octal: + if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + + return tkn, self.str[offset:self.chrOffset] +} diff --git a/vendor/github.com/robertkrimen/otto/parser/lexer_test.go b/vendor/github.com/robertkrimen/otto/parser/lexer_test.go new file mode 100644 index 00000000..9885af7b --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/lexer_test.go @@ -0,0 +1,386 @@ +package parser + +import ( + "testing" + + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/terst" + "github.com/robertkrimen/otto/token" +) + +var tt = terst.Terst +var is = terst.Is + +func TestLexer(t *testing.T) { + tt(t, func() { + setup := func(src string) *_parser { + parser := _newParser("", src, 1, nil) + return parser + } + + test := func(src string, test ...interface{}) { + parser := setup(src) + for len(test) > 0 { + tkn, literal, idx := parser.scan() + if len(test) > 0 { + is(tkn, test[0].(token.Token)) + test = test[1:] + } + if len(test) > 0 { + is(literal, test[0].(string)) + test = test[1:] + } + if len(test) > 0 { + // FIXME terst, Fix this so that cast to file.Idx is not necessary? + is(idx, file.Idx(test[0].(int))) + test = test[1:] + } + } + } + + test("", + token.EOF, "", 1, + ) + + test("1", + token.NUMBER, "1", 1, + token.EOF, "", 2, + ) + + test(".0", + token.NUMBER, ".0", 1, + token.EOF, "", 3, + ) + + test("abc", + token.IDENTIFIER, "abc", 1, + token.EOF, "", 4, + ) + + test("abc(1)", + token.IDENTIFIER, "abc", 1, + token.LEFT_PARENTHESIS, "", 4, + token.NUMBER, "1", 5, + token.RIGHT_PARENTHESIS, "", 6, + token.EOF, "", 7, + ) + + test(".", + token.PERIOD, "", 1, + token.EOF, "", 2, + ) + + test("===.", + token.STRICT_EQUAL, "", 1, + token.PERIOD, "", 4, + token.EOF, "", 5, + ) + + test(">>>=.0", + token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, + token.NUMBER, ".0", 5, + token.EOF, "", 7, + ) + + test(">>>=0.0.", + token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, + token.NUMBER, "0.0", 5, + token.PERIOD, "", 8, + token.EOF, "", 9, + ) + + test("\"abc\"", + token.STRING, "\"abc\"", 1, + token.EOF, "", 6, + ) + + test("abc = //", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.EOF, "", 9, + ) + + test("abc = /*test*/", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.EOF, "", 15, + ) + + test("abc = 1 / 2", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.NUMBER, "1", 7, + token.SLASH, "", 9, + token.NUMBER, "2", 11, + token.EOF, "", 12, + ) + + test("xyzzy = 'Nothing happens.'", + token.IDENTIFIER, "xyzzy", 1, + token.ASSIGN, "", 7, + token.STRING, "'Nothing happens.'", 9, + token.EOF, "", 27, + ) + + test("abc = !false", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.NOT, "", 7, + token.BOOLEAN, "false", 8, + token.EOF, "", 13, + ) + + test("abc = !!true", + token.IDENTIFIER, "abc", 1, + token.ASSIGN, "", 5, + token.NOT, "", 7, + token.NOT, "", 8, + token.BOOLEAN, "true", 9, + token.EOF, "", 13, + ) + + test("abc *= 1", + token.IDENTIFIER, "abc", 1, + token.MULTIPLY_ASSIGN, "", 5, + token.NUMBER, "1", 8, + token.EOF, "", 9, + ) + + test("if 1 else", + token.IF, "if", 1, + token.NUMBER, "1", 4, + token.ELSE, "else", 6, + token.EOF, "", 10, + ) + + test("null", + token.NULL, "null", 1, + token.EOF, "", 5, + ) + + test(`"\u007a\x79\u000a\x78"`, + token.STRING, "\"\\u007a\\x79\\u000a\\x78\"", 1, + token.EOF, "", 23, + ) + + test(`"[First line \ +Second line \ + Third line\ +. ]" + `, + token.STRING, "\"[First line \\\nSecond line \\\n Third line\\\n. ]\"", 1, + token.EOF, "", 53, + ) + + test("/", + token.SLASH, "", 1, + token.EOF, "", 2, + ) + + test("var abc = \"abc\uFFFFabc\"", + token.VAR, "var", 1, + token.IDENTIFIER, "abc", 5, + token.ASSIGN, "", 9, + token.STRING, "\"abc\uFFFFabc\"", 11, + token.EOF, "", 22, + ) + + test(`'\t' === '\r'`, + token.STRING, "'\\t'", 1, + token.STRICT_EQUAL, "", 6, + token.STRING, "'\\r'", 10, + token.EOF, "", 14, + ) + + test(`var \u0024 = 1`, + token.VAR, "var", 1, + token.IDENTIFIER, "$", 5, + token.ASSIGN, "", 12, + token.NUMBER, "1", 14, + token.EOF, "", 15, + ) + + test("10e10000", + token.NUMBER, "10e10000", 1, + token.EOF, "", 9, + ) + + test(`var if var class`, + token.VAR, "var", 1, + token.IF, "if", 5, + token.VAR, "var", 8, + token.KEYWORD, "class", 12, + token.EOF, "", 17, + ) + + test(`-0`, + token.MINUS, "", 1, + token.NUMBER, "0", 2, + token.EOF, "", 3, + ) + + test(`.01`, + token.NUMBER, ".01", 1, + token.EOF, "", 4, + ) + + test(`.01e+2`, + token.NUMBER, ".01e+2", 1, + token.EOF, "", 7, + ) + + test(";", + token.SEMICOLON, "", 1, + token.EOF, "", 2, + ) + + test(";;", + token.SEMICOLON, "", 1, + token.SEMICOLON, "", 2, + token.EOF, "", 3, + ) + + test("//", + token.EOF, "", 3, + ) + + test(";;//test", + token.SEMICOLON, "", 1, + token.SEMICOLON, "", 2, + token.EOF, "", 9, + ) + + test("1", + token.NUMBER, "1", 1, + ) + + test("12 123", + token.NUMBER, "12", 1, + token.NUMBER, "123", 4, + ) + + test("1.2 12.3", + token.NUMBER, "1.2", 1, + token.NUMBER, "12.3", 5, + ) + + test("/ /=", + token.SLASH, "", 1, + token.QUOTIENT_ASSIGN, "", 3, + ) + + test(`"abc"`, + token.STRING, `"abc"`, 1, + ) + + test(`'abc'`, + token.STRING, `'abc'`, 1, + ) + + test("++", + token.INCREMENT, "", 1, + ) + + test(">", + token.GREATER, "", 1, + ) + + test(">=", + token.GREATER_OR_EQUAL, "", 1, + ) + + test(">>", + token.SHIFT_RIGHT, "", 1, + ) + + test(">>=", + token.SHIFT_RIGHT_ASSIGN, "", 1, + ) + + test(">>>", + token.UNSIGNED_SHIFT_RIGHT, "", 1, + ) + + test(">>>=", + token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, + ) + + test("1 \"abc\"", + token.NUMBER, "1", 1, + token.STRING, "\"abc\"", 3, + ) + + test(",", + token.COMMA, "", 1, + ) + + test("1, \"abc\"", + token.NUMBER, "1", 1, + token.COMMA, "", 2, + token.STRING, "\"abc\"", 4, + ) + + test("new abc(1, 3.14159);", + token.NEW, "new", 1, + token.IDENTIFIER, "abc", 5, + token.LEFT_PARENTHESIS, "", 8, + token.NUMBER, "1", 9, + token.COMMA, "", 10, + token.NUMBER, "3.14159", 12, + token.RIGHT_PARENTHESIS, "", 19, + token.SEMICOLON, "", 20, + ) + + test("1 == \"1\"", + token.NUMBER, "1", 1, + token.EQUAL, "", 3, + token.STRING, "\"1\"", 6, + ) + + test("1\n[]\n", + token.NUMBER, "1", 1, + token.LEFT_BRACKET, "", 3, + token.RIGHT_BRACKET, "", 4, + ) + + test("1\ufeff[]\ufeff", + token.NUMBER, "1", 1, + token.LEFT_BRACKET, "", 5, + token.RIGHT_BRACKET, "", 6, + ) + + // ILLEGAL + + test(`3ea`, + token.ILLEGAL, "3e", 1, + token.IDENTIFIER, "a", 3, + token.EOF, "", 4, + ) + + test(`3in`, + token.ILLEGAL, "3", 1, + token.IN, "in", 2, + token.EOF, "", 4, + ) + + test("\"Hello\nWorld\"", + token.ILLEGAL, "", 1, + token.IDENTIFIER, "World", 8, + token.ILLEGAL, "", 13, + token.EOF, "", 14, + ) + + test("\u203f = 10", + token.ILLEGAL, "", 1, + token.ASSIGN, "", 5, + token.NUMBER, "10", 7, + token.EOF, "", 9, + ) + + test(`"\x0G"`, + token.STRING, "\"\\x0G\"", 1, + token.EOF, "", 7, + ) + + }) +} diff --git a/vendor/github.com/robertkrimen/otto/parser/marshal_test.go b/vendor/github.com/robertkrimen/otto/parser/marshal_test.go new file mode 100644 index 00000000..f54cd2d4 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/marshal_test.go @@ -0,0 +1,930 @@ +package parser + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "reflect" + "strings" + "testing" + + "github.com/robertkrimen/otto/ast" +) + +func marshal(name string, children ...interface{}) interface{} { + if len(children) == 1 { + if name == "" { + return testMarshalNode(children[0]) + } + return map[string]interface{}{ + name: children[0], + } + } + map_ := map[string]interface{}{} + length := len(children) / 2 + for i := 0; i < length; i++ { + name := children[i*2].(string) + value := children[i*2+1] + map_[name] = value + } + if name == "" { + return map_ + } + return map[string]interface{}{ + name: map_, + } +} + +func testMarshalNode(node interface{}) interface{} { + switch node := node.(type) { + + // Expression + + case *ast.ArrayLiteral: + return marshal("Array", testMarshalNode(node.Value)) + + case *ast.AssignExpression: + return marshal("Assign", + "Left", testMarshalNode(node.Left), + "Right", testMarshalNode(node.Right), + ) + + case *ast.BinaryExpression: + return marshal("BinaryExpression", + "Operator", node.Operator.String(), + "Left", testMarshalNode(node.Left), + "Right", testMarshalNode(node.Right), + ) + + case *ast.BooleanLiteral: + return marshal("Literal", node.Value) + + case *ast.CallExpression: + return marshal("Call", + "Callee", testMarshalNode(node.Callee), + "ArgumentList", testMarshalNode(node.ArgumentList), + ) + + case *ast.ConditionalExpression: + return marshal("Conditional", + "Test", testMarshalNode(node.Test), + "Consequent", testMarshalNode(node.Consequent), + "Alternate", testMarshalNode(node.Alternate), + ) + + case *ast.DotExpression: + return marshal("Dot", + "Left", testMarshalNode(node.Left), + "Member", node.Identifier.Name, + ) + + case *ast.NewExpression: + return marshal("New", + "Callee", testMarshalNode(node.Callee), + "ArgumentList", testMarshalNode(node.ArgumentList), + ) + + case *ast.NullLiteral: + return marshal("Literal", nil) + + case *ast.NumberLiteral: + return marshal("Literal", node.Value) + + case *ast.ObjectLiteral: + return marshal("Object", testMarshalNode(node.Value)) + + case *ast.RegExpLiteral: + return marshal("Literal", node.Literal) + + case *ast.StringLiteral: + return marshal("Literal", node.Literal) + + case *ast.VariableExpression: + return []interface{}{node.Name, testMarshalNode(node.Initializer)} + + // Statement + + case *ast.Program: + return testMarshalNode(node.Body) + + case *ast.BlockStatement: + return marshal("BlockStatement", testMarshalNode(node.List)) + + case *ast.EmptyStatement: + return "EmptyStatement" + + case *ast.ExpressionStatement: + return testMarshalNode(node.Expression) + + case *ast.ForInStatement: + return marshal("ForIn", + "Into", marshal("", node.Into), + "Source", marshal("", node.Source), + "Body", marshal("", node.Body), + ) + + case *ast.FunctionLiteral: + return marshal("Function", testMarshalNode(node.Body)) + + case *ast.Identifier: + return marshal("Identifier", node.Name) + + case *ast.IfStatement: + if_ := marshal("", + "Test", testMarshalNode(node.Test), + "Consequent", testMarshalNode(node.Consequent), + ).(map[string]interface{}) + if node.Alternate != nil { + if_["Alternate"] = testMarshalNode(node.Alternate) + } + return marshal("If", if_) + + case *ast.LabelledStatement: + return marshal("Label", + "Name", node.Label.Name, + "Statement", testMarshalNode(node.Statement), + ) + case ast.Property: + return marshal("", + "Key", node.Key, + "Value", testMarshalNode(node.Value), + ) + + case *ast.ReturnStatement: + return marshal("Return", testMarshalNode(node.Argument)) + + case *ast.SequenceExpression: + return marshal("Sequence", testMarshalNode(node.Sequence)) + + case *ast.ThrowStatement: + return marshal("Throw", testMarshalNode(node.Argument)) + + case *ast.VariableStatement: + return marshal("Var", testMarshalNode(node.List)) + + } + + { + value := reflect.ValueOf(node) + if value.Kind() == reflect.Slice { + tmp0 := []interface{}{} + for index := 0; index < value.Len(); index++ { + tmp0 = append(tmp0, testMarshalNode(value.Index(index).Interface())) + } + return tmp0 + } + } + + if node != nil { + fmt.Fprintf(os.Stderr, "testMarshalNode(%T)\n", node) + } + + return nil +} + +func testMarshal(node interface{}) string { + value, err := json.Marshal(testMarshalNode(node)) + if err != nil { + panic(err) + } + return string(value) +} + +func TestParserAST(t *testing.T) { + tt(t, func() { + + test := func(inputOutput string) { + match := matchBeforeAfterSeparator.FindStringIndex(inputOutput) + input := strings.TrimSpace(inputOutput[0:match[0]]) + wantOutput := strings.TrimSpace(inputOutput[match[1]:]) + _, program, err := testParse(input) + is(err, nil) + haveOutput := testMarshal(program) + tmp0, tmp1 := bytes.Buffer{}, bytes.Buffer{} + json.Indent(&tmp0, []byte(haveOutput), "\t\t", " ") + json.Indent(&tmp1, []byte(wantOutput), "\t\t", " ") + is("\n\t\t"+tmp0.String(), "\n\t\t"+tmp1.String()) + } + + test(` + --- +[] + `) + + test(` + ; + --- +[ + "EmptyStatement" +] + `) + + test(` + ;;; + --- +[ + "EmptyStatement", + "EmptyStatement", + "EmptyStatement" +] + `) + + test(` + 1; true; abc; "abc"; null; + --- +[ + { + "Literal": 1 + }, + { + "Literal": true + }, + { + "Identifier": "abc" + }, + { + "Literal": "\"abc\"" + }, + { + "Literal": null + } +] + `) + + test(` + { 1; null; 3.14159; ; } + --- +[ + { + "BlockStatement": [ + { + "Literal": 1 + }, + { + "Literal": null + }, + { + "Literal": 3.14159 + }, + "EmptyStatement" + ] + } +] + `) + + test(` + new abc(); + --- +[ + { + "New": { + "ArgumentList": [], + "Callee": { + "Identifier": "abc" + } + } + } +] + `) + + test(` + new abc(1, 3.14159) + --- +[ + { + "New": { + "ArgumentList": [ + { + "Literal": 1 + }, + { + "Literal": 3.14159 + } + ], + "Callee": { + "Identifier": "abc" + } + } + } +] + `) + + test(` + true ? false : true + --- +[ + { + "Conditional": { + "Alternate": { + "Literal": true + }, + "Consequent": { + "Literal": false + }, + "Test": { + "Literal": true + } + } + } +] + `) + + test(` + true || false + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": true + }, + "Operator": "||", + "Right": { + "Literal": false + } + } + } +] + `) + + test(` + 0 + { abc: true } + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": 0 + }, + "Operator": "+", + "Right": { + "Object": [ + { + "Key": "abc", + "Value": { + "Literal": true + } + } + ] + } + } + } +] + `) + + test(` + 1 == "1" + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": 1 + }, + "Operator": "==", + "Right": { + "Literal": "\"1\"" + } + } + } +] + `) + + test(` + abc(1) + --- +[ + { + "Call": { + "ArgumentList": [ + { + "Literal": 1 + } + ], + "Callee": { + "Identifier": "abc" + } + } + } +] + `) + + test(` + Math.pow(3, 2) + --- +[ + { + "Call": { + "ArgumentList": [ + { + "Literal": 3 + }, + { + "Literal": 2 + } + ], + "Callee": { + "Dot": { + "Left": { + "Identifier": "Math" + }, + "Member": "pow" + } + } + } + } +] + `) + + test(` + 1, 2, 3 + --- +[ + { + "Sequence": [ + { + "Literal": 1 + }, + { + "Literal": 2 + }, + { + "Literal": 3 + } + ] + } +] + `) + + test(` + / abc / gim; + --- +[ + { + "Literal": "/ abc / gim" + } +] + `) + + test(` + if (0) + 1; + --- +[ + { + "If": { + "Consequent": { + "Literal": 1 + }, + "Test": { + "Literal": 0 + } + } + } +] + `) + + test(` + 0+function(){ + return; + } + --- +[ + { + "BinaryExpression": { + "Left": { + "Literal": 0 + }, + "Operator": "+", + "Right": { + "Function": { + "BlockStatement": [ + { + "Return": null + } + ] + } + } + } + } +] + `) + + test(` + xyzzy // Ignore it + // Ignore this + // And this + /* And all.. + + + + ... of this! + */ + "Nothing happens." + // And finally this + --- +[ + { + "Identifier": "xyzzy" + }, + { + "Literal": "\"Nothing happens.\"" + } +] + `) + + test(` + ((x & (x = 1)) !== 0) + --- +[ + { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": { + "Identifier": "x" + }, + "Operator": "\u0026", + "Right": { + "Assign": { + "Left": { + "Identifier": "x" + }, + "Right": { + "Literal": 1 + } + } + } + } + }, + "Operator": "!==", + "Right": { + "Literal": 0 + } + } + } +] + `) + + test(` + { abc: 'def' } + --- +[ + { + "BlockStatement": [ + { + "Label": { + "Name": "abc", + "Statement": { + "Literal": "'def'" + } + } + } + ] + } +] + `) + + test(` + // This is not an object, this is a string literal with a label! + ({ abc: 'def' }) + --- +[ + { + "Object": [ + { + "Key": "abc", + "Value": { + "Literal": "'def'" + } + } + ] + } +] + `) + + test(` + [,] + --- +[ + { + "Array": [ + null + ] + } +] + `) + + test(` + [,,] + --- +[ + { + "Array": [ + null, + null + ] + } +] + `) + + test(` + ({ get abc() {} }) + --- +[ + { + "Object": [ + { + "Key": "abc", + "Value": { + "Function": { + "BlockStatement": [] + } + } + } + ] + } +] + `) + + test(` + /abc/.source + --- +[ + { + "Dot": { + "Left": { + "Literal": "/abc/" + }, + "Member": "source" + } + } +] + `) + + test(` + xyzzy + + throw new TypeError("Nothing happens.") + --- +[ + { + "Identifier": "xyzzy" + }, + { + "Throw": { + "New": { + "ArgumentList": [ + { + "Literal": "\"Nothing happens.\"" + } + ], + "Callee": { + "Identifier": "TypeError" + } + } + } + } +] + `) + + // When run, this will call a type error to be thrown + // This is essentially the same as: + // + // var abc = 1(function(){})() + // + test(` + var abc = 1 + (function(){ + })() + --- +[ + { + "Var": [ + [ + "abc", + { + "Call": { + "ArgumentList": [], + "Callee": { + "Call": { + "ArgumentList": [ + { + "Function": { + "BlockStatement": [] + } + } + ], + "Callee": { + "Literal": 1 + } + } + } + } + } + ] + ] + } +] + `) + + test(` + "use strict" + --- +[ + { + "Literal": "\"use strict\"" + } +] + `) + + test(` + "use strict" + abc = 1 + 2 + 11 + --- +[ + { + "Literal": "\"use strict\"" + }, + { + "Assign": { + "Left": { + "Identifier": "abc" + }, + "Right": { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": { + "Literal": 1 + }, + "Operator": "+", + "Right": { + "Literal": 2 + } + } + }, + "Operator": "+", + "Right": { + "Literal": 11 + } + } + } + } + } +] + `) + + test(` + abc = function() { 'use strict' } + --- +[ + { + "Assign": { + "Left": { + "Identifier": "abc" + }, + "Right": { + "Function": { + "BlockStatement": [ + { + "Literal": "'use strict'" + } + ] + } + } + } + } +] + `) + + test(` + for (var abc in def) { + } + --- +[ + { + "ForIn": { + "Body": { + "BlockStatement": [] + }, + "Into": [ + "abc", + null + ], + "Source": { + "Identifier": "def" + } + } + } +] + `) + + test(` + abc = { + '"': "'", + "'": '"', + } + --- +[ + { + "Assign": { + "Left": { + "Identifier": "abc" + }, + "Right": { + "Object": [ + { + "Key": "\"", + "Value": { + "Literal": "\"'\"" + } + }, + { + "Key": "'", + "Value": { + "Literal": "'\"'" + } + } + ] + } + } + } +] + `) + + return + + test(` + if (!abc && abc.jkl(def) && abc[0] === +abc[0] && abc.length < ghi) { + } + --- +[ + { + "If": { + "Consequent": { + "BlockStatement": [] + }, + "Test": { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": { + "BinaryExpression": { + "Left": null, + "Operator": "\u0026\u0026", + "Right": { + "Call": { + "ArgumentList": [ + { + "Identifier": "def" + } + ], + "Callee": { + "Dot": { + "Left": { + "Identifier": "abc" + }, + "Member": "jkl" + } + } + } + } + } + }, + "Operator": "\u0026\u0026", + "Right": { + "BinaryExpression": { + "Left": null, + "Operator": "===", + "Right": null + } + } + } + }, + "Operator": "\u0026\u0026", + "Right": { + "BinaryExpression": { + "Left": { + "Dot": { + "Left": { + "Identifier": "abc" + }, + "Member": "length" + } + }, + "Operator": "\u003c", + "Right": { + "Identifier": "ghi" + } + } + } + } + } + } + } +] + `) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/parser/parser.go b/vendor/github.com/robertkrimen/otto/parser/parser.go new file mode 100644 index 00000000..75b7c500 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/parser.go @@ -0,0 +1,344 @@ +/* +Package parser implements a parser for JavaScript. + + import ( + "github.com/robertkrimen/otto/parser" + ) + +Parse and return an AST + + filename := "" // A filename is optional + src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); + ` + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, filename, src, 0) + +Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +*/ +package parser + +import ( + "bytes" + "encoding/base64" + "errors" + "io" + "io/ioutil" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" + "gopkg.in/sourcemap.v1" +) + +// A Mode value is a set of flags (or 0). They control optional parser functionality. +type Mode uint + +const ( + IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking) + StoreComments // Store the comments from source to the comments map +) + +type _parser struct { + str string + length int + base int + + chr rune // The current character + chrOffset int // The offset of current character + offset int // The offset after current character (may be greater than 1) + + idx file.Idx // The index of token + token token.Token // The token + literal string // The literal of the token, if any + + scope *_scope + insertSemicolon bool // If we see a newline, then insert an implicit semicolon + implicitSemicolon bool // An implicit semicolon exists + + errors ErrorList + + recover struct { + // Scratch when trying to seek to the next statement, etc. + idx file.Idx + count int + } + + mode Mode + + file *file.File + + comments *ast.Comments +} + +type Parser interface { + Scan() (tkn token.Token, literal string, idx file.Idx) +} + +func _newParser(filename, src string, base int, sm *sourcemap.Consumer) *_parser { + return &_parser{ + chr: ' ', // This is set so we can start scanning by skipping whitespace + str: src, + length: len(src), + base: base, + file: file.NewFile(filename, src, base).WithSourceMap(sm), + comments: ast.NewComments(), + } +} + +// Returns a new Parser. +func NewParser(filename, src string) Parser { + return _newParser(filename, src, 1, nil) +} + +func ReadSource(filename string, src interface{}) ([]byte, error) { + if src != nil { + switch src := src.(type) { + case string: + return []byte(src), nil + case []byte: + return src, nil + case *bytes.Buffer: + if src != nil { + return src.Bytes(), nil + } + case io.Reader: + var bfr bytes.Buffer + if _, err := io.Copy(&bfr, src); err != nil { + return nil, err + } + return bfr.Bytes(), nil + } + return nil, errors.New("invalid source") + } + return ioutil.ReadFile(filename) +} + +func ReadSourceMap(filename string, src interface{}) (*sourcemap.Consumer, error) { + if src == nil { + return nil, nil + } + + switch src := src.(type) { + case string: + return sourcemap.Parse(filename, []byte(src)) + case []byte: + return sourcemap.Parse(filename, src) + case *bytes.Buffer: + if src != nil { + return sourcemap.Parse(filename, src.Bytes()) + } + case io.Reader: + var bfr bytes.Buffer + if _, err := io.Copy(&bfr, src); err != nil { + return nil, err + } + return sourcemap.Parse(filename, bfr.Bytes()) + case *sourcemap.Consumer: + return src, nil + } + + return nil, errors.New("invalid sourcemap type") +} + +func ParseFileWithSourceMap(fileSet *file.FileSet, filename string, javascriptSource, sourcemapSource interface{}, mode Mode) (*ast.Program, error) { + src, err := ReadSource(filename, javascriptSource) + if err != nil { + return nil, err + } + + if sourcemapSource == nil { + lines := bytes.Split(src, []byte("\n")) + lastLine := lines[len(lines)-1] + if bytes.HasPrefix(lastLine, []byte("//# sourceMappingURL=data:application/json")) { + bits := bytes.SplitN(lastLine, []byte(","), 2) + if len(bits) == 2 { + if d, err := base64.StdEncoding.DecodeString(string(bits[1])); err == nil { + sourcemapSource = d + } + } + } + } + + sm, err := ReadSourceMap(filename, sourcemapSource) + if err != nil { + return nil, err + } + + base := 1 + if fileSet != nil { + base = fileSet.AddFile(filename, string(src)) + } + + parser := _newParser(filename, string(src), base, sm) + parser.mode = mode + program, err := parser.parse() + program.Comments = parser.comments.CommentMap + + return program, err +} + +// ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns +// the corresponding ast.Program node. +// +// If fileSet == nil, ParseFile parses source without a FileSet. +// If fileSet != nil, ParseFile first adds filename and src to fileSet. +// +// The filename argument is optional and is used for labelling errors, etc. +// +// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. +// +// // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList +// program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0) +// +func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) { + return ParseFileWithSourceMap(fileSet, filename, src, nil, mode) +} + +// ParseFunction parses a given parameter list and body as a function and returns the +// corresponding ast.FunctionLiteral node. +// +// The parameter list, if any, should be a comma-separated list of identifiers. +// +func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) { + + src := "(function(" + parameterList + ") {\n" + body + "\n})" + + parser := _newParser("", src, 1, nil) + program, err := parser.parse() + if err != nil { + return nil, err + } + + return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil +} + +// Scan reads a single token from the source at the current offset, increments the offset and +// returns the token.Token token, a string literal representing the value of the token (if applicable) +// and it's current file.Idx index. +func (self *_parser) Scan() (tkn token.Token, literal string, idx file.Idx) { + return self.scan() +} + +func (self *_parser) slice(idx0, idx1 file.Idx) string { + from := int(idx0) - self.base + to := int(idx1) - self.base + if from >= 0 && to <= len(self.str) { + return self.str[from:to] + } + + return "" +} + +func (self *_parser) parse() (*ast.Program, error) { + self.next() + program := self.parseProgram() + if false { + self.errors.Sort() + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(program, self.comments.FetchAll(), ast.TRAILING) + } + + return program, self.errors.Err() +} + +func (self *_parser) next() { + self.token, self.literal, self.idx = self.scan() +} + +func (self *_parser) optionalSemicolon() { + if self.token == token.SEMICOLON { + self.next() + return + } + + if self.implicitSemicolon { + self.implicitSemicolon = false + return + } + + if self.token != token.EOF && self.token != token.RIGHT_BRACE { + self.expect(token.SEMICOLON) + } +} + +func (self *_parser) semicolon() { + if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE { + if self.implicitSemicolon { + self.implicitSemicolon = false + return + } + + self.expect(token.SEMICOLON) + } +} + +func (self *_parser) idxOf(offset int) file.Idx { + return file.Idx(self.base + offset) +} + +func (self *_parser) expect(value token.Token) file.Idx { + idx := self.idx + if self.token != value { + self.errorUnexpectedToken(self.token) + } + self.next() + return idx +} + +func lineCount(str string) (int, int) { + line, last := 0, -1 + pair := false + for index, chr := range str { + switch chr { + case '\r': + line += 1 + last = index + pair = true + continue + case '\n': + if !pair { + line += 1 + } + last = index + case '\u2028', '\u2029': + line += 1 + last = index + 2 + } + pair = false + } + return line, last +} + +func (self *_parser) position(idx file.Idx) file.Position { + position := file.Position{} + offset := int(idx) - self.base + str := self.str[:offset] + position.Filename = self.file.Name() + line, last := lineCount(str) + position.Line = 1 + line + if last >= 0 { + position.Column = offset - last + } else { + position.Column = 1 + len(str) + } + + return position +} diff --git a/vendor/github.com/robertkrimen/otto/parser/parser_test.go b/vendor/github.com/robertkrimen/otto/parser/parser_test.go new file mode 100644 index 00000000..6cc8f996 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/parser_test.go @@ -0,0 +1,1038 @@ +package parser + +import ( + "errors" + "regexp" + "strings" + "testing" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/underscore" +) + +func firstErr(err error) error { + switch err := err.(type) { + case ErrorList: + return err[0] + } + return err +} + +var matchBeforeAfterSeparator = regexp.MustCompile(`(?m)^[ \t]*---$`) + +func testParse(src string) (parser *_parser, program *ast.Program, err error) { + return testParseWithMode(src, 0) +} + +func testParseWithMode(src string, mode Mode) (parser *_parser, program *ast.Program, err error) { + defer func() { + if tmp := recover(); tmp != nil { + switch tmp := tmp.(type) { + case string: + if strings.HasPrefix(tmp, "SyntaxError:") { + parser = nil + program = nil + err = errors.New(tmp) + return + } + } + panic(tmp) + } + }() + parser = _newParser("", src, 1, nil) + parser.mode = mode + program, err = parser.parse() + return +} + +func TestParseFile(t *testing.T) { + tt(t, func() { + _, err := ParseFile(nil, "", `/abc/`, 0) + is(err, nil) + + _, err = ParseFile(nil, "", `/(?!def)abc/`, IgnoreRegExpErrors) + is(err, nil) + + _, err = ParseFile(nil, "", `/(?!def)abc/`, 0) + is(err, "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid (?!) ") + + _, err = ParseFile(nil, "", `/(?!def)abc/; return`, IgnoreRegExpErrors) + is(err, "(anonymous): Line 1:15 Illegal return statement") + + _, err = ParseFile(nil, "/make-sure-file-path-is-returned-not-anonymous", `a..`, 0) + is(err, "/make-sure-file-path-is-returned-not-anonymous: Line 1:3 Unexpected token .") + }) +} + +func TestParseFunction(t *testing.T) { + tt(t, func() { + test := func(prm, bdy string, expect interface{}) *ast.FunctionLiteral { + function, err := ParseFunction(prm, bdy) + is(firstErr(err), expect) + return function + } + + test("a, b,c,d", "", nil) + + test("a, b;,c,d", "", "(anonymous): Line 1:15 Unexpected token ;") + + test("this", "", "(anonymous): Line 1:11 Unexpected token this") + + test("a, b, c, null", "", "(anonymous): Line 1:20 Unexpected token null") + + test("a, b,c,d", "return;", nil) + + test("a, b,c,d", "break;", "(anonymous): Line 2:1 Illegal break statement") + + test("a, b,c,d", "{}", nil) + }) +} + +func TestParserErr(t *testing.T) { + tt(t, func() { + test := func(input string, expect interface{}) (*ast.Program, *_parser) { + parser := _newParser("", input, 1, nil) + program, err := parser.parse() + is(firstErr(err), expect) + return program, parser + } + + program, parser := test("", nil) + + program, parser = test(` + var abc; + break; do { + } while(true); + `, "(anonymous): Line 3:9 Illegal break statement") + { + stmt := program.Body[1].(*ast.BadStatement) + is(parser.position(stmt.From).Column, 9) + is(parser.position(stmt.To).Column, 16) + is(parser.slice(stmt.From, stmt.To), "break; ") + } + + test("{", "(anonymous): Line 1:2 Unexpected end of input") + + test("}", "(anonymous): Line 1:1 Unexpected token }") + + test("3ea", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3in", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3in []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3e", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3e+", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3e-", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("3x0", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("0x", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("09", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("018", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("01.0", "(anonymous): Line 1:3 Unexpected number") + + test("01a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("0x3in[]", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\"Hello\nWorld\"", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\u203f = 10", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("x\\\\u002a", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("/\n", "(anonymous): Line 1:1 Invalid regular expression: missing /") + + test("var x = /(s/g", "(anonymous): Line 1:9 Invalid regular expression: Unterminated group") + + test("0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("func() = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("(1 + 1) = 2", "(anonymous): Line 1:2 Invalid left-hand side in assignment") + + test("1++", "(anonymous): Line 1:2 Invalid left-hand side in assignment") + + test("1--", "(anonymous): Line 1:2 Invalid left-hand side in assignment") + + test("--1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("for((1 + 1) in abc) def();", "(anonymous): Line 1:1 Invalid left-hand side in for-in") + + test("[", "(anonymous): Line 1:2 Unexpected end of input") + + test("[,", "(anonymous): Line 1:3 Unexpected end of input") + + test("1 + {", "(anonymous): Line 1:6 Unexpected end of input") + + test("1 + { abc:abc", "(anonymous): Line 1:14 Unexpected end of input") + + test("1 + { abc:abc,", "(anonymous): Line 1:15 Unexpected end of input") + + test("var abc = /\n/", "(anonymous): Line 1:11 Invalid regular expression: missing /") + + test("var abc = \"\n", "(anonymous): Line 1:11 Unexpected token ILLEGAL") + + test("var if = 0", "(anonymous): Line 1:5 Unexpected token if") + + test("abc + 0 = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("+abc = 1", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + test("1 + (", "(anonymous): Line 1:6 Unexpected end of input") + + test("\n\n\n{", "(anonymous): Line 4:2 Unexpected end of input") + + test("\n/* Some multiline\ncomment */\n)", "(anonymous): Line 4:1 Unexpected token )") + + // TODO + //{ set 1 } + //{ get 2 } + //({ set: s(if) { } }) + //({ set s(.) { } }) + //({ set: s() { } }) + //({ set: s(a, b) { } }) + //({ get: g(d) { } }) + //({ get i() { }, i: 42 }) + //({ i: 42, get i() { } }) + //({ set i(x) { }, i: 42 }) + //({ i: 42, set i(x) { } }) + //({ get i() { }, get i() { } }) + //({ set i(x) { }, set i(x) { } }) + + test("function abc(if) {}", "(anonymous): Line 1:14 Unexpected token if") + + test("function abc(true) {}", "(anonymous): Line 1:14 Unexpected token true") + + test("function abc(false) {}", "(anonymous): Line 1:14 Unexpected token false") + + test("function abc(null) {}", "(anonymous): Line 1:14 Unexpected token null") + + test("function null() {}", "(anonymous): Line 1:10 Unexpected token null") + + test("function true() {}", "(anonymous): Line 1:10 Unexpected token true") + + test("function false() {}", "(anonymous): Line 1:10 Unexpected token false") + + test("function if() {}", "(anonymous): Line 1:10 Unexpected token if") + + test("a b;", "(anonymous): Line 1:3 Unexpected identifier") + + test("if.a", "(anonymous): Line 1:3 Unexpected token .") + + test("a if", "(anonymous): Line 1:3 Unexpected token if") + + test("a class", "(anonymous): Line 1:3 Unexpected reserved word") + + test("break\n", "(anonymous): Line 1:1 Illegal break statement") + + test("break 1;", "(anonymous): Line 1:7 Unexpected number") + + test("for (;;) { break 1; }", "(anonymous): Line 1:18 Unexpected number") + + test("continue\n", "(anonymous): Line 1:1 Illegal continue statement") + + test("continue 1;", "(anonymous): Line 1:10 Unexpected number") + + test("for (;;) { continue 1; }", "(anonymous): Line 1:21 Unexpected number") + + test("throw", "(anonymous): Line 1:1 Unexpected end of input") + + test("throw;", "(anonymous): Line 1:6 Unexpected token ;") + + test("throw \n", "(anonymous): Line 1:1 Unexpected end of input") + + test("for (var abc, def in {});", "(anonymous): Line 1:19 Unexpected token in") + + test("for ((abc in {});;);", nil) + + test("for ((abc in {}));", "(anonymous): Line 1:17 Unexpected token )") + + test("for (+abc in {});", "(anonymous): Line 1:1 Invalid left-hand side in for-in") + + test("if (false)", "(anonymous): Line 1:11 Unexpected end of input") + + test("if (false) abc(); else", "(anonymous): Line 1:23 Unexpected end of input") + + test("do", "(anonymous): Line 1:3 Unexpected end of input") + + test("while (false)", "(anonymous): Line 1:14 Unexpected end of input") + + test("for (;;)", "(anonymous): Line 1:9 Unexpected end of input") + + test("with (abc)", "(anonymous): Line 1:11 Unexpected end of input") + + test("try {}", "(anonymous): Line 1:1 Missing catch or finally after try") + + test("try {} catch {}", "(anonymous): Line 1:14 Unexpected token {") + + test("try {} catch () {}", "(anonymous): Line 1:15 Unexpected token )") + + test("\u203f = 1", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + // TODO + // const x = 12, y; + // const x, y = 12; + // const x; + // if(true) let a = 1; + // if(true) const a = 1; + + test(`new abc()."def"`, "(anonymous): Line 1:11 Unexpected string") + + test("/*", "(anonymous): Line 1:3 Unexpected end of input") + + test("/**", "(anonymous): Line 1:4 Unexpected end of input") + + test("/*\n\n\n", "(anonymous): Line 4:1 Unexpected end of input") + + test("/*\n\n\n*", "(anonymous): Line 4:2 Unexpected end of input") + + test("/*abc", "(anonymous): Line 1:6 Unexpected end of input") + + test("/*abc *", "(anonymous): Line 1:9 Unexpected end of input") + + test("\n]", "(anonymous): Line 2:1 Unexpected token ]") + + test("\r\n]", "(anonymous): Line 2:1 Unexpected token ]") + + test("\n\r]", "(anonymous): Line 3:1 Unexpected token ]") + + test("//\r\n]", "(anonymous): Line 2:1 Unexpected token ]") + + test("//\n\r]", "(anonymous): Line 3:1 Unexpected token ]") + + test("/abc\\\n/", "(anonymous): Line 1:1 Invalid regular expression: missing /") + + test("//\r \n]", "(anonymous): Line 3:1 Unexpected token ]") + + test("/*\r\n*/]", "(anonymous): Line 2:3 Unexpected token ]") + + test("/*\r \n*/]", "(anonymous): Line 3:3 Unexpected token ]") + + test("\\\\", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u005c", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\abc", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u0000", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u200c = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("\\u200D = []", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test(`"\`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test(`"\u`, "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("return", "(anonymous): Line 1:1 Illegal return statement") + + test("continue", "(anonymous): Line 1:1 Illegal continue statement") + + test("break", "(anonymous): Line 1:1 Illegal break statement") + + test("switch (abc) { default: continue; }", "(anonymous): Line 1:25 Illegal continue statement") + + test("do { abc } *", "(anonymous): Line 1:12 Unexpected token *") + + test("while (true) { break abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") + + test("while (true) { continue abc; }", "(anonymous): Line 1:16 Undefined label 'abc'") + + test("abc: while (true) { (function(){ break abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") + + test("abc: while (true) { (function(){ abc: break abc; }); }", nil) + + test("abc: while (true) { (function(){ continue abc; }); }", "(anonymous): Line 1:34 Undefined label 'abc'") + + test(`abc: if (0) break abc; else {}`, nil) + + test(`abc: if (0) { break abc; } else {}`, nil) + + test(`abc: if (0) { break abc } else {}`, nil) + + test("abc: while (true) { abc: while (true) {} }", "(anonymous): Line 1:21 Label 'abc' already exists") + + if false { + // TODO When strict mode is implemented + test("(function () { 'use strict'; delete abc; }())", "") + } + + test("_: _: while (true) {]", "(anonymous): Line 1:4 Label '_' already exists") + + test("_:\n_:\nwhile (true) {]", "(anonymous): Line 2:1 Label '_' already exists") + + test("_:\n _:\nwhile (true) {]", "(anonymous): Line 2:4 Label '_' already exists") + + test("/Xyzzy(?!Nothing happens)/", + "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid (?!) ") + + test("function(){}", "(anonymous): Line 1:9 Unexpected token (") + + test("\n/*/", "(anonymous): Line 2:4 Unexpected end of input") + + test("/*/.source", "(anonymous): Line 1:11 Unexpected end of input") + + test("/\\1/.source", "(anonymous): Line 1:1 Invalid regular expression: re2: Invalid \\1 ") + + test("var class", "(anonymous): Line 1:5 Unexpected reserved word") + + test("var if", "(anonymous): Line 1:5 Unexpected token if") + + test("object Object", "(anonymous): Line 1:8 Unexpected identifier") + + test("[object Object]", "(anonymous): Line 1:9 Unexpected identifier") + + test("\\u0xyz", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test(`for (var abc, def in {}) {}`, "(anonymous): Line 1:19 Unexpected token in") + + test(`for (abc, def in {}) {}`, "(anonymous): Line 1:1 Invalid left-hand side in for-in") + + test(`for (var abc=def, ghi=("abc" in {}); true;) {}`, nil) + + { + // Semicolon insertion + + test("this\nif (1);", nil) + + test("while (1) { break\nif (1); }", nil) + + test("throw\nif (1);", "(anonymous): Line 1:1 Illegal newline after throw") + + test("(function(){ return\nif (1); })", nil) + + test("while (1) { continue\nif (1); }", nil) + + test("debugger\nif (1);", nil) + } + + { // Reserved words + + test("class", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.class = 1", nil) + test("var class;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("const", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.const = 1", nil) + test("var const;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("enum", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.enum = 1", nil) + test("var enum;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("export", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.export = 1", nil) + test("var export;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("extends", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.extends = 1", nil) + test("var extends;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("import", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.import = 1", nil) + test("var import;", "(anonymous): Line 1:5 Unexpected reserved word") + + test("super", "(anonymous): Line 1:1 Unexpected reserved word") + test("abc.super = 1", nil) + test("var super;", "(anonymous): Line 1:5 Unexpected reserved word") + } + + { // Reserved words (strict) + + test(`implements`, nil) + test(`abc.implements = 1`, nil) + test(`var implements;`, nil) + + test(`interface`, nil) + test(`abc.interface = 1`, nil) + test(`var interface;`, nil) + + test(`let`, nil) + test(`abc.let = 1`, nil) + test(`var let;`, nil) + + test(`package`, nil) + test(`abc.package = 1`, nil) + test(`var package;`, nil) + + test(`private`, nil) + test(`abc.private = 1`, nil) + test(`var private;`, nil) + + test(`protected`, nil) + test(`abc.protected = 1`, nil) + test(`var protected;`, nil) + + test(`public`, nil) + test(`abc.public = 1`, nil) + test(`var public;`, nil) + + test(`static`, nil) + test(`abc.static = 1`, nil) + test(`var static;`, nil) + + test(`yield`, nil) + test(`abc.yield = 1`, nil) + test(`var yield;`, nil) + } + }) +} + +func TestParser(t *testing.T) { + tt(t, func() { + test := func(source string, chk interface{}) *ast.Program { + _, program, err := testParse(source) + is(firstErr(err), chk) + return program + } + + test(` + abc + -- + [] + `, "(anonymous): Line 3:13 Invalid left-hand side in assignment") + + test(` + abc-- + [] + `, nil) + + test("1\n[]\n", "(anonymous): Line 2:2 Unexpected token ]") + + test(` + function abc() { + } + abc() + `, nil) + + program := test("", nil) + + test("//", nil) + + test("/* */", nil) + + test("/** **/", nil) + + test("/*****/", nil) + + test("/*", "(anonymous): Line 1:3 Unexpected end of input") + + test("#", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("/**/#", "(anonymous): Line 1:5 Unexpected token ILLEGAL") + + test("new +", "(anonymous): Line 1:5 Unexpected token +") + + program = test(";", nil) + is(len(program.Body), 1) + is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) + + program = test(";;", nil) + is(len(program.Body), 2) + is(program.Body[0].(*ast.EmptyStatement).Semicolon, file.Idx(1)) + is(program.Body[1].(*ast.EmptyStatement).Semicolon, file.Idx(2)) + + program = test("1.2", nil) + is(len(program.Body), 1) + is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") + + program = test("/* */1.2", nil) + is(len(program.Body), 1) + is(program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.NumberLiteral).Literal, "1.2") + + program = test("\n", nil) + is(len(program.Body), 0) + + test(` + if (0) { + abc = 0 + } + else abc = 0 + `, nil) + + test("if (0) abc = 0 else abc = 0", "(anonymous): Line 1:16 Unexpected token else") + + test(` + if (0) { + abc = 0 + } else abc = 0 + `, nil) + + test(` + if (0) { + abc = 1 + } else { + } + `, nil) + + test(` + do { + } while (true) + `, nil) + + test(` + try { + } finally { + } + `, nil) + + test(` + try { + } catch (abc) { + } finally { + } + `, nil) + + test(` + try { + } + catch (abc) { + } + finally { + } + `, nil) + + test(`try {} catch (abc) {} finally {}`, nil) + + test(` + do { + do { + } while (0) + } while (0) + `, nil) + + test(` + (function(){ + try { + if ( + 1 + ) { + return 1 + } + return 0 + } finally { + } + })() + `, nil) + + test("abc = ''\ndef", nil) + + test("abc = 1\ndef", nil) + + test("abc = Math\ndef", nil) + + test(`"\'"`, nil) + + test(` + abc = function(){ + } + abc = 0 + `, nil) + + test("abc.null = 0", nil) + + test("0x41", nil) + + test(`"\d"`, nil) + + test(`(function(){return this})`, nil) + + test(` + Object.defineProperty(Array.prototype, "0", { + value: 100, + writable: false, + configurable: true + }); + abc = [101]; + abc.hasOwnProperty("0") && abc[0] === 101; + `, nil) + + test(`new abc()`, nil) + test(`new {}`, nil) + + test(` + limit = 4 + result = 0 + while (limit) { + limit = limit - 1 + if (limit) { + } + else { + break + } + result = result + 1 + } + `, nil) + + test(` + while (0) { + if (0) { + continue + } + } + `, nil) + + test("var \u0061\u0062\u0063 = 0", nil) + + // 7_3_1 + test("var test7_3_1\nabc = 66;", nil) + test("var test7_3_1\u2028abc = 66;", nil) + + // 7_3_3 + test("//\u2028 =;", "(anonymous): Line 2:2 Unexpected token =") + + // 7_3_10 + test("var abc = \u2029;", "(anonymous): Line 2:1 Unexpected token ;") + test("var abc = \\u2029;", "(anonymous): Line 1:11 Unexpected token ILLEGAL") + test("var \\u0061\\u0062\\u0063 = 0;", nil) + + test("'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + test("'\nstr\ning\n'", "(anonymous): Line 1:1 Unexpected token ILLEGAL") + + // S7.6_A4.3_T1 + test(`var $\u0030 = 0;`, nil) + + // S7.6.1.1_A1.1 + test(`switch = 1`, "(anonymous): Line 1:8 Unexpected token =") + + // S7.8.3_A2.1_T1 + test(`.0 === 0.0`, nil) + + // 7.8.5-1 + test("var regExp = /\\\rn/;", "(anonymous): Line 1:14 Invalid regular expression: missing /") + + // S7.8.5_A1.1_T2 + test("var regExp = /=/;", nil) + + // S7.8.5_A1.2_T1 + test("/*/", "(anonymous): Line 1:4 Unexpected end of input") + + // Sbp_7.9_A9_T3 + test(` + do { + ; + } while (false) true + `, nil) + + // S7.9_A10_T10 + test(` + {a:1 + } 3 + `, nil) + + test(` + abc + ++def + `, nil) + + // S7.9_A5.2_T1 + test(` + for(false;false + ) { + break; + } + `, "(anonymous): Line 3:13 Unexpected token )") + + // S7.9_A9_T8 + test(` + do {}; + while (false) + `, "(anonymous): Line 2:18 Unexpected token ;") + + // S8.4_A5 + test(` + "x\0y" + `, nil) + + // S9.3.1_A6_T1 + test(` + 10e10000 + `, nil) + + // 10.4.2-1-5 + test(` + "abc\ + def" + `, nil) + + test("'\\\n'", nil) + + test("'\\\r\n'", nil) + + //// 11.13.1-1-1 + test("42 = 42;", "(anonymous): Line 1:1 Invalid left-hand side in assignment") + + // S11.13.2_A4.2_T1.3 + test(` + abc /= "1" + `, nil) + + // 12.1-1 + test(` + try{};catch(){} + `, "(anonymous): Line 2:13 Missing catch or finally after try") + + // 12.1-3 + test(` + try{};finally{} + `, "(anonymous): Line 2:13 Missing catch or finally after try") + + // S12.6.3_A11.1_T3 + test(` + while (true) { + break abc; + } + `, "(anonymous): Line 3:17 Undefined label 'abc'") + + // S15.3_A2_T1 + test(`var x / = 1;`, "(anonymous): Line 1:7 Unexpected token /") + + test(` + function abc() { + if (0) + return; + else { + } + } + `, nil) + + test("//\u2028 var =;", "(anonymous): Line 2:6 Unexpected token =") + + test(` + throw + {} + `, "(anonymous): Line 2:13 Illegal newline after throw") + + // S7.6.1.1_A1.11 + test(` + function = 1 + `, "(anonymous): Line 2:22 Unexpected token =") + + // S7.8.3_A1.2_T1 + test(`0e1`, nil) + + test("abc = 1; abc\n++", "(anonymous): Line 2:3 Unexpected end of input") + + // --- + + test("({ get abc() {} })", nil) + + test(`for (abc.def in {}) {}`, nil) + + test(`while (true) { break }`, nil) + + test(`while (true) { continue }`, nil) + + test(`abc=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?)|(.{0,2}\/{1}))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,def=/^(?:(\w+:)\/{2})|(.{0,2}\/{1})?([/.]*?(?:[^?]+)?\/?)?$/`, nil) + + test(`(function() { try {} catch (err) {} finally {} return })`, nil) + + test(`0xde0b6b3a7640080.toFixed(0)`, nil) + + test(`/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/`, nil) + + test(`/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]/`, nil) + + test("var abc = 1;\ufeff", nil) + + test("\ufeff/* var abc = 1; */", nil) + + test(`if (-0x8000000000000000<=abc&&abc<=0x8000000000000000) {}`, nil) + + test(`(function(){debugger;return this;})`, nil) + + test(` + + `, nil) + + test(` + var abc = "" + debugger + `, nil) + + test(` + var abc = /\[\]$/ + debugger + `, nil) + + test(` + var abc = 1 / + 2 + debugger + `, nil) + }) +} + +func Test_parseStringLiteral(t *testing.T) { + tt(t, func() { + test := func(have, want string) { + have, err := parseStringLiteral(have) + is(err, nil) + is(have, want) + } + + test("", "") + + test("1(\\\\d+)", "1(\\d+)") + + test("\\u2029", "\u2029") + + test("abc\\uFFFFabc", "abc\uFFFFabc") + + test("[First line \\\nSecond line \\\n Third line\\\n. ]", + "[First line Second line Third line. ]") + + test("\\u007a\\x79\\u000a\\x78", "zy\nx") + + // S7.8.4_A4.2_T3 + test("\\a", "a") + test("\u0410", "\u0410") + + // S7.8.4_A5.1_T1 + test("\\0", "\u0000") + + // S8.4_A5 + test("\u0000", "\u0000") + + // 15.5.4.20 + test("'abc'\\\n'def'", "'abc''def'") + + // 15.5.4.20-4-1 + test("'abc'\\\r\n'def'", "'abc''def'") + + // Octal + test("\\0", "\000") + test("\\00", "\000") + test("\\000", "\000") + test("\\09", "\0009") + test("\\009", "\0009") + test("\\0009", "\0009") + test("\\1", "\001") + test("\\01", "\001") + test("\\001", "\001") + test("\\0011", "\0011") + test("\\1abc", "\001abc") + + test("\\\u4e16", "\u4e16") + + // err + test = func(have, want string) { + have, err := parseStringLiteral(have) + is(err.Error(), want) + is(have, "") + } + + test(`\u`, `invalid escape: \u: len("") != 4`) + test(`\u0`, `invalid escape: \u: len("0") != 4`) + test(`\u00`, `invalid escape: \u: len("00") != 4`) + test(`\u000`, `invalid escape: \u: len("000") != 4`) + + test(`\x`, `invalid escape: \x: len("") != 2`) + test(`\x0`, `invalid escape: \x: len("0") != 2`) + test(`\x0`, `invalid escape: \x: len("0") != 2`) + }) +} + +func Test_parseNumberLiteral(t *testing.T) { + tt(t, func() { + test := func(input string, expect interface{}) { + result, err := parseNumberLiteral(input) + is(err, nil) + is(result, expect) + } + + test("0", 0) + + test("0x8000000000000000", float64(9.223372036854776e+18)) + }) +} + +func TestPosition(t *testing.T) { + tt(t, func() { + parser := _newParser("", "// Lorem ipsum", 1, nil) + + // Out of range, idx0 (error condition) + is(parser.slice(0, 1), "") + is(parser.slice(0, 10), "") + + // Out of range, idx1 (error condition) + is(parser.slice(1, 128), "") + + is(parser.str[0:0], "") + is(parser.slice(1, 1), "") + + is(parser.str[0:1], "/") + is(parser.slice(1, 2), "/") + + is(parser.str[0:14], "// Lorem ipsum") + is(parser.slice(1, 15), "// Lorem ipsum") + + parser = _newParser("", "(function(){ return 0; })", 1, nil) + program, err := parser.parse() + is(err, nil) + + var node ast.Node + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) + is(node.Idx0(), file.Idx(2)) + is(node.Idx1(), file.Idx(25)) + is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") + is(parser.slice(node.Idx0(), node.Idx1()+1), "function(){ return 0; })") + is(parser.slice(node.Idx0(), node.Idx1()+2), "") + is(node.(*ast.FunctionLiteral).Source, "function(){ return 0; }") + + node = program + is(node.Idx0(), file.Idx(2)) + is(node.Idx1(), file.Idx(25)) + is(parser.slice(node.Idx0(), node.Idx1()), "function(){ return 0; }") + + parser = _newParser("", "(function(){ return abc; })", 1, nil) + program, err = parser.parse() + is(err, nil) + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral) + is(node.(*ast.FunctionLiteral).Source, "function(){ return abc; }") + + parser = _newParser("", "this.style", 1, nil) + program, err = parser.parse() + node = program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.DotExpression).Left.(*ast.ThisExpression) + is(node.Idx0(), file.Idx(1)) + is(node.Idx1(), file.Idx(5)) + + parser = _newParser("", "(function(){ if (abc) { throw 'failed'; } })", 1, nil) + program, err = parser.parse() + is(err, nil) + block := program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral).Body.(*ast.BlockStatement) + node = block.List[0].(*ast.IfStatement) + is(node.Idx0(), 21) + node = node.(*ast.IfStatement).Consequent.(*ast.BlockStatement).List[0].(*ast.ThrowStatement) + is(node.Idx0(), 39) + }) +} + +func BenchmarkParser(b *testing.B) { + src := underscore.Source() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + parser := _newParser("", src, 1, nil) + parser.parse() + } +} diff --git a/vendor/github.com/robertkrimen/otto/parser/regexp.go b/vendor/github.com/robertkrimen/otto/parser/regexp.go new file mode 100644 index 00000000..f614dae7 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/regexp.go @@ -0,0 +1,358 @@ +package parser + +import ( + "bytes" + "fmt" + "strconv" +) + +type _RegExp_parser struct { + str string + length int + + chr rune // The current character + chrOffset int // The offset of current character + offset int // The offset after current character (may be greater than 1) + + errors []error + invalid bool // The input is an invalid JavaScript RegExp + + goRegexp *bytes.Buffer +} + +// TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern. +// +// re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or +// backreference (\1, \2, ...) will cause an error. +// +// re2 (Go) has a different definition for \s: [\t\n\f\r ]. +// The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. +// +// If the pattern is invalid (not valid even in JavaScript), then this function +// returns the empty string and an error. +// +// If the pattern is valid, but incompatible (contains a lookahead or backreference), +// then this function returns the transformation (a non-empty string) AND an error. +func TransformRegExp(pattern string) (string, error) { + + if pattern == "" { + return "", nil + } + + // TODO If without \, if without (?=, (?!, then another shortcut + + parser := _RegExp_parser{ + str: pattern, + length: len(pattern), + goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)), + } + parser.read() // Pull in the first character + parser.scan() + var err error + if len(parser.errors) > 0 { + err = parser.errors[0] + } + if parser.invalid { + return "", err + } + + // Might not be re2 compatible, but is still a valid JavaScript RegExp + return parser.goRegexp.String(), err +} + +func (self *_RegExp_parser) scan() { + for self.chr != -1 { + switch self.chr { + case '\\': + self.read() + self.scanEscape(false) + case '(': + self.pass() + self.scanGroup() + case '[': + self.pass() + self.scanBracket() + case ')': + self.error(-1, "Unmatched ')'") + self.invalid = true + self.pass() + default: + self.pass() + } + } +} + +// (...) +func (self *_RegExp_parser) scanGroup() { + str := self.str[self.chrOffset:] + if len(str) > 1 { // A possibility of (?= or (?! + if str[0] == '?' { + if str[1] == '=' || str[1] == '!' { + self.error(-1, "re2: Invalid (%s) ", self.str[self.chrOffset:self.chrOffset+2]) + } + } + } + for self.chr != -1 && self.chr != ')' { + switch self.chr { + case '\\': + self.read() + self.scanEscape(false) + case '(': + self.pass() + self.scanGroup() + case '[': + self.pass() + self.scanBracket() + default: + self.pass() + continue + } + } + if self.chr != ')' { + self.error(-1, "Unterminated group") + self.invalid = true + return + } + self.pass() +} + +// [...] +func (self *_RegExp_parser) scanBracket() { + for self.chr != -1 { + if self.chr == ']' { + break + } else if self.chr == '\\' { + self.read() + self.scanEscape(true) + continue + } + self.pass() + } + if self.chr != ']' { + self.error(-1, "Unterminated character class") + self.invalid = true + return + } + self.pass() +} + +// \... +func (self *_RegExp_parser) scanEscape(inClass bool) { + offset := self.chrOffset + + var length, base uint32 + switch self.chr { + + case '0', '1', '2', '3', '4', '5', '6', '7': + var value int64 + size := 0 + for { + digit := int64(digitValue(self.chr)) + if digit >= 8 { + // Not a valid digit + break + } + value = value*8 + digit + self.read() + size += 1 + } + if size == 1 { // The number of characters read + _, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'}) + if err != nil { + self.errors = append(self.errors, err) + } + if value != 0 { + // An invalid backreference + self.error(-1, "re2: Invalid \\%d ", value) + } + return + } + tmp := []byte{'\\', 'x', '0', 0} + if value >= 16 { + tmp = tmp[0:2] + } else { + tmp = tmp[0:3] + } + tmp = strconv.AppendInt(tmp, value, 16) + _, err := self.goRegexp.Write(tmp) + if err != nil { + self.errors = append(self.errors, err) + } + return + + case '8', '9': + size := 0 + for { + digit := digitValue(self.chr) + if digit >= 10 { + // Not a valid digit + break + } + self.read() + size += 1 + } + err := self.goRegexp.WriteByte('\\') + if err != nil { + self.errors = append(self.errors, err) + } + _, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset]) + if err != nil { + self.errors = append(self.errors, err) + } + self.error(-1, "re2: Invalid \\%s ", self.str[offset:self.chrOffset]) + return + + case 'x': + self.read() + length, base = 2, 16 + + case 'u': + self.read() + length, base = 4, 16 + + case 'b': + if inClass { + _, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'}) + if err != nil { + self.errors = append(self.errors, err) + } + self.read() + return + } + fallthrough + + case 'B': + fallthrough + + case 'd', 'D', 's', 'S', 'w', 'W': + // This is slightly broken, because ECMAScript + // includes \v in \s, \S, while re2 does not + fallthrough + + case '\\': + fallthrough + + case 'f', 'n', 'r', 't', 'v': + err := self.goRegexp.WriteByte('\\') + if err != nil { + self.errors = append(self.errors, err) + } + self.pass() + return + + case 'c': + self.read() + var value int64 + if 'a' <= self.chr && self.chr <= 'z' { + value = int64(self.chr) - 'a' + 1 + } else if 'A' <= self.chr && self.chr <= 'Z' { + value = int64(self.chr) - 'A' + 1 + } else { + err := self.goRegexp.WriteByte('c') + if err != nil { + self.errors = append(self.errors, err) + } + return + } + tmp := []byte{'\\', 'x', '0', 0} + if value >= 16 { + tmp = tmp[0:2] + } else { + tmp = tmp[0:3] + } + tmp = strconv.AppendInt(tmp, value, 16) + _, err := self.goRegexp.Write(tmp) + if err != nil { + self.errors = append(self.errors, err) + } + self.read() + return + + default: + // $ is an identifier character, so we have to have + // a special case for it here + if self.chr == '$' || !isIdentifierPart(self.chr) { + // A non-identifier character needs escaping + err := self.goRegexp.WriteByte('\\') + if err != nil { + self.errors = append(self.errors, err) + } + } else { + // Unescape the character for re2 + } + self.pass() + return + } + + // Otherwise, we're a \u.... or \x... + valueOffset := self.chrOffset + + var value uint32 + { + length := length + for ; length > 0; length-- { + digit := uint32(digitValue(self.chr)) + if digit >= base { + // Not a valid digit + goto skip + } + value = value*base + digit + self.read() + } + } + + if length == 4 { + _, err := self.goRegexp.Write([]byte{ + '\\', + 'x', + '{', + self.str[valueOffset+0], + self.str[valueOffset+1], + self.str[valueOffset+2], + self.str[valueOffset+3], + '}', + }) + if err != nil { + self.errors = append(self.errors, err) + } + } else if length == 2 { + _, err := self.goRegexp.Write([]byte{ + '\\', + 'x', + self.str[valueOffset+0], + self.str[valueOffset+1], + }) + if err != nil { + self.errors = append(self.errors, err) + } + } else { + // Should never, ever get here... + self.error(-1, "re2: Illegal branch in scanEscape") + goto skip + } + + return + +skip: + _, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset]) + if err != nil { + self.errors = append(self.errors, err) + } +} + +func (self *_RegExp_parser) pass() { + if self.chr != -1 { + _, err := self.goRegexp.WriteRune(self.chr) + if err != nil { + self.errors = append(self.errors, err) + } + } + self.read() +} + +// TODO Better error reporting, use the offset, etc. +func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error { + err := fmt.Errorf(msg, msgValues...) + self.errors = append(self.errors, err) + return err +} diff --git a/vendor/github.com/robertkrimen/otto/parser/regexp_test.go b/vendor/github.com/robertkrimen/otto/parser/regexp_test.go new file mode 100644 index 00000000..3222db1a --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/regexp_test.go @@ -0,0 +1,149 @@ +package parser + +import ( + "regexp" + "testing" +) + +func TestRegExp(t *testing.T) { + tt(t, func() { + { + // err + test := func(input string, expect interface{}) { + _, err := TransformRegExp(input) + is(err, expect) + } + + test("[", "Unterminated character class") + + test("(", "Unterminated group") + + test("(?=)", "re2: Invalid (?=) ") + + test("(?=)", "re2: Invalid (?=) ") + + test("(?!)", "re2: Invalid (?!) ") + + // An error anyway + test("(?=", "re2: Invalid (?=) ") + + test("\\1", "re2: Invalid \\1 ") + + test("\\90", "re2: Invalid \\90 ") + + test("\\9123456789", "re2: Invalid \\9123456789 ") + + test("\\(?=)", "Unmatched ')'") + + test(")", "Unmatched ')'") + } + + { + // err + test := func(input, expect string, expectErr interface{}) { + output, err := TransformRegExp(input) + is(output, expect) + is(err, expectErr) + } + + test("(?!)", "(?!)", "re2: Invalid (?!) ") + + test(")", "", "Unmatched ')'") + + test("(?!))", "", "re2: Invalid (?!) ") + + test("\\0", "\\0", nil) + + test("\\1", "\\1", "re2: Invalid \\1 ") + + test("\\9123456789", "\\9123456789", "re2: Invalid \\9123456789 ") + } + + { + // err + test := func(input string, expect string) { + result, err := TransformRegExp(input) + is(err, nil) + if is(result, expect) { + _, err := regexp.Compile(result) + if !is(err, nil) { + t.Log(result) + } + } + } + + test("", "") + + test("abc", "abc") + + test(`\abc`, `abc`) + + test(`\a\b\c`, `a\bc`) + + test(`\x`, `x`) + + test(`\c`, `c`) + + test(`\cA`, `\x01`) + + test(`\cz`, `\x1a`) + + test(`\ca`, `\x01`) + + test(`\cj`, `\x0a`) + + test(`\ck`, `\x0b`) + + test(`\+`, `\+`) + + test(`[\b]`, `[\x08]`) + + test(`\u0z01\x\undefined`, `u0z01xundefined`) + + test(`\\|'|\r|\n|\t|\u2028|\u2029`, `\\|'|\r|\n|\t|\x{2028}|\x{2029}`) + + test("]", "]") + + test("}", "}") + + test("%", "%") + + test("(%)", "(%)") + + test("(?:[%\\s])", "(?:[%\\s])") + + test("[[]", "[[]") + + test("\\101", "\\x41") + + test("\\51", "\\x29") + + test("\\051", "\\x29") + + test("\\175", "\\x7d") + + test("\\04", "\\x04") + + test(`<%([\s\S]+?)%>`, `<%([\s\S]+?)%>`) + + test(`(.)^`, "(.)^") + + test(`<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`, `<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`) + + test(`\$`, `\$`) + + test(`[G-b]`, `[G-b]`) + + test(`[G-b\0]`, `[G-b\0]`) + } + }) +} + +func TestTransformRegExp(t *testing.T) { + tt(t, func() { + pattern, err := TransformRegExp(`\s+abc\s+`) + is(err, nil) + is(pattern, `\s+abc\s+`) + is(regexp.MustCompile(pattern).MatchString("\t abc def"), true) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/parser/scope.go b/vendor/github.com/robertkrimen/otto/parser/scope.go new file mode 100644 index 00000000..e1dbdda1 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/scope.go @@ -0,0 +1,44 @@ +package parser + +import ( + "github.com/robertkrimen/otto/ast" +) + +type _scope struct { + outer *_scope + allowIn bool + inIteration bool + inSwitch bool + inFunction bool + declarationList []ast.Declaration + + labels []string +} + +func (self *_parser) openScope() { + self.scope = &_scope{ + outer: self.scope, + allowIn: true, + } +} + +func (self *_parser) closeScope() { + self.scope = self.scope.outer +} + +func (self *_scope) declare(declaration ast.Declaration) { + self.declarationList = append(self.declarationList, declaration) +} + +func (self *_scope) hasLabel(name string) bool { + for _, label := range self.labels { + if label == name { + return true + } + } + if self.outer != nil && !self.inFunction { + // Crossing a function boundary to look for a label is verboten + return self.outer.hasLabel(name) + } + return false +} diff --git a/vendor/github.com/robertkrimen/otto/parser/statement.go b/vendor/github.com/robertkrimen/otto/parser/statement.go new file mode 100644 index 00000000..804cdfdc --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser/statement.go @@ -0,0 +1,940 @@ +package parser + +import ( + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/token" +) + +func (self *_parser) parseBlockStatement() *ast.BlockStatement { + node := &ast.BlockStatement{} + + // Find comments before the leading brace + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.LEADING) + self.comments.Unset() + } + + node.LeftBrace = self.expect(token.LEFT_BRACE) + node.List = self.parseStatementList() + + if self.mode&StoreComments != 0 { + self.comments.Unset() + self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.FINAL) + self.comments.AfterBlock() + } + + node.RightBrace = self.expect(token.RIGHT_BRACE) + + // Find comments after the trailing brace + if self.mode&StoreComments != 0 { + self.comments.ResetLineBreak() + self.comments.CommentMap.AddComments(node, self.comments.Fetch(), ast.TRAILING) + } + + return node +} + +func (self *_parser) parseEmptyStatement() ast.Statement { + idx := self.expect(token.SEMICOLON) + return &ast.EmptyStatement{Semicolon: idx} +} + +func (self *_parser) parseStatementList() (list []ast.Statement) { + for self.token != token.RIGHT_BRACE && self.token != token.EOF { + statement := self.parseStatement() + list = append(list, statement) + } + + return +} + +func (self *_parser) parseStatement() ast.Statement { + + if self.token == token.EOF { + self.errorUnexpectedToken(self.token) + return &ast.BadStatement{From: self.idx, To: self.idx + 1} + } + + if self.mode&StoreComments != 0 { + self.comments.ResetLineBreak() + } + + switch self.token { + case token.SEMICOLON: + return self.parseEmptyStatement() + case token.LEFT_BRACE: + return self.parseBlockStatement() + case token.IF: + return self.parseIfStatement() + case token.DO: + statement := self.parseDoWhileStatement() + self.comments.PostProcessNode(statement) + return statement + case token.WHILE: + return self.parseWhileStatement() + case token.FOR: + return self.parseForOrForInStatement() + case token.BREAK: + return self.parseBreakStatement() + case token.CONTINUE: + return self.parseContinueStatement() + case token.DEBUGGER: + return self.parseDebuggerStatement() + case token.WITH: + return self.parseWithStatement() + case token.VAR: + return self.parseVariableStatement() + case token.FUNCTION: + return self.parseFunctionStatement() + case token.SWITCH: + return self.parseSwitchStatement() + case token.RETURN: + return self.parseReturnStatement() + case token.THROW: + return self.parseThrowStatement() + case token.TRY: + return self.parseTryStatement() + } + + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + + expression := self.parseExpression() + + if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON { + // LabelledStatement + colon := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() // : + + label := identifier.Name + for _, value := range self.scope.labels { + if label == value { + self.error(identifier.Idx0(), "Label '%s' already exists", label) + } + } + var labelComments []*ast.Comment + if self.mode&StoreComments != 0 { + labelComments = self.comments.FetchAll() + } + self.scope.labels = append(self.scope.labels, label) // Push the label + statement := self.parseStatement() + self.scope.labels = self.scope.labels[:len(self.scope.labels)-1] // Pop the label + exp := &ast.LabelledStatement{ + Label: identifier, + Colon: colon, + Statement: statement, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(exp, labelComments, ast.LEADING) + } + + return exp + } + + self.optionalSemicolon() + + statement := &ast.ExpressionStatement{ + Expression: expression, + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(statement, comments, ast.LEADING) + } + return statement +} + +func (self *_parser) parseTryStatement() ast.Statement { + var tryComments []*ast.Comment + if self.mode&StoreComments != 0 { + tryComments = self.comments.FetchAll() + } + node := &ast.TryStatement{ + Try: self.expect(token.TRY), + Body: self.parseBlockStatement(), + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, tryComments, ast.LEADING) + self.comments.CommentMap.AddComments(node.Body, self.comments.FetchAll(), ast.TRAILING) + } + + if self.token == token.CATCH { + catch := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + self.expect(token.LEFT_PARENTHESIS) + if self.token != token.IDENTIFIER { + self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadStatement{From: catch, To: self.idx} + } else { + identifier := self.parseIdentifier() + self.expect(token.RIGHT_PARENTHESIS) + node.Catch = &ast.CatchStatement{ + Catch: catch, + Parameter: identifier, + Body: self.parseBlockStatement(), + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node.Catch.Body, self.comments.FetchAll(), ast.TRAILING) + } + } + } + + if self.token == token.FINALLY { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + if self.mode&StoreComments != 0 { + tryComments = self.comments.FetchAll() + } + + node.Finally = self.parseBlockStatement() + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node.Finally, tryComments, ast.LEADING) + } + } + + if node.Catch == nil && node.Finally == nil { + self.error(node.Try, "Missing catch or finally after try") + return &ast.BadStatement{From: node.Try, To: node.Body.Idx1()} + } + + return node +} + +func (self *_parser) parseFunctionParameterList() *ast.ParameterList { + opening := self.expect(token.LEFT_PARENTHESIS) + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + var list []*ast.Identifier + for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF { + if self.token != token.IDENTIFIER { + self.expect(token.IDENTIFIER) + } else { + identifier := self.parseIdentifier() + list = append(list, identifier) + } + if self.token != token.RIGHT_PARENTHESIS { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COMMA) + } + } + closing := self.expect(token.RIGHT_PARENTHESIS) + + return &ast.ParameterList{ + Opening: opening, + List: list, + Closing: closing, + } +} + +func (self *_parser) parseParameterList() (list []string) { + for self.token != token.EOF { + if self.token != token.IDENTIFIER { + self.expect(token.IDENTIFIER) + } + list = append(list, self.literal) + self.next() + if self.token != token.EOF { + self.expect(token.COMMA) + } + } + return +} + +func (self *_parser) parseFunctionStatement() *ast.FunctionStatement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + function := &ast.FunctionStatement{ + Function: self.parseFunction(true), + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(function, comments, ast.LEADING) + } + + return function +} + +func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral { + + node := &ast.FunctionLiteral{ + Function: self.expect(token.FUNCTION), + } + + var name *ast.Identifier + if self.token == token.IDENTIFIER { + name = self.parseIdentifier() + if declaration { + self.scope.declare(&ast.FunctionDeclaration{ + Function: node, + }) + } + } else if declaration { + // Use expect error handling + self.expect(token.IDENTIFIER) + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + node.Name = name + node.ParameterList = self.parseFunctionParameterList() + self.parseFunctionBlock(node) + node.Source = self.slice(node.Idx0(), node.Idx1()) + + return node +} + +func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) { + { + self.openScope() + inFunction := self.scope.inFunction + self.scope.inFunction = true + defer func() { + self.scope.inFunction = inFunction + self.closeScope() + }() + node.Body = self.parseBlockStatement() + node.DeclarationList = self.scope.declarationList + } +} + +func (self *_parser) parseDebuggerStatement() ast.Statement { + idx := self.expect(token.DEBUGGER) + + node := &ast.DebuggerStatement{ + Debugger: idx, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.TRAILING) + } + + self.semicolon() + return node +} + +func (self *_parser) parseReturnStatement() ast.Statement { + idx := self.expect(token.RETURN) + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + + if !self.scope.inFunction { + self.error(idx, "Illegal return statement") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} + } + + node := &ast.ReturnStatement{ + Return: idx, + } + + if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF { + node.Argument = self.parseExpression() + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + self.semicolon() + + return node +} + +func (self *_parser) parseThrowStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.THROW) + + if self.implicitSemicolon { + if self.chr == -1 { // Hackish + self.error(idx, "Unexpected end of input") + } else { + self.error(idx, "Illegal newline after throw") + } + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} + } + + node := &ast.ThrowStatement{ + Throw: self.idx, + Argument: self.parseExpression(), + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + self.semicolon() + + return node +} + +func (self *_parser) parseSwitchStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.SWITCH) + if self.mode&StoreComments != 0 { + comments = append(comments, self.comments.FetchAll()...) + } + self.expect(token.LEFT_PARENTHESIS) + node := &ast.SwitchStatement{ + Discriminant: self.parseExpression(), + Default: -1, + } + self.expect(token.RIGHT_PARENTHESIS) + if self.mode&StoreComments != 0 { + comments = append(comments, self.comments.FetchAll()...) + } + + self.expect(token.LEFT_BRACE) + + inSwitch := self.scope.inSwitch + self.scope.inSwitch = true + defer func() { + self.scope.inSwitch = inSwitch + }() + + for index := 0; self.token != token.EOF; index++ { + if self.token == token.RIGHT_BRACE { + self.next() + break + } + + clause := self.parseCaseStatement() + if clause.Test == nil { + if node.Default != -1 { + self.error(clause.Case, "Already saw a default in switch") + } + node.Default = index + } + node.Body = append(node.Body, clause) + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + return node +} + +func (self *_parser) parseWithStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.WITH) + var withComments []*ast.Comment + if self.mode&StoreComments != 0 { + withComments = self.comments.FetchAll() + } + + self.expect(token.LEFT_PARENTHESIS) + + node := &ast.WithStatement{ + Object: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + + if self.mode&StoreComments != 0 { + //comments = append(comments, self.comments.FetchAll()...) + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, withComments, ast.WITH) + } + + node.Body = self.parseStatement() + + return node +} + +func (self *_parser) parseCaseStatement() *ast.CaseStatement { + node := &ast.CaseStatement{ + Case: self.idx, + } + + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + self.comments.Unset() + } + + if self.token == token.DEFAULT { + self.next() + } else { + self.expect(token.CASE) + node.Test = self.parseExpression() + } + + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COLON) + + for { + if self.token == token.EOF || + self.token == token.RIGHT_BRACE || + self.token == token.CASE || + self.token == token.DEFAULT { + break + } + consequent := self.parseStatement() + node.Consequent = append(node.Consequent, consequent) + } + + // Link the comments to the case statement + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + return node +} + +func (self *_parser) parseIterationStatement() ast.Statement { + inIteration := self.scope.inIteration + self.scope.inIteration = true + defer func() { + self.scope.inIteration = inIteration + }() + return self.parseStatement() +} + +func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement { + + // Already have consumed " in" + + source := self.parseExpression() + self.expect(token.RIGHT_PARENTHESIS) + body := self.parseIterationStatement() + + forin := &ast.ForInStatement{ + Into: into, + Source: source, + Body: body, + } + + return forin +} + +func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement { + + // Already have consumed " ;" + + var test, update ast.Expression + + if self.token != token.SEMICOLON { + test = self.parseExpression() + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.SEMICOLON) + + if self.token != token.RIGHT_PARENTHESIS { + update = self.parseExpression() + } + self.expect(token.RIGHT_PARENTHESIS) + body := self.parseIterationStatement() + + forstatement := &ast.ForStatement{ + Initializer: initializer, + Test: test, + Update: update, + Body: body, + } + + return forstatement +} + +func (self *_parser) parseForOrForInStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.FOR) + var forComments []*ast.Comment + if self.mode&StoreComments != 0 { + forComments = self.comments.FetchAll() + } + self.expect(token.LEFT_PARENTHESIS) + + var left []ast.Expression + + forIn := false + if self.token != token.SEMICOLON { + + allowIn := self.scope.allowIn + self.scope.allowIn = false + if self.token == token.VAR { + var_ := self.idx + var varComments []*ast.Comment + if self.mode&StoreComments != 0 { + varComments = self.comments.FetchAll() + self.comments.Unset() + } + self.next() + list := self.parseVariableDeclarationList(var_) + if len(list) == 1 && self.token == token.IN { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() // in + forIn = true + left = []ast.Expression{list[0]} // There is only one declaration + } else { + left = list + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(left[0], varComments, ast.LEADING) + } + } else { + left = append(left, self.parseExpression()) + if self.token == token.IN { + self.next() + forIn = true + } + } + self.scope.allowIn = allowIn + } + + if forIn { + switch left[0].(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.VariableExpression: + // These are all acceptable + default: + self.error(idx, "Invalid left-hand side in for-in") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} + } + + forin := self.parseForIn(left[0]) + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(forin, comments, ast.LEADING) + self.comments.CommentMap.AddComments(forin, forComments, ast.FOR) + } + return forin + } + + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.SEMICOLON) + initializer := &ast.SequenceExpression{Sequence: left} + forstatement := self.parseFor(initializer) + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(forstatement, comments, ast.LEADING) + self.comments.CommentMap.AddComments(forstatement, forComments, ast.FOR) + } + return forstatement +} + +func (self *_parser) parseVariableStatement() *ast.VariableStatement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.VAR) + + list := self.parseVariableDeclarationList(idx) + + statement := &ast.VariableStatement{ + Var: idx, + List: list, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(statement, comments, ast.LEADING) + self.comments.Unset() + } + self.semicolon() + + return statement +} + +func (self *_parser) parseDoWhileStatement() ast.Statement { + inIteration := self.scope.inIteration + self.scope.inIteration = true + defer func() { + self.scope.inIteration = inIteration + }() + + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.DO) + var doComments []*ast.Comment + if self.mode&StoreComments != 0 { + doComments = self.comments.FetchAll() + } + + node := &ast.DoWhileStatement{} + if self.token == token.LEFT_BRACE { + node.Body = self.parseBlockStatement() + } else { + node.Body = self.parseStatement() + } + + self.expect(token.WHILE) + var whileComments []*ast.Comment + if self.mode&StoreComments != 0 { + whileComments = self.comments.FetchAll() + } + self.expect(token.LEFT_PARENTHESIS) + node.Test = self.parseExpression() + self.expect(token.RIGHT_PARENTHESIS) + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, doComments, ast.DO) + self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE) + } + + return node +} + +func (self *_parser) parseWhileStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.WHILE) + + var whileComments []*ast.Comment + if self.mode&StoreComments != 0 { + whileComments = self.comments.FetchAll() + } + + self.expect(token.LEFT_PARENTHESIS) + node := &ast.WhileStatement{ + Test: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + node.Body = self.parseIterationStatement() + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE) + } + + return node +} + +func (self *_parser) parseIfStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.IF) + var ifComments []*ast.Comment + if self.mode&StoreComments != 0 { + ifComments = self.comments.FetchAll() + } + + self.expect(token.LEFT_PARENTHESIS) + node := &ast.IfStatement{ + If: self.idx, + Test: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + if self.token == token.LEFT_BRACE { + node.Consequent = self.parseBlockStatement() + } else { + node.Consequent = self.parseStatement() + } + + if self.token == token.ELSE { + self.next() + node.Alternate = self.parseStatement() + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, ifComments, ast.IF) + } + + return node +} + +func (self *_parser) parseSourceElement() ast.Statement { + statement := self.parseStatement() + //self.comments.Unset() + return statement +} + +func (self *_parser) parseSourceElements() []ast.Statement { + body := []ast.Statement(nil) + + for { + if self.token != token.STRING { + break + } + body = append(body, self.parseSourceElement()) + } + + for self.token != token.EOF { + body = append(body, self.parseSourceElement()) + } + + return body +} + +func (self *_parser) parseProgram() *ast.Program { + self.openScope() + defer self.closeScope() + return &ast.Program{ + Body: self.parseSourceElements(), + DeclarationList: self.scope.declarationList, + File: self.file, + } +} + +func (self *_parser) parseBreakStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.BREAK) + semicolon := self.implicitSemicolon + if self.token == token.SEMICOLON { + semicolon = true + self.next() + } + + if semicolon || self.token == token.RIGHT_BRACE { + self.implicitSemicolon = false + if !self.scope.inIteration && !self.scope.inSwitch { + goto illegal + } + breakStatement := &ast.BranchStatement{ + Idx: idx, + Token: token.BREAK, + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING) + self.comments.CommentMap.AddComments(breakStatement, self.comments.FetchAll(), ast.TRAILING) + } + + return breakStatement + } + + if self.token == token.IDENTIFIER { + identifier := self.parseIdentifier() + if !self.scope.hasLabel(identifier.Name) { + self.error(idx, "Undefined label '%s'", identifier.Name) + return &ast.BadStatement{From: idx, To: identifier.Idx1()} + } + self.semicolon() + breakStatement := &ast.BranchStatement{ + Idx: idx, + Token: token.BREAK, + Label: identifier, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING) + } + + return breakStatement + } + + self.expect(token.IDENTIFIER) + +illegal: + self.error(idx, "Illegal break statement") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} +} + +func (self *_parser) parseContinueStatement() ast.Statement { + idx := self.expect(token.CONTINUE) + semicolon := self.implicitSemicolon + if self.token == token.SEMICOLON { + semicolon = true + self.next() + } + + if semicolon || self.token == token.RIGHT_BRACE { + self.implicitSemicolon = false + if !self.scope.inIteration { + goto illegal + } + return &ast.BranchStatement{ + Idx: idx, + Token: token.CONTINUE, + } + } + + if self.token == token.IDENTIFIER { + identifier := self.parseIdentifier() + if !self.scope.hasLabel(identifier.Name) { + self.error(idx, "Undefined label '%s'", identifier.Name) + return &ast.BadStatement{From: idx, To: identifier.Idx1()} + } + if !self.scope.inIteration { + goto illegal + } + self.semicolon() + return &ast.BranchStatement{ + Idx: idx, + Token: token.CONTINUE, + Label: identifier, + } + } + + self.expect(token.IDENTIFIER) + +illegal: + self.error(idx, "Illegal continue statement") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} +} + +// Find the next statement after an error (recover) +func (self *_parser) nextStatement() { + for { + switch self.token { + case token.BREAK, token.CONTINUE, + token.FOR, token.IF, token.RETURN, token.SWITCH, + token.VAR, token.DO, token.TRY, token.WITH, + token.WHILE, token.THROW, token.CATCH, token.FINALLY: + // Return only if parser made some progress since last + // sync or if it has not reached 10 next calls without + // progress. Otherwise consume at least one token to + // avoid an endless parser loop + if self.idx == self.recover.idx && self.recover.count < 10 { + self.recover.count++ + return + } + if self.idx > self.recover.idx { + self.recover.idx = self.idx + self.recover.count = 0 + return + } + // Reaching here indicates a parser bug, likely an + // incorrect token list in this function, but it only + // leads to skipping of possibly correct code if a + // previous error is present, and thus is preferred + // over a non-terminating parse. + case token.EOF: + return + } + self.next() + } +} diff --git a/vendor/github.com/robertkrimen/otto/parser_test.go b/vendor/github.com/robertkrimen/otto/parser_test.go new file mode 100644 index 00000000..7db43d23 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/parser_test.go @@ -0,0 +1,42 @@ +package otto + +import ( + "testing" +) + +func TestPersistence(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + function abc() { return 1; } + abc.toString(); + `, "function abc() { return 1; }") + + test(` + function def() { return 3.14159; } + [ abc.toString(), def.toString() ]; + `, "function abc() { return 1; },function def() { return 3.14159; }") + + test(` + eval("function ghi() { return 'ghi' }"); + [ abc.toString(), def.toString(), ghi.toString() ]; + `, "function abc() { return 1; },function def() { return 3.14159; },function ghi() { return 'ghi' }") + + test(` + [ abc.toString(), def.toString(), ghi.toString() ]; + `, "function abc() { return 1; },function def() { return 3.14159; },function ghi() { return 'ghi' }") + + test(`/* + + + + + + + + + + */`, UndefinedValue()) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/property.go b/vendor/github.com/robertkrimen/otto/property.go new file mode 100644 index 00000000..5445eccd --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/property.go @@ -0,0 +1,220 @@ +package otto + +// property + +type _propertyMode int + +const ( + modeWriteMask _propertyMode = 0700 + modeEnumerateMask = 0070 + modeConfigureMask = 0007 + modeOnMask = 0111 + modeOffMask = 0000 + modeSetMask = 0222 // If value is 2, then mode is neither "On" nor "Off" +) + +type _propertyGetSet [2]*_object + +var _nilGetSetObject _object = _object{} + +type _property struct { + value interface{} + mode _propertyMode +} + +func (self _property) writable() bool { + return self.mode&modeWriteMask == modeWriteMask&modeOnMask +} + +func (self *_property) writeOn() { + self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeOnMask) +} + +func (self *_property) writeOff() { + self.mode &= ^modeWriteMask +} + +func (self *_property) writeClear() { + self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeSetMask) +} + +func (self _property) writeSet() bool { + return 0 == self.mode&modeWriteMask&modeSetMask +} + +func (self _property) enumerable() bool { + return self.mode&modeEnumerateMask == modeEnumerateMask&modeOnMask +} + +func (self *_property) enumerateOn() { + self.mode = (self.mode & ^modeEnumerateMask) | (modeEnumerateMask & modeOnMask) +} + +func (self *_property) enumerateOff() { + self.mode &= ^modeEnumerateMask +} + +func (self _property) enumerateSet() bool { + return 0 == self.mode&modeEnumerateMask&modeSetMask +} + +func (self _property) configurable() bool { + return self.mode&modeConfigureMask == modeConfigureMask&modeOnMask +} + +func (self *_property) configureOn() { + self.mode = (self.mode & ^modeConfigureMask) | (modeConfigureMask & modeOnMask) +} + +func (self *_property) configureOff() { + self.mode &= ^modeConfigureMask +} + +func (self _property) configureSet() bool { + return 0 == self.mode&modeConfigureMask&modeSetMask +} + +func (self _property) copy() *_property { + property := self + return &property +} + +func (self _property) get(this *_object) Value { + switch value := self.value.(type) { + case Value: + return value + case _propertyGetSet: + if value[0] != nil { + return value[0].call(toValue(this), nil, false, nativeFrame) + } + } + return Value{} +} + +func (self _property) isAccessorDescriptor() bool { + setGet, test := self.value.(_propertyGetSet) + return test && (setGet[0] != nil || setGet[1] != nil) +} + +func (self _property) isDataDescriptor() bool { + if self.writeSet() { // Either "On" or "Off" + return true + } + value, valid := self.value.(Value) + return valid && !value.isEmpty() +} + +func (self _property) isGenericDescriptor() bool { + return !(self.isDataDescriptor() || self.isAccessorDescriptor()) +} + +func (self _property) isEmpty() bool { + return self.mode == 0222 && self.isGenericDescriptor() +} + +// _enumerableValue, _enumerableTrue, _enumerableFalse? +// .enumerableValue() .enumerableExists() + +func toPropertyDescriptor(rt *_runtime, value Value) (descriptor _property) { + objectDescriptor := value._object() + if objectDescriptor == nil { + panic(rt.panicTypeError()) + } + + { + descriptor.mode = modeSetMask // Initially nothing is set + if objectDescriptor.hasProperty("enumerable") { + if objectDescriptor.get("enumerable").bool() { + descriptor.enumerateOn() + } else { + descriptor.enumerateOff() + } + } + + if objectDescriptor.hasProperty("configurable") { + if objectDescriptor.get("configurable").bool() { + descriptor.configureOn() + } else { + descriptor.configureOff() + } + } + + if objectDescriptor.hasProperty("writable") { + if objectDescriptor.get("writable").bool() { + descriptor.writeOn() + } else { + descriptor.writeOff() + } + } + } + + var getter, setter *_object + getterSetter := false + + if objectDescriptor.hasProperty("get") { + value := objectDescriptor.get("get") + if value.IsDefined() { + if !value.isCallable() { + panic(rt.panicTypeError()) + } + getter = value._object() + getterSetter = true + } else { + getter = &_nilGetSetObject + getterSetter = true + } + } + + if objectDescriptor.hasProperty("set") { + value := objectDescriptor.get("set") + if value.IsDefined() { + if !value.isCallable() { + panic(rt.panicTypeError()) + } + setter = value._object() + getterSetter = true + } else { + setter = &_nilGetSetObject + getterSetter = true + } + } + + if getterSetter { + if descriptor.writeSet() { + panic(rt.panicTypeError()) + } + descriptor.value = _propertyGetSet{getter, setter} + } + + if objectDescriptor.hasProperty("value") { + if getterSetter { + panic(rt.panicTypeError()) + } + descriptor.value = objectDescriptor.get("value") + } + + return +} + +func (self *_runtime) fromPropertyDescriptor(descriptor _property) *_object { + object := self.newObject() + if descriptor.isDataDescriptor() { + object.defineProperty("value", descriptor.value.(Value), 0111, false) + object.defineProperty("writable", toValue_bool(descriptor.writable()), 0111, false) + } else if descriptor.isAccessorDescriptor() { + getSet := descriptor.value.(_propertyGetSet) + get := Value{} + if getSet[0] != nil { + get = toValue_object(getSet[0]) + } + set := Value{} + if getSet[1] != nil { + set = toValue_object(getSet[1]) + } + object.defineProperty("get", get, 0111, false) + object.defineProperty("set", set, 0111, false) + } + object.defineProperty("enumerable", toValue_bool(descriptor.enumerable()), 0111, false) + object.defineProperty("configurable", toValue_bool(descriptor.configurable()), 0111, false) + return object +} diff --git a/vendor/github.com/robertkrimen/otto/reflect_test.go b/vendor/github.com/robertkrimen/otto/reflect_test.go new file mode 100644 index 00000000..703034ea --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/reflect_test.go @@ -0,0 +1,759 @@ +package otto + +import ( + "fmt" + "math" + "reflect" + "testing" +) + +type _abcStruct struct { + Abc bool + Def int + Ghi string + Jkl interface{} + Mno _mnoStruct + Pqr map[string]int8 +} + +func (abc _abcStruct) String() string { + return abc.Ghi +} + +func (abc *_abcStruct) FuncPointer() string { + return "abc" +} + +func (abc _abcStruct) Func() { + return +} + +func (abc _abcStruct) FuncReturn1() string { + return "abc" +} + +func (abc _abcStruct) FuncReturn2() (string, error) { + return "def", nil +} + +func (abc _abcStruct) Func1Return1(a string) string { + return a +} + +func (abc _abcStruct) Func2Return1(x, y string) string { + return x + y +} + +func (abc _abcStruct) FuncEllipsis(xyz ...string) int { + return len(xyz) +} + +func (abc _abcStruct) FuncReturnStruct() _mnoStruct { + return _mnoStruct{} +} + +func (abs _abcStruct) Func1Int(i int) int { + return i + 1 +} + +func (abs _abcStruct) Func1Int8(i int8) int8 { + return i + 1 +} + +func (abs _abcStruct) Func1Int16(i int16) int16 { + return i + 1 +} + +func (abs _abcStruct) Func1Int32(i int32) int32 { + return i + 1 +} + +func (abs _abcStruct) Func1Int64(i int64) int64 { + return i + 1 +} + +func (abs _abcStruct) Func1Uint(i uint) uint { + return i + 1 +} + +func (abs _abcStruct) Func1Uint8(i uint8) uint8 { + return i + 1 +} + +func (abs _abcStruct) Func1Uint16(i uint16) uint16 { + return i + 1 +} + +func (abs _abcStruct) Func1Uint32(i uint32) uint32 { + return i + 1 +} + +func (abs _abcStruct) Func1Uint64(i uint64) uint64 { + return i + 1 +} + +func (abs _abcStruct) Func2Int(i, j int) int { + return i + j +} + +func (abs _abcStruct) Func2StringInt(s string, i int) string { + return fmt.Sprintf("%v:%v", s, i) +} + +func (abs _abcStruct) Func1IntVariadic(a ...int) int { + t := 0 + for _, i := range a { + t += i + } + return t +} + +func (abs _abcStruct) Func2IntVariadic(s string, a ...int) string { + t := 0 + for _, i := range a { + t += i + } + return fmt.Sprintf("%v:%v", s, t) +} + +func (abs _abcStruct) Func2IntArrayVariadic(s string, a ...[]int) string { + t := 0 + for _, i := range a { + for _, j := range i { + t += j + } + } + return fmt.Sprintf("%v:%v", s, t) +} + +type _mnoStruct struct { + Ghi string +} + +func (mno _mnoStruct) Func() string { + return "mno" +} + +func TestReflect(t *testing.T) { + if true { + return + } + tt(t, func() { + // Testing dbgf + // These should panic + toValue("Xyzzy").toReflectValue(reflect.Ptr) + stringToReflectValue("Xyzzy", reflect.Ptr) + }) +} + +func Test_reflectStruct(t *testing.T) { + tt(t, func() { + test, vm := test() + + // _abcStruct + { + abc := &_abcStruct{} + vm.Set("abc", abc) + + test(` + [ abc.Abc, abc.Ghi ]; + `, "false,") + + abc.Abc = true + abc.Ghi = "Nothing happens." + + test(` + [ abc.Abc, abc.Ghi ]; + `, "true,Nothing happens.") + + *abc = _abcStruct{} + + test(` + [ abc.Abc, abc.Ghi ]; + `, "false,") + + abc.Abc = true + abc.Ghi = "Xyzzy" + vm.Set("abc", abc) + + test(` + [ abc.Abc, abc.Ghi ]; + `, "true,Xyzzy") + + is(abc.Abc, true) + test(` + abc.Abc = false; + abc.Def = 451; + abc.Ghi = "Nothing happens."; + abc.abc = "Something happens."; + [ abc.Def, abc.abc ]; + `, "451,Something happens.") + is(abc.Abc, false) + is(abc.Def, 451) + is(abc.Ghi, "Nothing happens.") + + test(` + delete abc.Def; + delete abc.abc; + [ abc.Def, abc.abc ]; + `, "451,") + is(abc.Def, 451) + + test(` + abc.FuncPointer(); + `, "abc") + + test(` + abc.Func(); + `, "undefined") + + test(` + abc.FuncReturn1(); + `, "abc") + + test(` + abc.Func1Return1("abc"); + `, "abc") + + test(` + abc.Func2Return1("abc", "def"); + `, "abcdef") + + test(` + abc.FuncEllipsis("abc", "def", "ghi"); + `, 3) + + test(` + ret = abc.FuncReturn2(); + if (ret && ret.length && ret.length == 2 && ret[0] == "def" && ret[1] === undefined) { + true; + } else { + false; + } + `, true) + + test(` + abc.FuncReturnStruct(); + `, "[object Object]") + + test(` + abc.FuncReturnStruct().Func(); + `, "mno") + + test(` + abc.Func1Int(1); + `, 2) + + test(` + abc.Func1Int(0x01 & 0x01); + `, 2) + + test(`raise: + abc.Func1Int(1.1); + `, "RangeError: converting float64 to int would cause loss of precision") + + test(` + var v = 1; + abc.Func1Int(v + 1); + `, 3) + + test(` + abc.Func2Int(1, 2); + `, 3) + + test(` + abc.Func1Int8(1); + `, 2) + + test(` + abc.Func1Int16(1); + `, 2) + + test(` + abc.Func1Int32(1); + `, 2) + + test(` + abc.Func1Int64(1); + `, 2) + + test(` + abc.Func1Uint(1); + `, 2) + + test(` + abc.Func1Uint8(1); + `, 2) + + test(` + abc.Func1Uint16(1); + `, 2) + + test(` + abc.Func1Uint32(1); + `, 2) + + test(` + abc.Func1Uint64(1); + `, 2) + + test(` + abc.Func2StringInt("test", 1); + `, "test:1") + + test(` + abc.Func1IntVariadic(1, 2); + `, 3) + + test(` + abc.Func2IntVariadic("test", 1, 2); + `, "test:3") + + test(` + abc.Func2IntVariadic("test", [1, 2]); + `, "test:3") + + test(` + abc.Func2IntArrayVariadic("test", [1, 2]); + `, "test:3") + + test(` + abc.Func2IntArrayVariadic("test", [1, 2], [3, 4]); + `, "test:10") + + test(` + abc.Func2IntArrayVariadic("test", [[1, 2], [3, 4]]); + `, "test:10") + } + }) +} + +func Test_reflectMap(t *testing.T) { + tt(t, func() { + test, vm := test() + + // map[string]string + { + abc := map[string]string{ + "Xyzzy": "Nothing happens.", + "def": "1", + } + vm.Set("abc", abc) + + test(` + abc.xyz = "pqr"; + [ abc.Xyzzy, abc.def, abc.ghi ]; + `, "Nothing happens.,1,") + + is(abc["xyz"], "pqr") + } + + // map[string]float64 + { + abc := map[string]float64{ + "Xyzzy": math.Pi, + "def": 1, + } + vm.Set("abc", abc) + + test(` + abc.xyz = "pqr"; + abc.jkl = 10; + [ abc.Xyzzy, abc.def, abc.ghi ]; + `, "3.141592653589793,1,") + + is(abc["xyz"], math.NaN()) + is(abc["jkl"], float64(10)) + } + + // map[string]int32 + { + abc := map[string]int32{ + "Xyzzy": 3, + "def": 1, + } + vm.Set("abc", abc) + + test(` + abc.xyz = "pqr"; + abc.jkl = 10; + [ abc.Xyzzy, abc.def, abc.ghi ]; + `, "3,1,") + + is(abc["xyz"], 0) + is(abc["jkl"], int32(10)) + + test(` + delete abc["Xyzzy"]; + `) + + _, exists := abc["Xyzzy"] + is(exists, false) + is(abc["Xyzzy"], 0) + } + + // map[int32]string + { + abc := map[int32]string{ + 0: "abc", + 1: "def", + } + vm.Set("abc", abc) + + test(` + abc[2] = "pqr"; + //abc.jkl = 10; + abc[3] = 10; + [ abc[0], abc[1], abc[2], abc[3] ] + `, "abc,def,pqr,10") + + is(abc[2], "pqr") + is(abc[3], "10") + + test(` + delete abc[2]; + `) + + _, exists := abc[2] + is(exists, false) + } + + }) +} + +func Test_reflectMapIterateKeys(t *testing.T) { + tt(t, func() { + test, vm := test() + + // map[string]interface{} + { + abc := map[string]interface{}{ + "Xyzzy": "Nothing happens.", + "def": 1, + } + vm.Set("abc", abc) + test(` + var keys = []; + for (var key in abc) { + keys.push(key); + } + keys.sort(); + keys; + `, "Xyzzy,def") + } + + // map[uint]interface{} + { + abc := map[uint]interface{}{ + 456: "Nothing happens.", + 123: 1, + } + vm.Set("abc", abc) + test(` + var keys = []; + for (var key in abc) { + keys.push(key); + } + keys.sort(); + keys; + `, "123,456") + } + + // map[byte]interface{} + { + abc := map[byte]interface{}{ + 10: "Nothing happens.", + 20: 1, + } + vm.Set("abc", abc) + test(` + for (var key in abc) { + abc[key] = "123"; + } + `) + is(abc[10], "123") + is(abc[20], "123") + } + + }) +} + +func Test_reflectSlice(t *testing.T) { + tt(t, func() { + test, vm := test() + + // []bool + { + abc := []bool{ + false, + true, + true, + false, + } + vm.Set("abc", abc) + + test(` + abc; + `, "false,true,true,false") + + test(` + abc[0] = true; + abc[abc.length-1] = true; + delete abc[2]; + abc; + `, "true,true,false,true") + + is(abc, []bool{true, true, false, true}) + is(abc[len(abc)-1], true) + } + + // []int32 + { + abc := make([]int32, 4) + vm.Set("abc", abc) + + test(` + abc; + `, "0,0,0,0") + + test(`raise: + abc[0] = "42"; + abc[1] = 4.2; + abc[2] = 3.14; + abc; + `, "RangeError: 4.2 to reflect.Kind: int32") + + is(abc, []int32{42, 0, 0, 0}) + + test(` + delete abc[1]; + delete abc[2]; + `) + is(abc[1], 0) + is(abc[2], 0) + } + }) +} + +func Test_reflectArray(t *testing.T) { + tt(t, func() { + test, vm := test() + + // []bool + { + abc := [4]bool{ + false, + true, + true, + false, + } + vm.Set("abc", abc) + + test(` + abc; + `, "false,true,true,false") + // Unaddressable array + + test(` + abc[0] = true; + abc[abc.length-1] = true; + abc; + `, "false,true,true,false") + // Again, unaddressable array + + is(abc, [4]bool{false, true, true, false}) + is(abc[len(abc)-1], false) + // ... + } + // []int32 + { + abc := make([]int32, 4) + vm.Set("abc", abc) + + test(` + abc; + `, "0,0,0,0") + + test(`raise: + abc[0] = "42"; + abc[1] = 4.2; + abc[2] = 3.14; + abc; + `, "RangeError: 4.2 to reflect.Kind: int32") + + is(abc, []int32{42, 0, 0, 0}) + } + + // []bool + { + abc := [4]bool{ + false, + true, + true, + false, + } + vm.Set("abc", &abc) + + test(` + abc; + `, "false,true,true,false") + + test(` + abc[0] = true; + abc[abc.length-1] = true; + delete abc[2]; + abc; + `, "true,true,false,true") + + is(abc, [4]bool{true, true, false, true}) + is(abc[len(abc)-1], true) + } + + // no common type + { + test(` + abc = [1, 2.2, "str"]; + abc; + `, "1,2.2,str") + val, err := vm.Get("abc") + is(err, nil) + abc, err := val.Export() + is(err, nil) + is(abc, []interface{}{int64(1), 2.2, "str"}) + } + + // common type int + { + test(` + abc = [1, 2, 3]; + abc; + `, "1,2,3") + val, err := vm.Get("abc") + is(err, nil) + abc, err := val.Export() + is(err, nil) + is(abc, []int64{1, 2, 3}) + } + + // common type string + { + + test(` + abc = ["str1", "str2", "str3"]; + abc; + `, "str1,str2,str3") + + val, err := vm.Get("abc") + is(err, nil) + abc, err := val.Export() + is(err, nil) + is(abc, []string{"str1", "str2", "str3"}) + } + + // issue #269 + { + called := false + vm.Set("blah", func(c FunctionCall) Value { + v, err := c.Argument(0).Export() + is(err, nil) + is(v, []int64{3}) + called = true + return UndefinedValue() + }) + is(called, false) + test(`var x = 3; blah([x])`) + is(called, true) + } + }) +} + +func Test_reflectArray_concat(t *testing.T) { + tt(t, func() { + test, vm := test() + + vm.Set("ghi", []string{"jkl", "mno"}) + vm.Set("pqr", []interface{}{"jkl", 42, 3.14159, true}) + test(` + var def = { + "abc": ["abc"], + "xyz": ["xyz"] + }; + xyz = pqr.concat(ghi, def.abc, def, def.xyz); + [ xyz, xyz.length ]; + `, "jkl,42,3.14159,true,jkl,mno,abc,[object Object],xyz,9") + }) +} + +func Test_reflectMapInterface(t *testing.T) { + tt(t, func() { + test, vm := test() + + { + abc := map[string]interface{}{ + "Xyzzy": "Nothing happens.", + "def": "1", + "jkl": "jkl", + } + vm.Set("abc", abc) + vm.Set("mno", &_abcStruct{}) + + test(` + abc.xyz = "pqr"; + abc.ghi = {}; + abc.jkl = 3.14159; + abc.mno = mno; + mno.Abc = true; + mno.Ghi = "Something happens."; + [ abc.Xyzzy, abc.def, abc.ghi, abc.mno ]; + `, "Nothing happens.,1,[object Object],[object Object]") + + is(abc["xyz"], "pqr") + is(abc["ghi"], "[object Object]") + is(abc["jkl"], float64(3.14159)) + mno, valid := abc["mno"].(*_abcStruct) + is(valid, true) + is(mno.Abc, true) + is(mno.Ghi, "Something happens.") + } + }) +} + +func TestPassthrough(t *testing.T) { + tt(t, func() { + test, vm := test() + + { + abc := &_abcStruct{ + Mno: _mnoStruct{ + Ghi: "", + }, + } + vm.Set("abc", abc) + + test(` + abc.Mno.Ghi; + `, "") + + vm.Set("pqr", map[string]int8{ + "xyzzy": 0, + "Nothing happens.": 1, + }) + + test(` + abc.Ghi = "abc"; + abc.Pqr = pqr; + abc.Pqr["Nothing happens."]; + `, 1) + + mno := _mnoStruct{ + Ghi: "", + } + vm.Set("mno", mno) + + test(` + abc.Mno = mno; + abc.Mno.Ghi; + `, "") + } + }) +} diff --git a/vendor/github.com/robertkrimen/otto/regexp_test.go b/vendor/github.com/robertkrimen/otto/regexp_test.go new file mode 100644 index 00000000..8e65ee46 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/regexp_test.go @@ -0,0 +1,290 @@ +package otto + +import ( + "fmt" + "testing" +) + +func TestRegExp(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ + /abc/.toString(), + /abc/gim.toString(), + ""+/abc/gi.toString(), + new RegExp("1(\\d+)").toString(), + ]; + `, "/abc/,/abc/gim,/abc/gi,/1(\\d+)/") + + test(` + [ + new RegExp("abc").exec("123abc456"), + null === new RegExp("xyzzy").exec("123abc456"), + new RegExp("1(\\d+)").exec("123abc456"), + new RegExp("xyzzy").test("123abc456"), + new RegExp("1(\\d+)").test("123abc456"), + new RegExp("abc").exec("123abc456"), + ]; + `, "abc,true,123,23,false,true,abc") + + test(`new RegExp("abc").toString()`, "/abc/") + test(`new RegExp("abc", "g").toString()`, "/abc/g") + test(`new RegExp("abc", "mig").toString()`, "/abc/gim") + + result := test(`/(a)?/.exec('b')`, ",") + is(result._object().get("0"), "") + is(result._object().get("1"), "undefined") + is(result._object().get("length"), 2) + + result = test(`/(a)?(b)?/.exec('b')`, "b,,b") + is(result._object().get("0"), "b") + is(result._object().get("1"), "undefined") + is(result._object().get("2"), "b") + is(result._object().get("length"), 3) + + test(`/\u0041/.source`, "\\u0041") + test(`/\a/.source`, "\\a") + test(`/\;/.source`, "\\;") + + test(`/a\a/.source`, "a\\a") + test(`/,\;/.source`, ",\\;") + test(`/ \ /.source`, " \\ ") + + // Start sanity check... + test("eval(\"/abc/\").source", "abc") + test("eval(\"/\u0023/\").source", "#") + test("eval(\"/\u0058/\").source", "X") + test("eval(\"/\\\u0023/\").source == \"\\\u0023\"", true) + test("'0x' + '0058'", "0x0058") + test("'\\\\' + '0x' + '0058'", "\\0x0058") + // ...stop sanity check + + test(`abc = '\\' + String.fromCharCode('0x' + '0058'); eval('/' + abc + '/').source`, "\\X") + test(`abc = '\\' + String.fromCharCode('0x0058'); eval('/' + abc + '/').source == "\\\u0058"`, true) + test(`abc = '\\' + String.fromCharCode('0x0023'); eval('/' + abc + '/').source == "\\\u0023"`, true) + test(`abc = '\\' + String.fromCharCode('0x0078'); eval('/' + abc + '/').source == "\\\u0078"`, true) + + test(` + var abc = Object.getOwnPropertyDescriptor(RegExp, "prototype"); + [ [ typeof RegExp.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestRegExp_global(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /(?:ab|cd)\d?/g; + var found = []; + do { + match = abc.exec("ab cd2 ab34 cd"); + if (match !== null) { + found.push(match[0]); + } else { + break; + } + } while (true); + found; + `, "ab,cd2,ab3,cd") + }) +} + +func TestRegExp_exec(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = /./g; + def = '123456'; + ghi = 0; + while (ghi < 100 && abc.exec(def) !== null) { + ghi += 1; + } + [ ghi, def.length, ghi == def.length ]; + `, "6,6,true") + + test(` + abc = /[abc](\d)?/g; + def = 'a0 b c1 d3'; + ghi = 0; + lastIndex = 0; + while (ghi < 100 && abc.exec(def) !== null) { + lastIndex = abc.lastIndex; + ghi += 1; + + } + [ ghi, lastIndex ]; + `, "3,7") + + test(` + var abc = /[abc](\d)?/.exec("a0 b c1 d3"); + [ abc.length, abc.input, abc.index, abc ]; + `, "2,a0 b c1 d3,0,a0,0") + + test(`raise: + var exec = RegExp.prototype.exec; + exec("Xyzzy"); + `, "TypeError: Calling RegExp.exec on a non-RegExp object") + + test(` + var abc = /\w{3}\d?/.exec("CE\uFFFFL\uFFDDbox127"); + [ abc.input.length, abc.length, abc.input, abc.index, abc ]; + `, "11,1,CE\uFFFFL\uFFDDbox127,5,box1") + + test(`RegExp.prototype.exec.length`, 1) + test(`RegExp.prototype.exec.prototype`, "undefined") + }) +} + +func TestRegExp_test(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`RegExp.prototype.test.length`, 1) + test(`RegExp.prototype.test.prototype`, "undefined") + }) +} + +func TestRegExp_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`RegExp.prototype.toString.length`, 0) + test(`RegExp.prototype.toString.prototype`, "undefined") + }) +} + +func TestRegExp_zaacbbbcac(t *testing.T) { + if true { + return + } + + tt(t, func() { + test, _ := test() + + // FIXME? TODO /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac") + test(` + var abc = /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac"); + [ abc.length, abc.index, abc ]; + `, "6,0,zaacbbbcac,z,ac,a,,c") + }) +} + +func TestRegExpCopying(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = /xyzzy/i; + def = RegExp(abc); + abc.indicator = 1; + [ abc.indicator, def.indicator ]; + `, "1,1") + + test(`raise: + RegExp(new RegExp("\\d"), "1"); + `, "TypeError: Cannot supply flags when constructing one RegExp from another") + }) +} + +func TestRegExp_multiline(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /s$/m.exec("pairs\nmakes\tdouble"); + [ abc.length, abc.index, abc ]; + `, "1,4,s") + }) +} + +func TestRegExp_source(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [ /xyzzy/i.source, /./i.source ]; + `, "xyzzy,.") + + test(` + var abc = /./i; + var def = new RegExp(abc); + [ abc.source, def.source, abc.source === def.source ]; + `, ".,.,true") + + test(` + var abc = /./i; + var def = abc.hasOwnProperty("source"); + var ghi = abc.source; + abc.source = "xyzzy"; + [ def, abc.source ]; + `, "true,.") + }) +} + +func TestRegExp_newRegExp(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + Math.toString(); + var abc = new RegExp(Math,eval("\"g\"")); + [ abc, abc.global ]; + `, "/[object Math]/g,true") + }) +} + +func TestRegExp_flags(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /./i; + var def = new RegExp(abc); + [ abc.multiline == def.multiline, abc.global == def.global, abc.ignoreCase == def.ignoreCase ]; + `, "true,true,true") + }) +} + +func TestRegExp_controlCharacter(t *testing.T) { + tt(t, func() { + test, _ := test() + + for code := 0x41; code < 0x5a; code++ { + string_ := string(code - 64) + test(fmt.Sprintf(` + var code = 0x%x; + var string = String.fromCharCode(code %% 32); + var result = (new RegExp("\\c" + String.fromCharCode(code))).exec(string); + [ code, string, result ]; + `, code), fmt.Sprintf("%d,%s,%s", code, string_, string_)) + } + }) +} + +func TestRegExp_notNotEmptyCharacterClass(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /[\s\S]a/m.exec("a\naba"); + [ abc.length, abc.input, abc ]; + `, "1,a\naba,\na") + }) +} + +func TestRegExp_compile(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = /[\s\S]a/; + abc.compile('^\w+'); + `, "undefined") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/registry/README.markdown b/vendor/github.com/robertkrimen/otto/registry/README.markdown new file mode 100644 index 00000000..ba2d3890 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/registry/README.markdown @@ -0,0 +1,51 @@ +# registry +-- + import "github.com/robertkrimen/otto/registry" + +Package registry is an expirmental package to facillitate altering the otto +runtime via import. + +This interface can change at any time. + +## Usage + +#### func Apply + +```go +func Apply(callback func(Entry)) +``` + +#### type Entry + +```go +type Entry struct { +} +``` + + +#### func Register + +```go +func Register(source func() string) *Entry +``` + +#### func (*Entry) Disable + +```go +func (self *Entry) Disable() +``` + +#### func (*Entry) Enable + +```go +func (self *Entry) Enable() +``` + +#### func (Entry) Source + +```go +func (self Entry) Source() string +``` + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/robertkrimen/otto/registry/registry.go b/vendor/github.com/robertkrimen/otto/registry/registry.go new file mode 100644 index 00000000..966638ac --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/registry/registry.go @@ -0,0 +1,47 @@ +/* +Package registry is an expirmental package to facillitate altering the otto runtime via import. + +This interface can change at any time. +*/ +package registry + +var registry []*Entry = make([]*Entry, 0) + +type Entry struct { + active bool + source func() string +} + +func newEntry(source func() string) *Entry { + return &Entry{ + active: true, + source: source, + } +} + +func (self *Entry) Enable() { + self.active = true +} + +func (self *Entry) Disable() { + self.active = false +} + +func (self Entry) Source() string { + return self.source() +} + +func Apply(callback func(Entry)) { + for _, entry := range registry { + if !entry.active { + continue + } + callback(*entry) + } +} + +func Register(source func() string) *Entry { + entry := newEntry(source) + registry = append(registry, entry) + return entry +} diff --git a/vendor/github.com/robertkrimen/otto/repl/autocompleter.go b/vendor/github.com/robertkrimen/otto/repl/autocompleter.go new file mode 100644 index 00000000..3256678d --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/repl/autocompleter.go @@ -0,0 +1,57 @@ +package repl + +import ( + "regexp" + "strings" + + "github.com/robertkrimen/otto" +) + +type autoCompleter struct { + vm *otto.Otto +} + +var lastExpressionRegex = regexp.MustCompile(`[a-zA-Z0-9]([a-zA-Z0-9\.]*[a-zA-Z0-9])?\.?$`) + +func (a *autoCompleter) Do(line []rune, pos int) ([][]rune, int) { + lastExpression := lastExpressionRegex.FindString(string(line)) + + bits := strings.Split(lastExpression, ".") + + first := bits[:len(bits)-1] + last := bits[len(bits)-1] + + var l []string + + if len(first) == 0 { + c := a.vm.Context() + + l = make([]string, len(c.Symbols)) + + i := 0 + for k := range c.Symbols { + l[i] = k + i++ + } + } else { + r, err := a.vm.Eval(strings.Join(bits[:len(bits)-1], ".")) + if err != nil { + return nil, 0 + } + + if o := r.Object(); o != nil { + for _, v := range o.KeysByParent() { + l = append(l, v...) + } + } + } + + var r [][]rune + for _, s := range l { + if strings.HasPrefix(s, last) { + r = append(r, []rune(strings.TrimPrefix(s, last))) + } + } + + return r, len(last) +} diff --git a/vendor/github.com/robertkrimen/otto/repl/repl.go b/vendor/github.com/robertkrimen/otto/repl/repl.go new file mode 100644 index 00000000..268d913f --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/repl/repl.go @@ -0,0 +1,149 @@ +// Package repl implements a REPL (read-eval-print loop) for otto. +package repl + +import ( + "fmt" + "io" + "strings" + "sync/atomic" + + "github.com/robertkrimen/otto" + "gopkg.in/readline.v1" +) + +var counter uint32 + +// DebuggerHandler implements otto's debugger handler signature, providing a +// simple drop-in debugger implementation. +func DebuggerHandler(vm *otto.Otto) { + i := atomic.AddUint32(&counter, 1) + + // purposefully ignoring the error here - we can't do anything useful with + // it except panicking, and that'd be pretty rude. it'd be easy enough for a + // consumer to define an equivalent function that _does_ panic if desired. + _ = RunWithPrompt(vm, fmt.Sprintf("DEBUGGER[%d]> ", i)) +} + +// Run creates a REPL with the default prompt and no prelude. +func Run(vm *otto.Otto) error { + return RunWithOptions(vm, Options{}) +} + +// RunWithPrompt runs a REPL with the given prompt and no prelude. +func RunWithPrompt(vm *otto.Otto, prompt string) error { + return RunWithOptions(vm, Options{ + Prompt: prompt, + }) +} + +// RunWithPrelude runs a REPL with the default prompt and the given prelude. +func RunWithPrelude(vm *otto.Otto, prelude string) error { + return RunWithOptions(vm, Options{ + Prelude: prelude, + }) +} + +// RunWithPromptAndPrelude runs a REPL with the given prompt and prelude. +func RunWithPromptAndPrelude(vm *otto.Otto, prompt, prelude string) error { + return RunWithOptions(vm, Options{ + Prompt: prompt, + Prelude: prelude, + }) +} + +// Options contains parameters for configuring a REPL session. +type Options struct { + // Prompt controls what's shown at the beginning of the line. If not + // specified, this defaults to "> " + Prompt string + // Prelude is some text that's printed before control is given to the user. + // By default this is empty. If specified, this will be printed with a + // newline following it. + Prelude string + // Autocomplete controls whether this REPL session has autocompletion + // enabled. The way autocomplete is implemented can incur a performance + // penalty, so it's turned off by default. + Autocomplete bool +} + +// RunWithOptions runs a REPL with the given options. +func RunWithOptions(vm *otto.Otto, options Options) error { + prompt := options.Prompt + if prompt == "" { + prompt = "> " + } + + c := &readline.Config{ + Prompt: prompt, + } + + if options.Autocomplete { + c.AutoComplete = &autoCompleter{vm} + } + + rl, err := readline.NewEx(c) + if err != nil { + return err + } + + prelude := options.Prelude + if prelude != "" { + if _, err := io.Copy(rl.Stderr(), strings.NewReader(prelude+"\n")); err != nil { + return err + } + + rl.Refresh() + } + + var d []string + + for { + l, err := rl.Readline() + if err != nil { + if err == readline.ErrInterrupt { + if d != nil { + d = nil + + rl.SetPrompt(prompt) + rl.Refresh() + + continue + } + + break + } + + return err + } + + if l == "" { + continue + } + + d = append(d, l) + + s, err := vm.Compile("repl", strings.Join(d, "\n")) + if err != nil { + rl.SetPrompt(strings.Repeat(" ", len(prompt))) + } else { + rl.SetPrompt(prompt) + + d = nil + + v, err := vm.Eval(s) + if err != nil { + if oerr, ok := err.(*otto.Error); ok { + io.Copy(rl.Stdout(), strings.NewReader(oerr.String())) + } else { + io.Copy(rl.Stdout(), strings.NewReader(err.Error())) + } + } else { + rl.Stdout().Write([]byte(v.String() + "\n")) + } + } + + rl.Refresh() + } + + return rl.Close() +} diff --git a/vendor/github.com/robertkrimen/otto/result.go b/vendor/github.com/robertkrimen/otto/result.go new file mode 100644 index 00000000..63642e7d --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/result.go @@ -0,0 +1,30 @@ +package otto + +import () + +type _resultKind int + +const ( + resultNormal _resultKind = iota + resultReturn + resultBreak + resultContinue +) + +type _result struct { + kind _resultKind + value Value + target string +} + +func newReturnResult(value Value) _result { + return _result{resultReturn, value, ""} +} + +func newContinueResult(target string) _result { + return _result{resultContinue, emptyValue, target} +} + +func newBreakResult(target string) _result { + return _result{resultBreak, emptyValue, target} +} diff --git a/vendor/github.com/robertkrimen/otto/runtime.go b/vendor/github.com/robertkrimen/otto/runtime.go new file mode 100644 index 00000000..7d29ecca --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/runtime.go @@ -0,0 +1,715 @@ +package otto + +import ( + "errors" + "fmt" + "math" + "path" + "reflect" + "runtime" + "strconv" + "sync" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/parser" +) + +type _global struct { + Object *_object // Object( ... ), new Object( ... ) - 1 (length) + Function *_object // Function( ... ), new Function( ... ) - 1 + Array *_object // Array( ... ), new Array( ... ) - 1 + String *_object // String( ... ), new String( ... ) - 1 + Boolean *_object // Boolean( ... ), new Boolean( ... ) - 1 + Number *_object // Number( ... ), new Number( ... ) - 1 + Math *_object + Date *_object // Date( ... ), new Date( ... ) - 7 + RegExp *_object // RegExp( ... ), new RegExp( ... ) - 2 + Error *_object // Error( ... ), new Error( ... ) - 1 + EvalError *_object + TypeError *_object + RangeError *_object + ReferenceError *_object + SyntaxError *_object + URIError *_object + JSON *_object + + ObjectPrototype *_object // Object.prototype + FunctionPrototype *_object // Function.prototype + ArrayPrototype *_object // Array.prototype + StringPrototype *_object // String.prototype + BooleanPrototype *_object // Boolean.prototype + NumberPrototype *_object // Number.prototype + DatePrototype *_object // Date.prototype + RegExpPrototype *_object // RegExp.prototype + ErrorPrototype *_object // Error.prototype + EvalErrorPrototype *_object + TypeErrorPrototype *_object + RangeErrorPrototype *_object + ReferenceErrorPrototype *_object + SyntaxErrorPrototype *_object + URIErrorPrototype *_object +} + +type _runtime struct { + global _global + globalObject *_object + globalStash *_objectStash + scope *_scope + otto *Otto + eval *_object // The builtin eval, for determine indirect versus direct invocation + debugger func(*Otto) + random func() float64 + stackLimit int + traceLimit int + + labels []string // FIXME + lck sync.Mutex +} + +func (self *_runtime) enterScope(scope *_scope) { + scope.outer = self.scope + if self.scope != nil { + if self.stackLimit != 0 && self.scope.depth+1 >= self.stackLimit { + panic(self.panicRangeError("Maximum call stack size exceeded")) + } + + scope.depth = self.scope.depth + 1 + } + + self.scope = scope +} + +func (self *_runtime) leaveScope() { + self.scope = self.scope.outer +} + +// FIXME This is used in two places (cloning) +func (self *_runtime) enterGlobalScope() { + self.enterScope(newScope(self.globalStash, self.globalStash, self.globalObject)) +} + +func (self *_runtime) enterFunctionScope(outer _stash, this Value) *_fnStash { + if outer == nil { + outer = self.globalStash + } + stash := self.newFunctionStash(outer) + var thisObject *_object + switch this.kind { + case valueUndefined, valueNull: + thisObject = self.globalObject + default: + thisObject = self.toObject(this) + } + self.enterScope(newScope(stash, stash, thisObject)) + return stash +} + +func (self *_runtime) putValue(reference _reference, value Value) { + name := reference.putValue(value) + if name != "" { + // Why? -- If reference.base == nil + // strict = false + self.globalObject.defineProperty(name, value, 0111, false) + } +} + +func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exception bool) { + // resultValue = The value of the block (e.g. the last statement) + // throw = Something was thrown + // throwValue = The value of what was thrown + // other = Something that changes flow (return, break, continue) that is not a throw + // Otherwise, some sort of unknown panic happened, we'll just propagate it + defer func() { + if caught := recover(); caught != nil { + if exception, ok := caught.(*_exception); ok { + caught = exception.eject() + } + switch caught := caught.(type) { + case _error: + exception = true + tryValue = toValue_object(self.newError(caught.name, caught.messageValue(), 0)) + case Value: + exception = true + tryValue = caught + default: + panic(caught) + } + } + }() + + tryValue = inner() + return +} + +// toObject + +func (self *_runtime) toObject(value Value) *_object { + switch value.kind { + case valueEmpty, valueUndefined, valueNull: + panic(self.panicTypeError()) + case valueBoolean: + return self.newBoolean(value) + case valueString: + return self.newString(value) + case valueNumber: + return self.newNumber(value) + case valueObject: + return value._object() + } + panic(self.panicTypeError()) +} + +func (self *_runtime) objectCoerce(value Value) (*_object, error) { + switch value.kind { + case valueUndefined: + return nil, errors.New("undefined") + case valueNull: + return nil, errors.New("null") + case valueBoolean: + return self.newBoolean(value), nil + case valueString: + return self.newString(value), nil + case valueNumber: + return self.newNumber(value), nil + case valueObject: + return value._object(), nil + } + panic(self.panicTypeError()) +} + +func checkObjectCoercible(rt *_runtime, value Value) { + isObject, mustCoerce := testObjectCoercible(value) + if !isObject && !mustCoerce { + panic(rt.panicTypeError()) + } +} + +// testObjectCoercible + +func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) { + switch value.kind { + case valueReference, valueEmpty, valueNull, valueUndefined: + return false, false + case valueNumber, valueString, valueBoolean: + return false, true + case valueObject: + return true, false + default: + panic("this should never happen") + } +} + +func (self *_runtime) safeToValue(value interface{}) (Value, error) { + result := Value{} + err := catchPanic(func() { + result = self.toValue(value) + }) + return result, err +} + +// convertNumeric converts numeric parameter val from js to that of type t if it is safe to do so, otherwise it panics. +// This allows literals (int64), bitwise values (int32) and the general form (float64) of javascript numerics to be passed as parameters to go functions easily. +func (self *_runtime) convertNumeric(v Value, t reflect.Type) reflect.Value { + val := reflect.ValueOf(v.export()) + + if val.Kind() == t.Kind() { + return val + } + + if val.Kind() == reflect.Interface { + val = reflect.ValueOf(val.Interface()) + } + + switch val.Kind() { + case reflect.Float32, reflect.Float64: + f64 := val.Float() + switch t.Kind() { + case reflect.Float64: + return reflect.ValueOf(f64) + case reflect.Float32: + if reflect.Zero(t).OverflowFloat(f64) { + panic(self.panicRangeError("converting float64 to float32 would overflow")) + } + + return val.Convert(t) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + i64 := int64(f64) + if float64(i64) != f64 { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would cause loss of precision", val.Type(), t))) + } + + // The float represents an integer + val = reflect.ValueOf(i64) + default: + panic(self.panicTypeError(fmt.Sprintf("cannot convert %v to %v", val.Type(), t))) + } + } + + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i64 := val.Int() + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if reflect.Zero(t).OverflowInt(i64) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if i64 < 0 { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would underflow", val.Type(), t))) + } + if reflect.Zero(t).OverflowUint(uint64(i64)) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + u64 := val.Uint() + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if u64 > math.MaxInt64 || reflect.Zero(t).OverflowInt(int64(u64)) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if reflect.Zero(t).OverflowUint(u64) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + } + } + + panic(self.panicTypeError(fmt.Sprintf("unsupported type %v for numeric conversion", val.Type()))) +} + +var typeOfValue = reflect.TypeOf(Value{}) + +// convertCallParameter converts request val to type t if possible. +// If the conversion fails due to overflow or type miss-match then it panics. +// If no conversion is known then the original value is returned. +func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Value { + if t == typeOfValue { + return reflect.ValueOf(v) + } + + if v.kind == valueObject { + if gso, ok := v._object().value.(*_goStructObject); ok { + if gso.value.Type().AssignableTo(t) { + return gso.value + } + } + } + + if t.Kind() == reflect.Interface { + e := v.export() + if e == nil { + return reflect.Zero(t) + } + iv := reflect.ValueOf(e) + if iv.Type().AssignableTo(t) { + return iv + } + } + + tk := t.Kind() + + if tk == reflect.Ptr { + switch v.kind { + case valueEmpty, valueNull, valueUndefined: + return reflect.Zero(t) + default: + var vv reflect.Value + if err := catchPanic(func() { vv = self.convertCallParameter(v, t.Elem()) }); err == nil { + if vv.CanAddr() { + return vv.Addr() + } + + pv := reflect.New(vv.Type()) + pv.Elem().Set(vv) + return pv + } + } + } + + switch tk { + case reflect.Bool: + return reflect.ValueOf(v.bool()) + case reflect.String: + switch v.kind { + case valueString: + return reflect.ValueOf(v.value) + case valueNumber: + return reflect.ValueOf(fmt.Sprintf("%v", v.value)) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: + switch v.kind { + case valueNumber: + return self.convertNumeric(v, t) + } + case reflect.Slice: + if o := v._object(); o != nil { + if lv := o.get("length"); lv.IsNumber() { + l := lv.number().int64 + + s := reflect.MakeSlice(t, int(l), int(l)) + + tt := t.Elem() + + if o.class == "Array" { + for i := int64(0); i < l; i++ { + p, ok := o.property[strconv.FormatInt(i, 10)] + if !ok { + continue + } + + e, ok := p.value.(Value) + if !ok { + continue + } + + ev := self.convertCallParameter(e, tt) + + s.Index(int(i)).Set(ev) + } + } else if o.class == "GoArray" { + + var gslice bool + switch o.value.(type) { + case *_goSliceObject: + gslice = true + case *_goArrayObject: + gslice = false + } + + for i := int64(0); i < l; i++ { + var p *_property + if gslice { + p = goSliceGetOwnProperty(o, strconv.FormatInt(i, 10)) + } else { + p = goArrayGetOwnProperty(o, strconv.FormatInt(i, 10)) + } + if p == nil { + continue + } + + e, ok := p.value.(Value) + if !ok { + continue + } + + ev := self.convertCallParameter(e, tt) + + s.Index(int(i)).Set(ev) + } + } + + return s + } + } + case reflect.Map: + if o := v._object(); o != nil && t.Key().Kind() == reflect.String { + m := reflect.MakeMap(t) + + o.enumerate(false, func(k string) bool { + m.SetMapIndex(reflect.ValueOf(k), self.convertCallParameter(o.get(k), t.Elem())) + return true + }) + + return m + } + case reflect.Func: + if t.NumOut() > 1 { + panic(self.panicTypeError("converting JavaScript values to Go functions with more than one return value is currently not supported")) + } + + if o := v._object(); o != nil && o.class == "Function" { + return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value { + l := make([]interface{}, len(args)) + for i, a := range args { + if a.CanInterface() { + l[i] = a.Interface() + } + } + + rv, err := v.Call(nullValue, l...) + if err != nil { + panic(err) + } + + if t.NumOut() == 0 { + return nil + } + + return []reflect.Value{self.convertCallParameter(rv, t.Out(0))} + }) + } + } + + if tk == reflect.String { + if o := v._object(); o != nil && o.hasProperty("toString") { + if fn := o.get("toString"); fn.IsFunction() { + sv, err := fn.Call(v) + if err != nil { + panic(err) + } + + var r reflect.Value + if err := catchPanic(func() { r = self.convertCallParameter(sv, t) }); err == nil { + return r + } + } + } + + return reflect.ValueOf(v.String()) + } + + s := "OTTO DOES NOT UNDERSTAND THIS TYPE" + switch v.kind { + case valueBoolean: + s = "boolean" + case valueNull: + s = "null" + case valueNumber: + s = "number" + case valueString: + s = "string" + case valueUndefined: + s = "undefined" + case valueObject: + s = v.Class() + } + + panic(self.panicTypeError("can't convert from %q to %q", s, t.String())) +} + +func (self *_runtime) toValue(value interface{}) Value { + switch value := value.(type) { + case Value: + return value + case func(FunctionCall) Value: + var name, file string + var line int + pc := reflect.ValueOf(value).Pointer() + fn := runtime.FuncForPC(pc) + if fn != nil { + name = fn.Name() + file, line = fn.FileLine(pc) + file = path.Base(file) + } + return toValue_object(self.newNativeFunction(name, file, line, value)) + case _nativeFunction: + var name, file string + var line int + pc := reflect.ValueOf(value).Pointer() + fn := runtime.FuncForPC(pc) + if fn != nil { + name = fn.Name() + file, line = fn.FileLine(pc) + file = path.Base(file) + } + return toValue_object(self.newNativeFunction(name, file, line, value)) + case Object, *Object, _object, *_object: + // Nothing happens. + // FIXME We should really figure out what can come here. + // This catch-all is ugly. + default: + { + value := reflect.ValueOf(value) + + switch value.Kind() { + case reflect.Ptr: + switch reflect.Indirect(value).Kind() { + case reflect.Struct: + return toValue_object(self.newGoStructObject(value)) + case reflect.Array: + return toValue_object(self.newGoArray(value)) + } + case reflect.Struct: + return toValue_object(self.newGoStructObject(value)) + case reflect.Map: + return toValue_object(self.newGoMapObject(value)) + case reflect.Slice: + return toValue_object(self.newGoSlice(value)) + case reflect.Array: + return toValue_object(self.newGoArray(value)) + case reflect.Func: + var name, file string + var line int + if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr { + pc := v.Pointer() + fn := runtime.FuncForPC(pc) + if fn != nil { + name = fn.Name() + file, line = fn.FileLine(pc) + file = path.Base(file) + } + } + + typ := value.Type() + + return toValue_object(self.newNativeFunction(name, file, line, func(c FunctionCall) Value { + nargs := typ.NumIn() + + if len(c.ArgumentList) != nargs { + if typ.IsVariadic() { + if len(c.ArgumentList) < nargs-1 { + panic(self.panicRangeError(fmt.Sprintf("expected at least %d arguments; got %d", nargs-1, len(c.ArgumentList)))) + } + } else { + panic(self.panicRangeError(fmt.Sprintf("expected %d argument(s); got %d", nargs, len(c.ArgumentList)))) + } + } + + in := make([]reflect.Value, len(c.ArgumentList)) + + callSlice := false + + for i, a := range c.ArgumentList { + var t reflect.Type + + n := i + if n >= nargs-1 && typ.IsVariadic() { + if n > nargs-1 { + n = nargs - 1 + } + + t = typ.In(n).Elem() + } else { + t = typ.In(n) + } + + // if this is a variadic Go function, and the caller has supplied + // exactly the number of JavaScript arguments required, and this + // is the last JavaScript argument, try treating the it as the + // actual set of variadic Go arguments. if that succeeds, break + // out of the loop. + if typ.IsVariadic() && len(c.ArgumentList) == nargs && i == nargs-1 { + var v reflect.Value + if err := catchPanic(func() { v = self.convertCallParameter(a, typ.In(n)) }); err == nil { + in[i] = v + callSlice = true + break + } + } + + in[i] = self.convertCallParameter(a, t) + } + + var out []reflect.Value + if callSlice { + out = value.CallSlice(in) + } else { + out = value.Call(in) + } + + switch len(out) { + case 0: + return Value{} + case 1: + return self.toValue(out[0].Interface()) + default: + s := make([]interface{}, len(out)) + for i, v := range out { + s[i] = self.toValue(v.Interface()) + } + + return self.toValue(s) + } + })) + } + } + } + + return toValue(value) +} + +func (runtime *_runtime) newGoSlice(value reflect.Value) *_object { + self := runtime.newGoSliceObject(value) + self.prototype = runtime.global.ArrayPrototype + return self +} + +func (runtime *_runtime) newGoArray(value reflect.Value) *_object { + self := runtime.newGoArrayObject(value) + self.prototype = runtime.global.ArrayPrototype + return self +} + +func (runtime *_runtime) parse(filename string, src, sm interface{}) (*ast.Program, error) { + return parser.ParseFileWithSourceMap(nil, filename, src, sm, 0) +} + +func (runtime *_runtime) cmpl_parse(filename string, src, sm interface{}) (*_nodeProgram, error) { + program, err := parser.ParseFileWithSourceMap(nil, filename, src, sm, 0) + if err != nil { + return nil, err + } + + return cmpl_parse(program), nil +} + +func (self *_runtime) parseSource(src, sm interface{}) (*_nodeProgram, *ast.Program, error) { + switch src := src.(type) { + case *ast.Program: + return nil, src, nil + case *Script: + return src.program, nil, nil + } + + program, err := self.parse("", src, sm) + + return nil, program, err +} + +func (self *_runtime) cmpl_runOrEval(src, sm interface{}, eval bool) (Value, error) { + result := Value{} + cmpl_program, program, err := self.parseSource(src, sm) + if err != nil { + return result, err + } + if cmpl_program == nil { + cmpl_program = cmpl_parse(program) + } + err = catchPanic(func() { + result = self.cmpl_evaluate_nodeProgram(cmpl_program, eval) + }) + switch result.kind { + case valueEmpty: + result = Value{} + case valueReference: + result = result.resolve() + } + return result, err +} + +func (self *_runtime) cmpl_run(src, sm interface{}) (Value, error) { + return self.cmpl_runOrEval(src, sm, false) +} + +func (self *_runtime) cmpl_eval(src, sm interface{}) (Value, error) { + return self.cmpl_runOrEval(src, sm, true) +} + +func (self *_runtime) parseThrow(err error) { + if err == nil { + return + } + switch err := err.(type) { + case parser.ErrorList: + { + err := err[0] + if err.Message == "Invalid left-hand side in assignment" { + panic(self.panicReferenceError(err.Message)) + } + panic(self.panicSyntaxError(err.Message)) + } + } + panic(self.panicSyntaxError(err.Error())) +} + +func (self *_runtime) cmpl_parseOrThrow(src, sm interface{}) *_nodeProgram { + program, err := self.cmpl_parse("", src, sm) + self.parseThrow(err) // Will panic/throw appropriately + return program +} diff --git a/vendor/github.com/robertkrimen/otto/runtime_test.go b/vendor/github.com/robertkrimen/otto/runtime_test.go new file mode 100644 index 00000000..2310e963 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/runtime_test.go @@ -0,0 +1,859 @@ +package otto + +import ( + "math" + "testing" +) + +// FIXME terst, Review tests + +func TestOperator(t *testing.T) { + tt(t, func() { + test, vm := test() + + test("xyzzy = 1") + test("xyzzy", 1) + + if true { + vm.Set("twoPlusTwo", func(FunctionCall) Value { + return toValue(5) + }) + test("twoPlusTwo( 1 )", 5) + + test("1 + twoPlusTwo( 1 )", 6) + + test("-1 + twoPlusTwo( 1 )", 4) + } + + test("result = 4") + test("result", 4) + + test("result += 1") + test("result", 5) + + test("result *= 2") + test("result", 10) + + test("result /= 2") + test("result", 5) + + test("result = 112.51 % 3.1") + test("result", 0.9100000000000019) + + test("result = 'Xyzzy'") + test("result", "Xyzzy") + + test("result = 'Xyz' + 'zy'") + test("result", "Xyzzy") + + test("result = \"Xyzzy\"") + test("result", "Xyzzy") + + test("result = 1; result = result") + test("result", 1) + + test(` + var result64 + = + 64 + , result10 = + 10 + `) + test("result64", 64) + test("result10", 10) + + test(` + result = 1; + result += 1; + `) + test("result", 2) + }) +} + +func TestFunction_(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + result = 2 + xyzzy = function() { + result += 1 + } + xyzzy() + result; + `, 3) + + test(` + xyzzy = function() { + return 1 + } + result = xyzzy() + `, 1) + + test(` + xyzzy = function() {} + result = xyzzy() + `, "undefined") + + test(` + xyzzy = function() { + return 64 + return 1 + } + result = xyzzy() + `, 64) + + test(` + result = 4 + xyzzy = function() { + result = 2 + } + xyzzy(); + result; + `, 2) + + test(` + result = 4 + xyzzy = function() { + var result + result = 2 + } + xyzzy(); + result; + `, 4) + + test(` + xyzzy = function() { + var result = 4 + return result + } + result = xyzzy() + `, 4) + + test(` + xyzzy = function() { + function test() { + var result = 1 + return result + } + return test() + 1 + } + result = xyzzy() + 1 + `, 3) + + test(` + xyzzy = function() { + function test() { + var result = 1 + return result + } + _xyzzy = 2 + var result = _xyzzy + test() + 1 + return result + } + result = xyzzy() + 1; + [ result, _xyzzy ]; + `, "5,2") + + test(` + xyzzy = function(apple) { + return 1 + } + result = xyzzy(1) + `, 1) + + test(` + xyzzy = function(apple) { + return apple + 1 + } + result = xyzzy(2) + `, 3) + + test(` + { + result = 1 + result += 1; + } + `, 2) + + test(` + var global = 1 + outer = function() { + var global = 2 + var inner = function(){ + return global + } + return inner() + } + result = outer() + `, 2) + + test(` + var apple = 1 + var banana = function() { + return apple + } + var cherry = function() { + var apple = 2 + return banana() + } + result = cherry() + `, 1) + + test(` + function xyz() { + }; + delete xyz; + `, false) + + test(` + var abc = function __factorial(def){ + if (def === 1) { + return def; + } else { + return __factorial(def-1)*def; + } + }; + abc(3); + `, 6) + }) +} + +func TestDoWhile(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + limit = 4; + result = 0; + do { + result = result + 1; + limit = limit - 1; + } while (limit); + result; + `, 4) + + test(` + result = eval("do {abc=1; break; abc=2;} while (0);"); + [ result, abc ]; + `, "1,1") + }) +} + +func TestContinueBreak(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + limit = 4 + result = 0 + while (limit) { + limit = limit - 1 + if (limit) { + } + else { + break + } + result = result + 1 + } + [ result, limit ]; + `, "3,0") + + test(` + limit = 4 + result = 0 + while (limit) { + limit = limit - 1 + if (limit) { + continue + } + else { + break + } + result = result + 1 + } + result; + `, 0) + + test(` + limit = 4 + result = 0 + do { + limit = limit - 1 + if (limit) { + continue + } + else { + break + } + result = result + 1 + } while (limit) + result; + `, 0) + }) +} + +func TestTryCatchError(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc + try { + 1() + } + catch (def) { + abc = def + } + abc; + `, "TypeError: 1 is not a function") + + }) +} + +func TestPositiveNegativeZero(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`1/0`, _Infinity) + test(`1/-0`, -_Infinity) + test(` + abc = -0 + 1/abc + `, -_Infinity) + }) +} + +func TestComparison(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + undefined = 1; undefined; + `, "undefined") + + test("undefined == undefined", true) + + test("undefined != undefined", false) + + test("null == null", true) + + test("null != null", false) + + test("0 == 1", false) + + is(negativeZero(), -0) + is(positiveZero(), 0) + is(math.Signbit(negativeZero()), true) + is(positiveZero() == negativeZero(), true) + + test("1 == 1", true) + + test("'Hello, World.' == 'Goodbye, World.'", false) + + test("'Hello, World.' == true", false) + + test("'Hello, World.' == false", false) + + test("'Hello, World.' == 1", false) + + test("1 == 'Hello, World.'", false) + + is(parseNumber("-1"), -1) + + test("0+Object", "0function Object() { [native code] }") + }) +} + +func TestComparisonRelational(t *testing.T) { + tt(t, func() { + test, _ := test() + + test("0 < 0", false) + + test("0 > 0", false) + + test("0 <= 0", true) + + test("0 >= 0", true) + + test("' 0' >= 0", true) + + test("'_ 0' >= 0", false) + }) +} + +func TestArguments(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + xyzzy = function() { + return arguments[0] + } + result = xyzzy("xyzzy"); + `, "xyzzy") + + test(` + xyzzy = function() { + arguments[0] = "abcdef" + return arguments[0] + } + result = xyzzy("xyzzy"); + `, "abcdef") + + test(` + xyzzy = function(apple) { + apple = "abcdef" + return arguments[0] + } + result = xyzzy("xyzzy"); + `, "abcdef") + + test(` + (function(){ + return arguments + })() + `, "[object Arguments]") + + test(` + (function(){ + return arguments.length + })() + `, 0) + + test(` + (function(){ + return arguments.length + })(1, 2, 4, 8, 10) + `, 5) + }) +} + +func TestObjectLiteral(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + ({}); + `, "[object Object]") + + test(` + var abc = { + xyzzy: "Nothing happens.", + get 1e2() { + return 3.14159; + }, + get null() { + return true; + }, + get "[\n]"() { + return "<>"; + } + }; + [ abc["1e2"], abc.null, abc["[\n]"] ]; + `, "3.14159,true,<>") + + test(` + var abc = { + xyzzy: "Nothing happens.", + set 1e2() { + this[3.14159] = 100; + return Math.random(); + }, + set null(def) { + this.def = def; + return Math.random(); + }, + }; + [ abc["1e2"] = Infinity, abc[3.14159], abc.null = "xyz", abc.def ]; + `, "Infinity,100,xyz,xyz") + }) +} + +func TestUnaryPrefix(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var result = 0; + [++result, result]; + `, "1,1") + + test(` + result = 0; + [--result, result]; + `, "-1,-1") + + test(` + var object = { valueOf: function() { return 1; } }; + result = ++object; + [ result, typeof result ]; + `, "2,number") + + test(` + var object = { valueOf: function() { return 1; } }; + result = --object; + [ result, typeof result ]; + `, "0,number") + }) +} + +func TestUnaryPostfix(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var result = 0; + result++; + [ result++, result ]; + `, "1,2") + + test(` + result = 0; + result--; + [ result--, result ]; + `, "-1,-2") + + test(` + var object = { valueOf: function() { return 1; } }; + result = object++; + [ result, typeof result ]; + `, "1,number") + + test(` + var object = { valueOf: function() { return 1; } }; + result = object-- + [ result, typeof result ]; + `, "1,number") + }) +} + +func TestBinaryLogicalOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = true + def = false + ghi = false + jkl = false + result = abc && def || ghi && jkl + `, false) + + test(` + abc = true + def = true + ghi = false + jkl = false + result = abc && def || ghi && jkl + `, true) + + }) +} + +func TestBinaryBitwiseOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = 1 & 2; + def = 1 & 3; + ghi = 1 | 3; + jkl = 1 ^ 2; + mno = 1 ^ 3; + [ abc, def, ghi, jkl, mno ]; + `, "0,1,3,3,2") + }) +} + +func TestBinaryShiftOperation(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + high = (1 << 30) - 1 + (1 << 30) + low = -high - 1 + abc = 23 << 1 + def = -105 >> 1 + ghi = 23 << 2 + jkl = 1 >>> 31 + mno = 1 << 64 + pqr = 1 >> 2 + stu = -2 >> 4 + vwx = low >> 1 + yz = low >>> 1 + `) + test("abc", 46) + test("def", -53) + test("ghi", 92) + test("jkl", 0) + test("mno", 1) + test("pqr", 0) + test("stu", -1) + test("vwx", -1073741824) + test("yz", 1073741824) + }) +} + +func TestParenthesizing(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = 1 + 2 * 3 + def = (1 + 2) * 3 + ghi = !(false || true) + jkl = !false || true + `) + test("abc", 7) + test("def", 9) + test("ghi", false) + test("jkl", true) + }) +} + +func Test_instanceof(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = {} instanceof Object; + `, true) + + test(` + abc = "abc" instanceof Object; + `, false) + + test(`raise: + abc = {} instanceof "abc"; + `, "TypeError: Expecting a function in instanceof check, but got: abc") + + test(`raise: + "xyzzy" instanceof Math; + `, "TypeError") + }) +} + +func TestIn(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = "prototype" in Object; + def = "xyzzy" in Object; + [ abc, def ]; + `, "true,false") + }) +} + +func Test_new(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = new Boolean; + def = new Boolean(1); + [ abc, def ]; + `, "false,true") + }) +} + +func TestNewFunction(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + new Function("return 11")() + `, 11) + + test(` + abc = 10 + new Function("abc += 1")() + abc + `, 11) + + test(` + new Function("a", "b", "c", "return b + 2")(10, 11, 12) + `, 13) + + test(`raise: + new 1 + `, "TypeError: 1 is not a function") + + // TODO Better error reporting: new this + test(`raise: + new this + `, "TypeError: [object environment] is not a function") + + test(`raise: + new {} + `, "TypeError: [object Object] is not a function") + }) +} + +func TestNewPrototype(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = { 'xyzzy': 'Nothing happens.' } + function Xyzzy(){} + Xyzzy.prototype = abc; + (new Xyzzy()).xyzzy + `, "Nothing happens.") + }) +} + +func TestBlock(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc=0; + var ghi; + def: { + do { + abc++; + if (!(abc < 10)) { + break def; + ghi = "ghi"; + } + } while (true); + } + [ abc,ghi ]; + `, "10,") + }) +} + +func Test_toString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + [undefined+""] + `, "undefined") + }) +} + +func TestEvaluationOrder(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + var abc = 0; + abc < (abc = 1) === true; + `, true) + }) +} + +func TestClone(t *testing.T) { + tt(t, func() { + vm1 := New() + vm1.Run(` + var abc = 1; + `) + + vm2 := vm1.clone() + vm1.Run(` + abc += 2; + `) + vm2.Run(` + abc += 4; + `) + + is(vm1.getValue("abc"), 3) + is(vm2.getValue("abc"), 5) + }) +} + +func Test_debugger(t *testing.T) { + tt(t, func() { + called := false + + vm := New() + vm.SetDebuggerHandler(func(o *Otto) { + is(o, vm) + called = true + }) + + _, err := vm.Run(`debugger`) + is(err, nil) + is(called, true) + }) + + tt(t, func() { + called := false + + vm := New() + vm.SetDebuggerHandler(func(o *Otto) { + is(o, vm) + called = true + }) + + _, err := vm.Run(`null`) + is(err, nil) + is(called, false) + }) + + tt(t, func() { + vm := New() + + _, err := vm.Run(`debugger`) + is(err, nil) + }) +} + +func Test_random(t *testing.T) { + tt(t, func() { + vm := New() + vm.SetRandomSource(func() float64 { return 1 }) + + r, err := vm.Run(`Math.random()`) + is(err, nil) + f, err := r.ToFloat() + is(err, nil) + is(f, 1) + }) + + tt(t, func() { + vm := New() + + r1, err := vm.Run(`Math.random()`) + is(err, nil) + f1, err := r1.ToFloat() + is(err, nil) + + r2, err := vm.Run(`Math.random()`) + is(err, nil) + f2, err := r2.ToFloat() + is(err, nil) + + is(f1 == f2, false) + }) +} + +func Test_stringArray(t *testing.T) { + getStrings := func() []string { + return []string{"these", "are", "strings"} + } + concatStrings := func(a []string) string { + if len(a) == 0 { + return "" + } + r := a[0] + for i := 1; i < len(a); i++ { + r += " " + r += a[i] + } + return r + } + tt(t, func() { + vm := New() + vm.Set("getStrings", getStrings) + vm.Set("concatStrings", concatStrings) + r1, err := vm.Run(`var a = getStrings(); concatStrings(a)`) + is(err, nil) + is(r1, "these are strings") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/scope.go b/vendor/github.com/robertkrimen/otto/scope.go new file mode 100644 index 00000000..465e6b98 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/scope.go @@ -0,0 +1,35 @@ +package otto + +// _scope: +// entryFile +// entryIdx +// top? +// outer => nil + +// _stash: +// lexical +// variable +// +// _thisStash (ObjectEnvironment) +// _fnStash +// _dclStash + +// An ECMA-262 ExecutionContext +type _scope struct { + lexical _stash + variable _stash + this *_object + eval bool // Replace this with kind? + outer *_scope + depth int + + frame _frame +} + +func newScope(lexical _stash, variable _stash, this *_object) *_scope { + return &_scope{ + lexical: lexical, + variable: variable, + this: this, + } +} diff --git a/vendor/github.com/robertkrimen/otto/script.go b/vendor/github.com/robertkrimen/otto/script.go new file mode 100644 index 00000000..2ae890ec --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/script.go @@ -0,0 +1,119 @@ +package otto + +import ( + "bytes" + "encoding/gob" + "errors" +) + +var ErrVersion = errors.New("version mismatch") + +var scriptVersion = "2014-04-13/1" + +// Script is a handle for some (reusable) JavaScript. +// Passing a Script value to a run method will evaluate the JavaScript. +// +type Script struct { + version string + program *_nodeProgram + filename string + src string +} + +// Compile will parse the given source and return a Script value or nil and +// an error if there was a problem during compilation. +// +// script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) +// vm.Run(script) +// +func (self *Otto) Compile(filename string, src interface{}) (*Script, error) { + return self.CompileWithSourceMap(filename, src, nil) +} + +// CompileWithSourceMap does the same thing as Compile, but with the obvious +// difference of applying a source map. +func (self *Otto) CompileWithSourceMap(filename string, src, sm interface{}) (*Script, error) { + program, err := self.runtime.parse(filename, src, sm) + if err != nil { + return nil, err + } + + cmpl_program := cmpl_parse(program) + + script := &Script{ + version: scriptVersion, + program: cmpl_program, + filename: filename, + src: program.File.Source(), + } + + return script, nil +} + +func (self *Script) String() string { + return "// " + self.filename + "\n" + self.src +} + +// MarshalBinary will marshal a script into a binary form. A marshalled script +// that is later unmarshalled can be executed on the same version of the otto runtime. +// +// The binary format can change at any time and should be considered unspecified and opaque. +// +func (self *Script) marshalBinary() ([]byte, error) { + var bfr bytes.Buffer + encoder := gob.NewEncoder(&bfr) + err := encoder.Encode(self.version) + if err != nil { + return nil, err + } + err = encoder.Encode(self.program) + if err != nil { + return nil, err + } + err = encoder.Encode(self.filename) + if err != nil { + return nil, err + } + err = encoder.Encode(self.src) + if err != nil { + return nil, err + } + return bfr.Bytes(), nil +} + +// UnmarshalBinary will vivify a marshalled script into something usable. If the script was +// originally marshalled on a different version of the otto runtime, then this method +// will return an error. +// +// The binary format can change at any time and should be considered unspecified and opaque. +// +func (self *Script) unmarshalBinary(data []byte) error { + decoder := gob.NewDecoder(bytes.NewReader(data)) + err := decoder.Decode(&self.version) + if err != nil { + goto error + } + if self.version != scriptVersion { + err = ErrVersion + goto error + } + err = decoder.Decode(&self.program) + if err != nil { + goto error + } + err = decoder.Decode(&self.filename) + if err != nil { + goto error + } + err = decoder.Decode(&self.src) + if err != nil { + goto error + } + return nil +error: + self.version = "" + self.program = nil + self.filename = "" + self.src = "" + return err +} diff --git a/vendor/github.com/robertkrimen/otto/script_test.go b/vendor/github.com/robertkrimen/otto/script_test.go new file mode 100644 index 00000000..90992374 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/script_test.go @@ -0,0 +1,94 @@ +package otto + +import ( + "testing" +) + +func TestScript(t *testing.T) { + tt(t, func() { + vm := New() + + script, err := vm.Compile("xyzzy", `var abc; if (!abc) abc = 0; abc += 2; abc;`) + is(err, nil) + + str := script.String() + is(str, "// xyzzy\nvar abc; if (!abc) abc = 0; abc += 2; abc;") + + value, err := vm.Run(script) + is(err, nil) + is(value, 2) + + if true { + return + } + + tmp, err := script.marshalBinary() + is(err, nil) + is(len(tmp), 1228) + + { + script := &Script{} + err = script.unmarshalBinary(tmp) + is(err, nil) + + is(script.String(), str) + + value, err = vm.Run(script) + is(err, nil) + is(value, 4) + + tmp, err = script.marshalBinary() + is(err, nil) + is(len(tmp), 1228) + } + + { + script := &Script{} + err = script.unmarshalBinary(tmp) + is(err, nil) + + is(script.String(), str) + + value, err := vm.Run(script) + is(err, nil) + is(value, 6) + + tmp, err = script.marshalBinary() + is(err, nil) + is(len(tmp), 1228) + } + + { + version := scriptVersion + scriptVersion = "bogus" + + script := &Script{} + err = script.unmarshalBinary(tmp) + is(err, "version mismatch") + + is(script.String(), "// \n") + is(script.version, "") + is(script.program == nil, true) + is(script.filename, "") + is(script.src, "") + + scriptVersion = version + } + }) +} + +func TestFunctionCall_CallerLocation(t *testing.T) { + tt(t, func() { + vm := New() + vm.Set("loc", func(call FunctionCall) Value { + return toValue(call.CallerLocation()) + }) + script, err := vm.Compile("somefile.js", `var where = loc();`) + is(err, nil) + _, err = vm.Run(script) + is(err, nil) + where, err := vm.Get("where") + is(err, nil) + is(where, "somefile.js:1:13") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/sourcemap_test.go b/vendor/github.com/robertkrimen/otto/sourcemap_test.go new file mode 100644 index 00000000..afb82410 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/sourcemap_test.go @@ -0,0 +1,163 @@ +package otto + +import ( + "testing" +) + +const ( + testSourcemapCodeOriginal = "function functionA(argA, argB) {\n functionB(argA, argB);\n}\n\nfunction functionB(argA, argB) {\n functionExternal(argA, argB);\n}" + testSourcemapCodeMangled = "function functionA(argA,argB){functionB(argA,argB)}function functionB(argA,argB){functionExternal(argA,argB)}" + testSourcemapContent = `{"version":3,"sources":["hello.js"],"names":["functionA","argA","argB","functionB","functionExternal"],"mappings":"AAAA,QAASA,WAAUC,KAAMC,MACvBC,UAAUF,KAAMC,MAGlB,QAASC,WAAUF,KAAMC,MACvBE,iBAAiBH,KAAMC"}` + testSourcemapInline = "function functionA(argA,argB){functionB(argA,argB)}function functionB(argA,argB){functionExternal(argA,argB)}\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhlbGxvLmpzIl0sIm5hbWVzIjpbImZ1bmN0aW9uQSIsImFyZ0EiLCJhcmdCIiwiZnVuY3Rpb25CIiwiZnVuY3Rpb25FeHRlcm5hbCJdLCJtYXBwaW5ncyI6IkFBQUEsUUFBU0EsV0FBVUMsS0FBTUMsTUFDdkJDLFVBQVVGLEtBQU1DLE1BR2xCLFFBQVNDLFdBQVVGLEtBQU1DLE1BQ3ZCRSxpQkFBaUJILEtBQU1DIn0=" + testSourcemapOriginalStack = "ReferenceError: 'functionExternal' is not defined\n at functionB (hello.js:6:3)\n at functionA (hello.js:2:3)\n at :1:1\n" + testSourcemapMangledStack = "ReferenceError: 'functionExternal' is not defined\n at functionB (hello.js:1:82)\n at functionA (hello.js:1:31)\n at :1:1\n" + testSourcemapMappedStack = "ReferenceError: 'functionExternal' is not defined\n at functionB (hello.js:6:2)\n at functionA (hello.js:2:2)\n at :1:1\n" +) + +func TestSourceMapOriginalWithNoSourcemap(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.Compile("hello.js", testSourcemapCodeOriginal) + if err != nil { + panic(err) + } + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err = vm.Run(`functionA()`) + if err == nil { + panic("error should not be nil") + } + + is(err.(*Error).String(), testSourcemapOriginalStack) + }) +} + +func TestSourceMapMangledWithNoSourcemap(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.Compile("hello.js", testSourcemapCodeMangled) + if err != nil { + panic(err) + } + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err = vm.Run(`functionA()`) + if err == nil { + panic("error should not be nil") + } + + is(err.(*Error).String(), testSourcemapMangledStack) + }) +} + +func TestSourceMapMangledWithSourcemap(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.CompileWithSourceMap("hello.js", testSourcemapCodeMangled, testSourcemapContent) + if err != nil { + panic(err) + } + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err = vm.Run(`functionA()`) + if err == nil { + panic("error should not be nil") + } + + is(err.(*Error).String(), testSourcemapMappedStack) + }) +} + +func TestSourceMapMangledWithInlineSourcemap(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.CompileWithSourceMap("hello.js", testSourcemapInline, nil) + if err != nil { + panic(err) + } + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + _, err = vm.Run(`functionA()`) + if err == nil { + panic("error should not be nil") + } + + is(err.(*Error).String(), testSourcemapMappedStack) + }) +} + +func TestSourceMapContextPosition(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.CompileWithSourceMap("hello.js", testSourcemapCodeMangled, testSourcemapContent) + if err != nil { + panic(err) + } + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + vm.Set("functionExternal", func(c FunctionCall) Value { + ctx := c.Otto.Context() + + is(ctx.Filename, "hello.js") + is(ctx.Line, 6) + is(ctx.Column, 2) + + return UndefinedValue() + }) + + if _, err := vm.Run(`functionA()`); err != nil { + panic(err) + } + }) +} + +func TestSourceMapContextStacktrace(t *testing.T) { + tt(t, func() { + vm := New() + + s, err := vm.CompileWithSourceMap("hello.js", testSourcemapCodeMangled, testSourcemapContent) + if err != nil { + panic(err) + } + + if _, err := vm.Run(s); err != nil { + panic(err) + } + + vm.Set("functionExternal", func(c FunctionCall) Value { + ctx := c.Otto.Context() + + is(ctx.Stacktrace, []string{ + "functionB (hello.js:6:2)", + "functionA (hello.js:2:2)", + ":1:1", + }) + + return UndefinedValue() + }) + + if _, err := vm.Run(`functionA()`); err != nil { + panic(err) + } + }) +} diff --git a/vendor/github.com/robertkrimen/otto/stash.go b/vendor/github.com/robertkrimen/otto/stash.go new file mode 100644 index 00000000..0d3ffa51 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/stash.go @@ -0,0 +1,296 @@ +package otto + +import ( + "fmt" +) + +// ====== +// _stash +// ====== + +type _stash interface { + hasBinding(string) bool // + createBinding(string, bool, Value) // CreateMutableBinding + setBinding(string, Value, bool) // SetMutableBinding + getBinding(string, bool) Value // GetBindingValue + deleteBinding(string) bool // + setValue(string, Value, bool) // createBinding + setBinding + + outer() _stash + runtime() *_runtime + + newReference(string, bool, _at) _reference + + clone(clone *_clone) _stash +} + +// ========== +// _objectStash +// ========== + +type _objectStash struct { + _runtime *_runtime + _outer _stash + object *_object +} + +func (self *_objectStash) runtime() *_runtime { + return self._runtime +} + +func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash { + if object == nil { + object = runtime.newBaseObject() + object.class = "environment" + } + return &_objectStash{ + _runtime: runtime, + _outer: outer, + object: object, + } +} + +func (in *_objectStash) clone(clone *_clone) _stash { + out, exists := clone.objectStash(in) + if exists { + return out + } + *out = _objectStash{ + clone.runtime, + clone.stash(in._outer), + clone.object(in.object), + } + return out +} + +func (self *_objectStash) hasBinding(name string) bool { + return self.object.hasProperty(name) +} + +func (self *_objectStash) createBinding(name string, deletable bool, value Value) { + if self.object.hasProperty(name) { + panic(hereBeDragons()) + } + mode := _propertyMode(0111) + if !deletable { + mode = _propertyMode(0110) + } + // TODO False? + self.object.defineProperty(name, value, mode, false) +} + +func (self *_objectStash) setBinding(name string, value Value, strict bool) { + self.object.put(name, value, strict) +} + +func (self *_objectStash) setValue(name string, value Value, throw bool) { + if !self.hasBinding(name) { + self.createBinding(name, true, value) // Configurable by default + } else { + self.setBinding(name, value, throw) + } +} + +func (self *_objectStash) getBinding(name string, throw bool) Value { + if self.object.hasProperty(name) { + return self.object.get(name) + } + if throw { // strict? + panic(self._runtime.panicReferenceError("Not Defined", name)) + } + return Value{} +} + +func (self *_objectStash) deleteBinding(name string) bool { + return self.object.delete(name, false) +} + +func (self *_objectStash) outer() _stash { + return self._outer +} + +func (self *_objectStash) newReference(name string, strict bool, at _at) _reference { + return newPropertyReference(self._runtime, self.object, name, strict, at) +} + +// ========= +// _dclStash +// ========= + +type _dclStash struct { + _runtime *_runtime + _outer _stash + property map[string]_dclProperty +} + +type _dclProperty struct { + value Value + mutable bool + deletable bool + readable bool +} + +func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash { + return &_dclStash{ + _runtime: runtime, + _outer: outer, + property: map[string]_dclProperty{}, + } +} + +func (in *_dclStash) clone(clone *_clone) _stash { + out, exists := clone.dclStash(in) + if exists { + return out + } + property := make(map[string]_dclProperty, len(in.property)) + for index, value := range in.property { + property[index] = clone.dclProperty(value) + } + *out = _dclStash{ + clone.runtime, + clone.stash(in._outer), + property, + } + return out +} + +func (self *_dclStash) hasBinding(name string) bool { + _, exists := self.property[name] + return exists +} + +func (self *_dclStash) runtime() *_runtime { + return self._runtime +} + +func (self *_dclStash) createBinding(name string, deletable bool, value Value) { + _, exists := self.property[name] + if exists { + panic(fmt.Errorf("createBinding: %s: already exists", name)) + } + self.property[name] = _dclProperty{ + value: value, + mutable: true, + deletable: deletable, + readable: false, + } +} + +func (self *_dclStash) setBinding(name string, value Value, strict bool) { + property, exists := self.property[name] + if !exists { + panic(fmt.Errorf("setBinding: %s: missing", name)) + } + if property.mutable { + property.value = value + self.property[name] = property + } else { + self._runtime.typeErrorResult(strict) + } +} + +func (self *_dclStash) setValue(name string, value Value, throw bool) { + if !self.hasBinding(name) { + self.createBinding(name, false, value) // NOT deletable by default + } else { + self.setBinding(name, value, throw) + } +} + +// FIXME This is called a __lot__ +func (self *_dclStash) getBinding(name string, throw bool) Value { + property, exists := self.property[name] + if !exists { + panic(fmt.Errorf("getBinding: %s: missing", name)) + } + if !property.mutable && !property.readable { + if throw { // strict? + panic(self._runtime.panicTypeError()) + } + return Value{} + } + return property.value +} + +func (self *_dclStash) deleteBinding(name string) bool { + property, exists := self.property[name] + if !exists { + return true + } + if !property.deletable { + return false + } + delete(self.property, name) + return true +} + +func (self *_dclStash) outer() _stash { + return self._outer +} + +func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference { + return &_stashReference{ + name: name, + base: self, + } +} + +// ======== +// _fnStash +// ======== + +type _fnStash struct { + _dclStash + arguments *_object + indexOfArgumentName map[string]string +} + +func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash { + return &_fnStash{ + _dclStash: _dclStash{ + _runtime: runtime, + _outer: outer, + property: map[string]_dclProperty{}, + }, + } +} + +func (in *_fnStash) clone(clone *_clone) _stash { + out, exists := clone.fnStash(in) + if exists { + return out + } + dclStash := in._dclStash.clone(clone).(*_dclStash) + index := make(map[string]string, len(in.indexOfArgumentName)) + for name, value := range in.indexOfArgumentName { + index[name] = value + } + *out = _fnStash{ + _dclStash: *dclStash, + arguments: clone.object(in.arguments), + indexOfArgumentName: index, + } + return out +} + +func getStashProperties(stash _stash) (keys []string) { + switch vars := stash.(type) { + case *_dclStash: + for k := range vars.property { + keys = append(keys, k) + } + case *_fnStash: + for k := range vars.property { + keys = append(keys, k) + } + case *_objectStash: + for k := range vars.object.property { + keys = append(keys, k) + } + default: + panic("unknown stash type") + } + + return +} diff --git a/vendor/github.com/robertkrimen/otto/string_test.go b/vendor/github.com/robertkrimen/otto/string_test.go new file mode 100644 index 00000000..9ba361ee --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/string_test.go @@ -0,0 +1,422 @@ +package otto + +import ( + "testing" +) + +func TestString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = (new String("xyzzy")).length; + def = new String().length; + ghi = new String("Nothing happens.").length; + `) + test("abc", 5) + test("def", 0) + test("ghi", 16) + test(`"".length`, 0) + test(`"a\uFFFFbc".length`, 4) + test(`String(+0)`, "0") + test(`String(-0)`, "0") + test(`""+-0`, "0") + test(` + var abc = Object.getOwnPropertyDescriptor(String, "prototype"); + [ [ typeof String.prototype ], + [ abc.writable, abc.enumerable, abc.configurable ] ]; + `, "object,false,false,false") + }) +} + +func TestString_charAt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = "xyzzy".charAt(0) + def = "xyzzy".charAt(11) + `) + test("abc", "x") + test("def", "") + }) +} + +func TestString_charCodeAt(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(` + abc = "xyzzy".charCodeAt(0) + def = "xyzzy".charCodeAt(11) + `) + test("abc", 120) + test("def", _NaN) + }) +} + +func TestString_fromCharCode(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`String.fromCharCode()`, []uint16{}) + test(`String.fromCharCode(88, 121, 122, 122, 121)`, []uint16{88, 121, 122, 122, 121}) // FIXME terst, Double-check these... + test(`String.fromCharCode("88", 121, 122, 122.05, 121)`, []uint16{88, 121, 122, 122, 121}) + test(`String.fromCharCode("88", 121, 122, NaN, 121)`, []uint16{88, 121, 122, 0, 121}) + test(`String.fromCharCode("0x21")`, []uint16{33}) + test(`String.fromCharCode(-1).charCodeAt(0)`, 65535) + test(`String.fromCharCode(65535).charCodeAt(0)`, 65535) + test(`String.fromCharCode(65534).charCodeAt(0)`, 65534) + test(`String.fromCharCode(4294967295).charCodeAt(0)`, 65535) + test(`String.fromCharCode(4294967294).charCodeAt(0)`, 65534) + test(`String.fromCharCode(0x0024) === "$"`, true) + }) +} + +func TestString_concat(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"".concat()`, "") + test(`"".concat("abc", "def")`, "abcdef") + test(`"".concat("abc", undefined, "def")`, "abcundefineddef") + }) +} + +func TestString_indexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"".indexOf("")`, 0) + test(`"".indexOf("", 11)`, 0) + test(`"abc".indexOf("")`, 0) + test(`"abc".indexOf("", 11)`, 3) + test(`"abc".indexOf("a")`, 0) + test(`"abc".indexOf("bc")`, 1) + test(`"abc".indexOf("bc", 11)`, -1) + test(`"$$abcdabcd".indexOf("ab", function(){return -Infinity;}())`, 2) + test(`"$$abcdabcd".indexOf("ab", function(){return NaN;}())`, 2) + + test(` + var abc = {toString:function(){return "\u0041B";}} + var def = {valueOf:function(){return true;}} + var ghi = "ABB\u0041BABAB"; + var jkl; + with(ghi) { + jkl = indexOf(abc, def); + } + jkl; + `, 3) + }) +} + +func TestString_lastIndexOf(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"".lastIndexOf("")`, 0) + test(`"".lastIndexOf("", 11)`, 0) + test(`"abc".lastIndexOf("")`, 3) + test(`"abc".lastIndexOf("", 11)`, 3) + test(`"abc".lastIndexOf("a")`, 0) + test(`"abc".lastIndexOf("bc")`, 1) + test(`"abc".lastIndexOf("bc", 11)`, 1) + test(`"abc".lastIndexOf("bc", 0)`, -1) + test(`"abc".lastIndexOf("abcabcabc", 2)`, -1) + test(`"abc".lastIndexOf("abc", 0)`, 0) + test(`"abc".lastIndexOf("abc", 1)`, 0) + test(`"abc".lastIndexOf("abc", 2)`, 0) + test(`"abc".lastIndexOf("abc", 3)`, 0) + + test(` + abc = new Object(true); + abc.lastIndexOf = String.prototype.lastIndexOf; + abc.lastIndexOf(true, false); + `, 0) + }) +} + +func TestString_match(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc____abc_abc___".match(/__abc/)`, "__abc") + test(`"abc___abc_abc__abc__abc".match(/abc/g)`, "abc,abc,abc,abc,abc") + test(`"abc____abc_abc___".match(/__abc/g)`, "__abc") + test(` + abc = /abc/g + "abc___abc_abc__abc__abc".match(abc) + `, "abc,abc,abc,abc,abc") + test(`abc.lastIndex`, 23) + }) +} + +func BenchmarkString_match(b *testing.B) { + vm := New() + s, _ := vm.Compile("test.js", `"abc____abc_abc___".match(/__abc/g)`) + for i := 0; i < b.N; i++ { + _, e := vm.Run(s) + if e != nil { + b.Error(e.Error()) + } + } +} + +func TestString_replace(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc_abc".replace(/abc/, "$&123")`, "abc123_abc") + test(`"abc_abc".replace(/abc/g, "$&123")`, "abc123_abc123") + test(`"abc_abc_".replace(/abc/g, "$&123")`, "abc123_abc123_") + test(`"_abc_abc_".replace(/abc/g, "$&123")`, "_abc123_abc123_") + test(`"abc".replace(/abc/, "$&123")`, "abc123") + test(`"abc_".replace(/abc/, "$&123")`, "abc123_") + test("\"^abc$\".replace(/abc/, \"$`def\")", "^^def$") + test("\"^abc$\".replace(/abc/, \"def$`\")", "^def^$") + test(`"_abc_abd_".replace(/ab(c|d)/g, "$1")`, "_c_d_") + test(` + "_abc_abd_".replace(/ab(c|d)/g, function(){ + }) + `, "_undefined_undefined_") + + test(`"b".replace(/(a)?(b)?/, "_$1_")`, "__") + test(` + "b".replace(/(a)?(b)?/, function(a, b, c, d, e, f){ + return [a, b, c, d, e, f] + }) + `, "b,,b,0,b,") + + test(` + var abc = 'She sells seashells by the seashore.'; + var def = /sh/; + [ abc.replace(def, "$'" + 'sch') ]; + `, "She sells seaells by the seashore.schells by the seashore.") + }) +} + +func BenchmarkString_replace(b *testing.B) { + vm := New() + s, _ := vm.Compile("test.js", `"_abc_abd_".replace(/ab(c|d)/g, "$1")`) + for i := 0; i < b.N; i++ { + _, e := vm.Run(s) + if e != nil { + b.Error(e.Error()) + } + } +} + +func TestString_search(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".search(/abc/)`, 0) + test(`"abc".search(/def/)`, -1) + test(`"abc".search(/c$/)`, 2) + test(`"abc".search(/$/)`, 3) + }) +} + +func BenchmarkString_search(b *testing.B) { + vm := New() + s, _ := vm.Compile("test.js", `"abc".search(/c$/)`) + for i := 0; i < b.N; i++ { + _, e := vm.Run(s) + if e != nil { + b.Error(e.Error()) + } + } +} + +func TestString_split(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".split("", 1)`, "a") + test(`"abc".split("", 2)`, "a,b") + test(`"abc".split("", 3)`, "a,b,c") + test(`"abc".split("", 4)`, "a,b,c") + test(`"abc".split("", 11)`, "a,b,c") + test(`"abc".split("", 0)`, "") + test(`"abc".split("")`, "a,b,c") + + test(`"abc".split(undefined)`, "abc") + + test(`"__1__3_1__2__".split("_")`, ",,1,,3,1,,2,,") + + test(`"__1__3_1__2__".split(/_/)`, ",,1,,3,1,,2,,") + + test(`"ab".split(/a*/)`, ",b") + + test(`_ = "Aboldandcoded".split(/<(\/)?([^<>]+)>/)`, "A,,B,bold,/,B,and,,CODE,coded,/,CODE,") + test(`_.length`, 13) + test(`_[1] === undefined`, true) + test(`_[12] === ""`, true) + + test(` + var abc = new String("one-1 two-2 three-3"); + var def = abc.split(new RegExp); + + [ def.constructor === Array, abc.length, def.length, def.join('') ]; + `, "true,19,19,one-1 two-2 three-3") + }) +} + +func BenchmarkString_splitWithString(b *testing.B) { + vm := New() + vm.Set("data", "Lorem ipsum dolor sit amet, blandit nec elit. Ridiculus tortor wisi fusce vivamus") + s, _ := vm.Compile("test.js", `data.split(" ")`) + for i := 0; i < b.N; i++ { + _, e := vm.Run(s) + if e != nil { + b.Error(e.Error()) + } + } +} + +func BenchmarkString_splitWithRegex(b *testing.B) { + vm := New() + vm.Set("data", "Lorem ipsum dolor sit amet, blandit nec elit. Ridiculus tortor wisi fusce vivamus") + s, _ := vm.Compile("test.js", `data.split(/ /)`) + for i := 0; i < b.N; i++ { + _, e := vm.Run(s) + if e != nil { + b.Error(e.Error()) + } + } +} + +func TestString_slice(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".slice()`, "abc") + test(`"abc".slice(0)`, "abc") + test(`"abc".slice(0,11)`, "abc") + test(`"abc".slice(0,-1)`, "ab") + test(`"abc".slice(-1,11)`, "c") + test(`abc = "abc"; abc.slice(abc.length+1, 0)`, "") + }) +} + +func TestString_substring(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".substring()`, "abc") + test(`"abc".substring(0)`, "abc") + test(`"abc".substring(0,11)`, "abc") + test(`"abc".substring(11,0)`, "abc") + test(`"abc".substring(0,-1)`, "") + test(`"abc".substring(-1,11)`, "abc") + test(`"abc".substring(11,1)`, "bc") + test(`"abc".substring(1)`, "bc") + test(`"abc".substring(Infinity, Infinity)`, "") + }) +} + +func TestString_toCase(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`"abc".toLowerCase()`, "abc") + test(`"ABC".toLowerCase()`, "abc") + test(`"abc".toLocaleLowerCase()`, "abc") + test(`"ABC".toLocaleLowerCase()`, "abc") + test(`"abc".toUpperCase()`, "ABC") + test(`"ABC".toUpperCase()`, "ABC") + test(`"abc".toLocaleUpperCase()`, "ABC") + test(`"ABC".toLocaleUpperCase()`, "ABC") + }) +} + +func Test_floatToString(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`String(-1234567890)`, "-1234567890") + test(`-+String(-(-1234567890))`, -1234567890) + test(`String(-1e128)`, "-1e+128") + test(`String(0.12345)`, "0.12345") + test(`String(-0.00000012345)`, "-1.2345e-7") + test(`String(0.0000012345)`, "0.0000012345") + test(`String(1000000000000000000000)`, "1e+21") + test(`String(1e21)`, "1e+21") + test(`String(1E21)`, "1e+21") + test(`String(-1000000000000000000000)`, "-1e+21") + test(`String(-1e21)`, "-1e+21") + test(`String(-1E21)`, "-1e+21") + test(`String(0.0000001)`, "1e-7") + test(`String(1e-7)`, "1e-7") + test(`String(1E-7)`, "1e-7") + test(`String(-0.0000001)`, "-1e-7") + test(`String(-1e-7)`, "-1e-7") + test(`String(-1E-7)`, "-1e-7") + }) +} + +func TestString_indexing(t *testing.T) { + tt(t, func() { + test, _ := test() + + // Actually a test of stringToArrayIndex, under the hood. + test(` + abc = new String("abc"); + index = Math.pow(2, 32); + [ abc.length, abc[index], abc[index+1], abc[index+2], abc[index+3] ]; + `, "3,,,,") + }) +} + +func TestString_trim(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`' \n abc \t \n'.trim();`, "abc") + test(`" abc\u000B".trim()`, "abc") + test(`"abc ".trim()`, "abc") + test(` + var a = "\u180Eabc \u000B " + var b = a.trim() + a.length + b.length + `, 10) + }) +} + +func TestString_trimLeft(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`" abc\u000B".trimLeft()`, "abc\u000B") + test(`"abc ".trimLeft()`, "abc ") + test(` + var a = "\u180Eabc \u000B " + var b = a.trimLeft() + a.length + b.length + `, 13) + }) +} + +func TestString_trimRight(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`" abc\u000B".trimRight()`, " abc") + test(`" abc ".trimRight()`, " abc") + test(` + var a = "\u180Eabc \u000B " + var b = a.trimRight() + a.length + b.length + `, 11) + }) +} + +func TestString_localeCompare(t *testing.T) { + tt(t, func() { + test, _ := test() + + test(`'a'.localeCompare('c');`, -1) + test(`'c'.localeCompare('a');`, 1) + test(`'a'.localeCompare('a');`, 0) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/terst/terst.go b/vendor/github.com/robertkrimen/otto/terst/terst.go new file mode 100644 index 00000000..a25ca8b9 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/terst/terst.go @@ -0,0 +1,669 @@ +// This file was AUTOMATICALLY GENERATED by terst-import (smuggol) from github.com/robertkrimen/terst + +/* +Package terst is a terse (terst = test + terse), easy-to-use testing library for Go. + +terst is compatible with (and works via) the standard testing package: http://golang.org/pkg/testing + + var is = terst.Is + + func Test(t *testing.T) { + terst.Terst(t, func() { + is("abc", "abc") + + is(1, ">", 0) + + var abc []int + is(abc, nil) + } + } + +Do not import terst directly, instead use `terst-import` to copy it into your testing environment: + +https://github.com/robertkrimen/terst/tree/master/terst-import + + $ go get github.com/robertkrimen/terst/terst-import + + $ terst-import + +*/ +package terst + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "reflect" + "regexp" + "runtime" + "strings" + "sync" + "testing" + "time" +) + +// Is compares two values (got & expect) and returns true if the comparison is true, +// false otherwise. In addition, if the comparison is false, Is will report the error +// in a manner similar to testing.T.Error(...). Is also takes an optional argument, +// a comparator, that changes how the comparison is made. The following +// comparators are available: +// +// == # got == expect (default) +// != # got != expect +// +// > # got > expect (float32, uint, uint16, int, int64, ...) +// >= # got >= expect +// < # got < expect +// <= # got <= expect +// +// =~ # regexp.MustCompile(expect).Match{String}(got) +// !~ # !regexp.MustCompile(expect).Match{String}(got) +// +// Basic usage with the default comparator (==): +// +// Is(, ) +// +// Specifying a different comparator: +// +// Is(, , ) +// +// A simple comparison: +// +// Is(2 + 2, 4) +// +// A bit trickier: +// +// Is(1, ">", 0) +// Is(2 + 2, "!=", 5) +// Is("Nothing happens.", "=~", `ing(\s+)happens\.$`) +// +// Is should only be called under a Terst(t, ...) call. For a standalone version, +// use IsErr. If no scope is found and the comparison is false, then Is will panic the error. +// +func Is(arguments ...interface{}) bool { + err := IsErr(arguments...) + if err != nil { + call := Caller() + if call == nil { + panic(err) + } + call.Error(err) + return false + } + return true +} + +type ( + // ErrFail indicates a comparison failure (e.g. 0 > 1). + ErrFail error + + // ErrInvalid indicates an invalid comparison (e.g. bool == string). + ErrInvalid error +) + +var errInvalid = errors.New("invalid") + +var registry = struct { + table map[uintptr]*_scope + lock sync.RWMutex +}{ + table: map[uintptr]*_scope{}, +} + +func registerScope(pc uintptr, scope *_scope) { + registry.lock.Lock() + defer registry.lock.Unlock() + registry.table[pc] = scope +} + +func scope() *_scope { + scope, _ := findScope() + return scope +} + +func floatCompare(a float64, b float64) int { + if a > b { + return 1 + } else if a < b { + return -1 + } + // NaN == NaN + return 0 +} + +func bigIntCompare(a *big.Int, b *big.Int) int { + return a.Cmp(b) +} + +func bigInt(value int64) *big.Int { + return big.NewInt(value) +} + +func bigUint(value uint64) *big.Int { + return big.NewInt(0).SetUint64(value) +} + +type _toString interface { + String() string +} + +func toString(value interface{}) (string, error) { + switch value := value.(type) { + case string: + return value, nil + case _toString: + return value.String(), nil + case error: + return value.Error(), nil + } + return "", errInvalid +} + +func matchString(got string, expect *regexp.Regexp) (int, error) { + if expect.MatchString(got) { + return 0, nil + } + return -1, nil +} + +func match(got []byte, expect *regexp.Regexp) (int, error) { + if expect.Match(got) { + return 0, nil + } + return -1, nil +} + +func compareMatch(got, expect interface{}) (int, error) { + switch got := got.(type) { + case []byte: + switch expect := expect.(type) { + case string: + matcher, err := regexp.Compile(expect) + if err != nil { + return 0, err + } + return match(got, matcher) + case *regexp.Regexp: + return match(got, expect) + } + default: + if got, err := toString(got); err == nil { + switch expect := expect.(type) { + case string: + matcher, err := regexp.Compile(expect) + if err != nil { + return 0, err + } + return matchString(got, matcher) + case *regexp.Regexp: + return matchString(got, expect) + } + } else { + return 0, err + } + } + return 0, errInvalid +} + +func floatPromote(value reflect.Value) (float64, error) { + kind := value.Kind() + if reflect.Int <= kind && kind <= reflect.Int64 { + return float64(value.Int()), nil + } + if reflect.Uint <= kind && kind <= reflect.Uint64 { + return float64(value.Uint()), nil + } + if reflect.Float32 <= kind && kind <= reflect.Float64 { + return value.Float(), nil + } + return 0, errInvalid +} + +func bigIntPromote(value reflect.Value) (*big.Int, error) { + kind := value.Kind() + if reflect.Int <= kind && kind <= reflect.Int64 { + return bigInt(value.Int()), nil + } + if reflect.Uint <= kind && kind <= reflect.Uint64 { + return bigUint(value.Uint()), nil + } + return nil, errInvalid +} + +func compareOther(got, expect interface{}) (int, error) { + { + switch expect.(type) { + case float32, float64: + return compareNumber(got, expect) + case uint, uint8, uint16, uint32, uint64: + return compareNumber(got, expect) + case int, int8, int16, int32, int64: + return compareNumber(got, expect) + case string: + var err error + got, err = toString(got) + if err != nil { + return 0, err + } + case nil: + got := reflect.ValueOf(got) + switch got.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface: + if got.IsNil() { + return 0, nil + } + return -1, nil + case reflect.Invalid: // reflect.Invalid: var abc interface{} = nil + return 0, nil + } + return 0, errInvalid + } + } + + if reflect.ValueOf(got).Type() != reflect.ValueOf(expect).Type() { + return 0, errInvalid + } + + if reflect.DeepEqual(got, expect) { + return 0, nil + } + return -1, nil +} + +func compareNumber(got, expect interface{}) (int, error) { + { + got := reflect.ValueOf(got) + k0 := got.Kind() + expect := reflect.ValueOf(expect) + k1 := expect.Kind() + if reflect.Float32 <= k0 && k0 <= reflect.Float64 || + reflect.Float32 <= k1 && k1 <= reflect.Float64 { + got, err := floatPromote(got) + if err != nil { + return 0, err + } + expect, err := floatPromote(expect) + if err != nil { + return 0, err + } + return floatCompare(got, expect), nil + } else { + got, err := bigIntPromote(got) + if err != nil { + return 0, err + } + expect, err := bigIntPromote(expect) + if err != nil { + return 0, err + } + return got.Cmp(expect), nil + } + } + + return 0, errInvalid +} + +// IsErr compares two values (got & expect) and returns nil if the comparison is true, an ErrFail if +// the comparison is false, or an ErrInvalid if the comparison is invalid. IsErr also +// takes an optional argument, a comparator, that changes how the comparison is made. +// +// Is & IsErr are similar but different: +// +// Is(...) // Should only be called within a Terst(...) call +// IsErr(...) // A standalone comparator, the same as Is, just without the automatic reporting +// +func IsErr(arguments ...interface{}) error { + var got, expect interface{} + comparator := "==" + switch len(arguments) { + case 0, 1: + return fmt.Errorf("invalid number of arguments to IsErr: %d", len(arguments)) + case 2: + got, expect = arguments[0], arguments[1] + default: + if value, ok := arguments[1].(string); ok { + comparator = value + } else { + return fmt.Errorf("invalid comparator: %v", arguments[1]) + } + got, expect = arguments[0], arguments[2] + } + + var result int + var err error + + switch comparator { + case "<", "<=", ">", ">=": + result, err = compareNumber(got, expect) + case "=~", "!~": + result, err = compareMatch(got, expect) + case "==", "!=": + result, err = compareOther(got, expect) + default: + return fmt.Errorf("invalid comparator: %s", comparator) + } + + if err == errInvalid { + return ErrInvalid(fmt.Errorf( + "\nINVALID (%s):\n got: %v (%T)\n expected: %v (%T)", + comparator, + got, got, + expect, expect, + )) + } else if err != nil { + return err + } + + equality, pass := false, false + + switch comparator { + case "==", "=~": + equality = true + pass = result == 0 + case "!=", "!~": + equality = true + pass = result != 0 + case "<": + pass = result < 0 + case "<=": + pass = result <= 0 + case ">": + pass = result > 0 + case ">=": + pass = result >= 0 + } + + if !pass { + if equality { + if comparator[1] == '~' { + if value, ok := got.([]byte); ok { + return ErrFail(fmt.Errorf( + "\nFAIL (%s)\n got: %s %v%s\nexpected: %v%s", + comparator, + value, got, typeKindString(got), + expect, typeKindString(expect), + )) + } + } + return ErrFail(fmt.Errorf( + "\nFAIL (%s)\n got: %v%s\nexpected: %v%s", + comparator, + got, typeKindString(got), + expect, typeKindString(expect), + )) + } + return ErrFail(fmt.Errorf( + "\nFAIL (%s)\n got: %v%s\nexpected: %s %v%s", + comparator, + got, typeKindString(got), + comparator, expect, typeKindString(expect), + )) + } + + return nil +} + +func typeKindString(value interface{}) string { + reflectValue := reflect.ValueOf(value) + kind := reflectValue.Kind().String() + result := fmt.Sprintf("%T", value) + if kind == result { + if kind == "string" { + return "" + } + return fmt.Sprintf(" (%T)", value) + } + return fmt.Sprintf(" (%T=%s)", value, kind) +} + +func (scope *_scope) reset() { + scope.name = "" + scope.output = scope.output[:] + scope.start = time.Time{} + scope.duration = 0 +} + +// Terst creates a testing scope, where Is can be called and errors will be reported +// according to the top-level location of the comparison, and not where the Is call +// actually takes place. For example: +// +// func test(value int) { +// Is(value, 5) // <--- This failure is reported below. +// } +// +// Terst(t, func(){ +// +// Is(2, ">", 3) // <--- An error is reported here. +// +// test(5) // <--- An error is reported here. +// +// }) +// +func Terst(t *testing.T, arguments ...func()) { + scope := &_scope{ + t: t, + } + + pc, _, _, ok := runtime.Caller(1) // TODO Associate with the Test... func + if !ok { + panic("Here be dragons.") + } + + _, scope.testFunc = findTestFunc() + + registerScope(pc, scope) + + for _, fn := range arguments { + func() { + scope.reset() + name := scope.testFunc.Name() + index := strings.LastIndex(scope.testFunc.Name(), ".") + if index >= 0 { + name = name[index+1:] + "(Terst)" + } else { + name = "(Terst)" + } + name = "(Terst)" + scope.name = name + scope.start = time.Now() + defer func() { + scope.duration = time.Now().Sub(scope.start) + if err := recover(); err != nil { + scope.t.Fail() + scope.report() + panic(err) + } + scope.report() + }() + fn() + }() + } +} + +// From "testing" +func (scope *_scope) report() { + format := "~~~ %s: (Terst)\n%s" + if scope.t.Failed() { + fmt.Printf(format, "FAIL", scope.output) + } else if testing.Verbose() && len(scope.output) > 0 { + fmt.Printf(format, "PASS", scope.output) + } +} + +func (scope *_scope) log(call _entry, str string) { + scope.mu.Lock() + defer scope.mu.Unlock() + scope.output = append(scope.output, decorate(call, str)...) +} + +// decorate prefixes the string with the file and line of the call site +// and inserts the final newline if needed and indentation tabs for formascing. +func decorate(call _entry, s string) string { + + file, line := call.File, call.Line + if call.PC > 0 { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] + } + } else { + file = "???" + line = 1 + } + buf := new(bytes.Buffer) + // Every line is indented at least one tab. + buf.WriteByte('\t') + fmt.Fprintf(buf, "%s:%d: ", file, line) + lines := strings.Split(s, "\n") + if l := len(lines); l > 1 && lines[l-1] == "" { + lines = lines[:l-1] + } + for i, line := range lines { + if i > 0 { + // Second and subsequent lines are indented an extra tab. + buf.WriteString("\n\t\t") + } + buf.WriteString(line) + } + buf.WriteByte('\n') + return buf.String() +} + +func findScope() (*_scope, _entry) { + registry.lock.RLock() + defer registry.lock.RUnlock() + table := registry.table + depth := 2 // Starting depth + call := _entry{} + for { + pc, _, _, ok := runtime.Caller(depth) + if !ok { + break + } + if scope, exists := table[pc]; exists { + pc, file, line, _ := runtime.Caller(depth - 3) // Terst(...) + func(){}() + fn() => ???() + call.PC = pc + call.File = file + call.Line = line + return scope, call + } + depth++ + } + return nil, _entry{} +} + +// Call is a reference to a line immediately under a Terst testing scope. +type Call struct { + scope *_scope + entry _entry +} + +// Caller will search the stack, looking for a Terst testing scope. If a scope +// is found, then Caller returns a Call for logging errors, accessing testing.T, etc. +// If no scope is found, Caller returns nil. +func Caller() *Call { + scope, entry := findScope() + if scope == nil { + return nil + } + return &Call{ + scope: scope, + entry: entry, + } +} + +// TestFunc returns the *runtime.Func entry for the top-level Test...(t testing.T) +// function. +func (cl *Call) TestFunc() *runtime.Func { + return cl.scope.testFunc +} + +// T returns the original testing.T passed to Terst(...) +func (cl *Call) T() *testing.T { + return cl.scope.t +} + +// Log is the terst version of `testing.T.Log` +func (cl *Call) Log(arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) +} + +// Logf is the terst version of `testing.T.Logf` +func (cl *Call) Logf(format string, arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) +} + +// Error is the terst version of `testing.T.Error` +func (cl *Call) Error(arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) + cl.scope.t.Fail() +} + +// Errorf is the terst version of `testing.T.Errorf` +func (cl *Call) Errorf(format string, arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) + cl.scope.t.Fail() +} + +// Skip is the terst version of `testing.T.Skip` +func (cl *Call) Skip(arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintln(arguments...)) + cl.scope.t.SkipNow() +} + +// Skipf is the terst version of `testing.T.Skipf` +func (cl *Call) Skipf(format string, arguments ...interface{}) { + cl.scope.log(cl.entry, fmt.Sprintf(format, arguments...)) + cl.scope.t.SkipNow() +} + +type _scope struct { + t *testing.T + testFunc *runtime.Func + name string + mu sync.RWMutex + output []byte + start time.Time + duration time.Duration +} + +type _entry struct { + PC uintptr + File string + Line int + Func *runtime.Func +} + +func _findFunc(match string) (_entry, *runtime.Func) { + depth := 2 // Starting depth + for { + pc, file, line, ok := runtime.Caller(depth) + if !ok { + break + } + fn := runtime.FuncForPC(pc) + name := fn.Name() + if index := strings.LastIndex(name, match); index >= 0 { + // Assume we have an instance of TestXyzzy in a _test file + return _entry{ + PC: pc, + File: file, + Line: line, + Func: fn, + }, fn + } + depth++ + } + return _entry{}, nil +} + +func findTestFunc() (_entry, *runtime.Func) { + return _findFunc(".Test") +} + +func findTerstFunc() (_entry, *runtime.Func) { + return _findFunc(".Terst") +} diff --git a/vendor/github.com/robertkrimen/otto/test/Makefile b/vendor/github.com/robertkrimen/otto/test/Makefile new file mode 100644 index 00000000..ac76fdea --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/test/Makefile @@ -0,0 +1,26 @@ +.PHONY: test fetch clean build err report + +TESTER := tester + +test: $(TESTER) + for test in test-*.js; do ./$^ -test=true $$test 1>/dev/null || exit 1; done + @echo PASS + +report: $(TESTER) + ./$^ -report | grep -v "MT READY" + +fetch: $(TESTER) + ./$^ fetch + +build: + go build -a -o $(TESTER) + +$(TESTER): tester.go + $(MAKE) build + +clean: + rm -f test-*.js + rm -f $(TESTER) + +err: $(TESTER) + for test in test-*.js; do ./$^ $$test; done 2>$@ diff --git a/vendor/github.com/robertkrimen/otto/test/tester.go b/vendor/github.com/robertkrimen/otto/test/tester.go new file mode 100644 index 00000000..ea694fd8 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/test/tester.go @@ -0,0 +1,196 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "regexp" + "strings" + "sync" + "text/tabwriter" + + "github.com/robertkrimen/otto" + "github.com/robertkrimen/otto/parser" +) + +var flag_test *bool = flag.Bool("test", false, "") +var flag_report *bool = flag.Bool("report", false, "") + +var match_ReferenceError_not_defined = regexp.MustCompile(`^ReferenceError: \S+ is not defined$`) +var match_lookahead = regexp.MustCompile(`Invalid regular expression: re2: Invalid \(\?[=!]\) `) +var match_backreference = regexp.MustCompile(`Invalid regular expression: re2: Invalid \\\d `) +var match_TypeError_undefined = regexp.MustCompile(`^TypeError: Cannot access member '[^']+' of undefined$`) + +var target = map[string]string{ + "test-angular-bindonce.js": "fail", // (anonymous): Line 1:944 Unexpected token ( (and 40 more errors) + "test-jsforce.js": "fail", // (anonymous): Line 9:28329 RuneError (and 5 more errors) + "test-chaplin.js": "parse", // Error: Chaplin requires Common.js or AMD modules + "test-dropbox.js.js": "parse", // Error: dropbox.js loaded in an unsupported JavaScript environment. + "test-epitome.js": "parse", // TypeError: undefined is not a function + "test-portal.js": "parse", // TypeError + "test-reactive-coffee.js": "parse", // Dependencies are not met for reactive: _ and $ not found + "test-scriptaculous.js": "parse", // script.aculo.us requires the Prototype JavaScript framework >= 1.6.0.3 + "test-waypoints.js": "parse", // TypeError: undefined is not a function + "test-webuploader.js": "parse", // Error: `jQuery` is undefined + "test-xuijs.js": "parse", // TypeError: undefined is not a function +} + +// http://cdnjs.com/ +// http://api.cdnjs.com/libraries + +func fetch(name, location string) error { + response, err := http.Get(location) + if err != nil { + return err + } + defer response.Body.Close() + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + + if !strings.HasSuffix(location, ".js") { + return nil + } + + filename := "test-" + name + ".js" + fmt.Println(filename, len(body)) + return ioutil.WriteFile(filename, body, 0644) +} + +func test(filename string) error { + script, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + if !*flag_report { + fmt.Fprintln(os.Stdout, filename, len(script)) + } + + parse := false + option := target[filename] + + if option != "parse" { + vm := otto.New() + _, err = vm.Run(string(script)) + if err != nil { + value := err.Error() + switch { + case match_ReferenceError_not_defined.MatchString(value): + case match_TypeError_undefined.MatchString(value): + case match_lookahead.MatchString(value): + case match_backreference.MatchString(value): + default: + return err + } + parse = true + } + } + + if parse { + _, err = parser.ParseFile(nil, filename, string(script), parser.IgnoreRegExpErrors) + if err != nil { + return err + } + target[filename] = "parse" + } + + return nil +} + +func main() { + flag.Parse() + + filename := "" + + err := func() error { + + if flag.Arg(0) == "fetch" { + response, err := http.Get("http://api.cdnjs.com/libraries") + if err != nil { + return err + } + defer response.Body.Close() + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + + var tmp map[string]interface{} + + err = json.Unmarshal(body, &tmp) + if err != nil { + return err + } + + var wg sync.WaitGroup + + for _, value := range tmp["results"].([]interface{}) { + wg.Add(1) + library := value.(map[string]interface{}) + go func() { + defer wg.Done() + fetch(library["name"].(string), library["latest"].(string)) + }() + } + + wg.Wait() + + return nil + } + + if *flag_report { + files, err := ioutil.ReadDir(".") + if err != nil { + return err + } + writer := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Fprintln(writer, "", "\t| Status") + fmt.Fprintln(writer, "---", "\t| ---") + for _, file := range files { + filename := file.Name() + if !strings.HasPrefix(filename, "test-") { + continue + } + err := test(filename) + option := target[filename] + name := strings.TrimPrefix(strings.TrimSuffix(filename, ".js"), "test-") + if err == nil { + switch option { + case "": + fmt.Fprintln(writer, name, "\t| pass") + case "parse": + fmt.Fprintln(writer, name, "\t| pass (parse)") + case "re2": + continue + fmt.Fprintln(writer, name, "\t| unknown (re2)") + } + } else { + fmt.Fprintln(writer, name, "\t| fail") + } + } + writer.Flush() + return nil + } + + filename = flag.Arg(0) + return test(filename) + + }() + if err != nil { + if filename != "" { + if *flag_test && target[filename] == "fail" { + goto exit + } + fmt.Fprintf(os.Stderr, "%s: %s\n", filename, err.Error()) + } else { + fmt.Fprintln(os.Stderr, err) + } + os.Exit(64) + } +exit: +} diff --git a/vendor/github.com/robertkrimen/otto/testing_test.go b/vendor/github.com/robertkrimen/otto/testing_test.go new file mode 100644 index 00000000..a210a7d9 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/testing_test.go @@ -0,0 +1,136 @@ +package otto + +import ( + "errors" + "strings" + "testing" + "time" + + "github.com/robertkrimen/otto/terst" +) + +func tt(t *testing.T, arguments ...func()) { + halt := errors.New("A test was taking too long") + timer := time.AfterFunc(2*time.Second, func() { + panic(halt) + }) + defer func() { + timer.Stop() + }() + terst.Terst(t, arguments...) +} + +func is(arguments ...interface{}) bool { + var got, expect interface{} + + switch len(arguments) { + case 0, 1: + return terst.Is(arguments...) + case 2: + got, expect = arguments[0], arguments[1] + default: + got, expect = arguments[0], arguments[2] + } + + switch value := got.(type) { + case Value: + if value.value != nil { + got = value.value + } + case *Error: + if value != nil { + got = value.Error() + } + if expect == nil { + // FIXME This is weird + expect = "" + } + } + + if len(arguments) == 2 { + arguments[0] = got + arguments[1] = expect + } else { + arguments[0] = got + arguments[2] = expect + } + + return terst.Is(arguments...) +} + +func test(arguments ...interface{}) (func(string, ...interface{}) Value, *_tester) { + tester := newTester() + if len(arguments) > 0 { + tester.test(arguments[0].(string)) + } + return tester.test, tester +} + +type _tester struct { + vm *Otto +} + +func newTester() *_tester { + return &_tester{ + vm: New(), + } +} + +func (self *_tester) Get(name string) (Value, error) { + return self.vm.Get(name) +} + +func (self *_tester) Set(name string, value interface{}) Value { + err := self.vm.Set(name, value) + is(err, nil) + if err != nil { + terst.Caller().T().FailNow() + } + return self.vm.getValue(name) +} + +func (self *_tester) Run(src interface{}) (Value, error) { + return self.vm.Run(src) +} + +func (self *_tester) test(name string, expect ...interface{}) Value { + vm := self.vm + raise := false + defer func() { + if caught := recover(); caught != nil { + if exception, ok := caught.(*_exception); ok { + caught = exception.eject() + } + if raise { + if len(expect) > 0 { + is(caught, expect[0]) + } + } else { + dbg("Panic, caught:", caught) + panic(caught) + } + } + }() + var value Value + var err error + if isIdentifier(name) { + value = vm.getValue(name) + } else { + source := name + index := strings.Index(source, "raise:") + if index == 0 { + raise = true + source = source[6:] + source = strings.TrimLeft(source, " ") + } + value, err = vm.runtime.cmpl_run(source, nil) + if err != nil { + panic(err) + } + } + value = value.resolve() + if len(expect) > 0 { + is(value, expect[0]) + } + return value +} diff --git a/vendor/github.com/robertkrimen/otto/token/Makefile b/vendor/github.com/robertkrimen/otto/token/Makefile new file mode 100644 index 00000000..1e85c734 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/token/Makefile @@ -0,0 +1,2 @@ +token_const.go: tokenfmt + ./$^ | gofmt > $@ diff --git a/vendor/github.com/robertkrimen/otto/token/README.markdown b/vendor/github.com/robertkrimen/otto/token/README.markdown new file mode 100644 index 00000000..ff3b1610 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/token/README.markdown @@ -0,0 +1,171 @@ +# token +-- + import "github.com/robertkrimen/otto/token" + +Package token defines constants representing the lexical tokens of JavaScript +(ECMA5). + +## Usage + +```go +const ( + ILLEGAL + EOF + COMMENT + KEYWORD + + STRING + BOOLEAN + NULL + NUMBER + IDENTIFIER + + PLUS // + + MINUS // - + MULTIPLY // * + SLASH // / + REMAINDER // % + + AND // & + OR // | + EXCLUSIVE_OR // ^ + SHIFT_LEFT // << + SHIFT_RIGHT // >> + UNSIGNED_SHIFT_RIGHT // >>> + AND_NOT // &^ + + ADD_ASSIGN // += + SUBTRACT_ASSIGN // -= + MULTIPLY_ASSIGN // *= + QUOTIENT_ASSIGN // /= + REMAINDER_ASSIGN // %= + + AND_ASSIGN // &= + OR_ASSIGN // |= + EXCLUSIVE_OR_ASSIGN // ^= + SHIFT_LEFT_ASSIGN // <<= + SHIFT_RIGHT_ASSIGN // >>= + UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>= + AND_NOT_ASSIGN // &^= + + LOGICAL_AND // && + LOGICAL_OR // || + INCREMENT // ++ + DECREMENT // -- + + EQUAL // == + STRICT_EQUAL // === + LESS // < + GREATER // > + ASSIGN // = + NOT // ! + + BITWISE_NOT // ~ + + NOT_EQUAL // != + STRICT_NOT_EQUAL // !== + LESS_OR_EQUAL // <= + GREATER_OR_EQUAL // >= + + LEFT_PARENTHESIS // ( + LEFT_BRACKET // [ + LEFT_BRACE // { + COMMA // , + PERIOD // . + + RIGHT_PARENTHESIS // ) + RIGHT_BRACKET // ] + RIGHT_BRACE // } + SEMICOLON // ; + COLON // : + QUESTION_MARK // ? + + IF + IN + DO + + VAR + FOR + NEW + TRY + + THIS + ELSE + CASE + VOID + WITH + + WHILE + BREAK + CATCH + THROW + + RETURN + TYPEOF + DELETE + SWITCH + + DEFAULT + FINALLY + + FUNCTION + CONTINUE + DEBUGGER + + INSTANCEOF +) +``` + +#### type Token + +```go +type Token int +``` + +Token is the set of lexical tokens in JavaScript (ECMA5). + +#### func IsKeyword + +```go +func IsKeyword(literal string) (Token, bool) +``` +IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token if +the literal is a future keyword (const, let, class, super, ...), or 0 if the +literal is not a keyword. + +If the literal is a keyword, IsKeyword returns a second value indicating if the +literal is considered a future keyword in strict-mode only. + +7.6.1.2 Future Reserved Words: + + const + class + enum + export + extends + import + super + +7.6.1.2 Future Reserved Words (strict): + + implements + interface + let + package + private + protected + public + static + +#### func (Token) String + +```go +func (tkn Token) String() string +``` +String returns the string corresponding to the token. For operators, delimiters, +and keywords the string is the actual token string (e.g., for the token PLUS, +the String() is "+"). For all other tokens the string corresponds to the token +name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER"). + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/robertkrimen/otto/token/token.go b/vendor/github.com/robertkrimen/otto/token/token.go new file mode 100644 index 00000000..0e941ac9 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/token/token.go @@ -0,0 +1,116 @@ +// Package token defines constants representing the lexical tokens of JavaScript (ECMA5). +package token + +import ( + "strconv" +) + +// Token is the set of lexical tokens in JavaScript (ECMA5). +type Token int + +// String returns the string corresponding to the token. +// For operators, delimiters, and keywords the string is the actual +// token string (e.g., for the token PLUS, the String() is +// "+"). For all other tokens the string corresponds to the token +// name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER"). +// +func (tkn Token) String() string { + if 0 == tkn { + return "UNKNOWN" + } + if tkn < Token(len(token2string)) { + return token2string[tkn] + } + return "token(" + strconv.Itoa(int(tkn)) + ")" +} + +// This is not used for anything +func (tkn Token) precedence(in bool) int { + + switch tkn { + case LOGICAL_OR: + return 1 + + case LOGICAL_AND: + return 2 + + case OR, OR_ASSIGN: + return 3 + + case EXCLUSIVE_OR: + return 4 + + case AND, AND_ASSIGN, AND_NOT, AND_NOT_ASSIGN: + return 5 + + case EQUAL, + NOT_EQUAL, + STRICT_EQUAL, + STRICT_NOT_EQUAL: + return 6 + + case LESS, GREATER, LESS_OR_EQUAL, GREATER_OR_EQUAL, INSTANCEOF: + return 7 + + case IN: + if in { + return 7 + } + return 0 + + case SHIFT_LEFT, SHIFT_RIGHT, UNSIGNED_SHIFT_RIGHT: + fallthrough + case SHIFT_LEFT_ASSIGN, SHIFT_RIGHT_ASSIGN, UNSIGNED_SHIFT_RIGHT_ASSIGN: + return 8 + + case PLUS, MINUS, ADD_ASSIGN, SUBTRACT_ASSIGN: + return 9 + + case MULTIPLY, SLASH, REMAINDER, MULTIPLY_ASSIGN, QUOTIENT_ASSIGN, REMAINDER_ASSIGN: + return 11 + } + return 0 +} + +type _keyword struct { + token Token + futureKeyword bool + strict bool +} + +// IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token +// if the literal is a future keyword (const, let, class, super, ...), or 0 if the literal is not a keyword. +// +// If the literal is a keyword, IsKeyword returns a second value indicating if the literal +// is considered a future keyword in strict-mode only. +// +// 7.6.1.2 Future Reserved Words: +// +// const +// class +// enum +// export +// extends +// import +// super +// +// 7.6.1.2 Future Reserved Words (strict): +// +// implements +// interface +// let +// package +// private +// protected +// public +// static +// +func IsKeyword(literal string) (Token, bool) { + if keyword, exists := keywordTable[literal]; exists { + if keyword.futureKeyword { + return KEYWORD, keyword.strict + } + return keyword.token, false + } + return 0, false +} diff --git a/vendor/github.com/robertkrimen/otto/token/token_const.go b/vendor/github.com/robertkrimen/otto/token/token_const.go new file mode 100644 index 00000000..b1d83c6d --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/token/token_const.go @@ -0,0 +1,349 @@ +package token + +const ( + _ Token = iota + + ILLEGAL + EOF + COMMENT + KEYWORD + + STRING + BOOLEAN + NULL + NUMBER + IDENTIFIER + + PLUS // + + MINUS // - + MULTIPLY // * + SLASH // / + REMAINDER // % + + AND // & + OR // | + EXCLUSIVE_OR // ^ + SHIFT_LEFT // << + SHIFT_RIGHT // >> + UNSIGNED_SHIFT_RIGHT // >>> + AND_NOT // &^ + + ADD_ASSIGN // += + SUBTRACT_ASSIGN // -= + MULTIPLY_ASSIGN // *= + QUOTIENT_ASSIGN // /= + REMAINDER_ASSIGN // %= + + AND_ASSIGN // &= + OR_ASSIGN // |= + EXCLUSIVE_OR_ASSIGN // ^= + SHIFT_LEFT_ASSIGN // <<= + SHIFT_RIGHT_ASSIGN // >>= + UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>= + AND_NOT_ASSIGN // &^= + + LOGICAL_AND // && + LOGICAL_OR // || + INCREMENT // ++ + DECREMENT // -- + + EQUAL // == + STRICT_EQUAL // === + LESS // < + GREATER // > + ASSIGN // = + NOT // ! + + BITWISE_NOT // ~ + + NOT_EQUAL // != + STRICT_NOT_EQUAL // !== + LESS_OR_EQUAL // <= + GREATER_OR_EQUAL // >= + + LEFT_PARENTHESIS // ( + LEFT_BRACKET // [ + LEFT_BRACE // { + COMMA // , + PERIOD // . + + RIGHT_PARENTHESIS // ) + RIGHT_BRACKET // ] + RIGHT_BRACE // } + SEMICOLON // ; + COLON // : + QUESTION_MARK // ? + + firstKeyword + IF + IN + DO + + VAR + FOR + NEW + TRY + + THIS + ELSE + CASE + VOID + WITH + + WHILE + BREAK + CATCH + THROW + + RETURN + TYPEOF + DELETE + SWITCH + + DEFAULT + FINALLY + + FUNCTION + CONTINUE + DEBUGGER + + INSTANCEOF + lastKeyword +) + +var token2string = [...]string{ + ILLEGAL: "ILLEGAL", + EOF: "EOF", + COMMENT: "COMMENT", + KEYWORD: "KEYWORD", + STRING: "STRING", + BOOLEAN: "BOOLEAN", + NULL: "NULL", + NUMBER: "NUMBER", + IDENTIFIER: "IDENTIFIER", + PLUS: "+", + MINUS: "-", + MULTIPLY: "*", + SLASH: "/", + REMAINDER: "%", + AND: "&", + OR: "|", + EXCLUSIVE_OR: "^", + SHIFT_LEFT: "<<", + SHIFT_RIGHT: ">>", + UNSIGNED_SHIFT_RIGHT: ">>>", + AND_NOT: "&^", + ADD_ASSIGN: "+=", + SUBTRACT_ASSIGN: "-=", + MULTIPLY_ASSIGN: "*=", + QUOTIENT_ASSIGN: "/=", + REMAINDER_ASSIGN: "%=", + AND_ASSIGN: "&=", + OR_ASSIGN: "|=", + EXCLUSIVE_OR_ASSIGN: "^=", + SHIFT_LEFT_ASSIGN: "<<=", + SHIFT_RIGHT_ASSIGN: ">>=", + UNSIGNED_SHIFT_RIGHT_ASSIGN: ">>>=", + AND_NOT_ASSIGN: "&^=", + LOGICAL_AND: "&&", + LOGICAL_OR: "||", + INCREMENT: "++", + DECREMENT: "--", + EQUAL: "==", + STRICT_EQUAL: "===", + LESS: "<", + GREATER: ">", + ASSIGN: "=", + NOT: "!", + BITWISE_NOT: "~", + NOT_EQUAL: "!=", + STRICT_NOT_EQUAL: "!==", + LESS_OR_EQUAL: "<=", + GREATER_OR_EQUAL: ">=", + LEFT_PARENTHESIS: "(", + LEFT_BRACKET: "[", + LEFT_BRACE: "{", + COMMA: ",", + PERIOD: ".", + RIGHT_PARENTHESIS: ")", + RIGHT_BRACKET: "]", + RIGHT_BRACE: "}", + SEMICOLON: ";", + COLON: ":", + QUESTION_MARK: "?", + IF: "if", + IN: "in", + DO: "do", + VAR: "var", + FOR: "for", + NEW: "new", + TRY: "try", + THIS: "this", + ELSE: "else", + CASE: "case", + VOID: "void", + WITH: "with", + WHILE: "while", + BREAK: "break", + CATCH: "catch", + THROW: "throw", + RETURN: "return", + TYPEOF: "typeof", + DELETE: "delete", + SWITCH: "switch", + DEFAULT: "default", + FINALLY: "finally", + FUNCTION: "function", + CONTINUE: "continue", + DEBUGGER: "debugger", + INSTANCEOF: "instanceof", +} + +var keywordTable = map[string]_keyword{ + "if": _keyword{ + token: IF, + }, + "in": _keyword{ + token: IN, + }, + "do": _keyword{ + token: DO, + }, + "var": _keyword{ + token: VAR, + }, + "for": _keyword{ + token: FOR, + }, + "new": _keyword{ + token: NEW, + }, + "try": _keyword{ + token: TRY, + }, + "this": _keyword{ + token: THIS, + }, + "else": _keyword{ + token: ELSE, + }, + "case": _keyword{ + token: CASE, + }, + "void": _keyword{ + token: VOID, + }, + "with": _keyword{ + token: WITH, + }, + "while": _keyword{ + token: WHILE, + }, + "break": _keyword{ + token: BREAK, + }, + "catch": _keyword{ + token: CATCH, + }, + "throw": _keyword{ + token: THROW, + }, + "return": _keyword{ + token: RETURN, + }, + "typeof": _keyword{ + token: TYPEOF, + }, + "delete": _keyword{ + token: DELETE, + }, + "switch": _keyword{ + token: SWITCH, + }, + "default": _keyword{ + token: DEFAULT, + }, + "finally": _keyword{ + token: FINALLY, + }, + "function": _keyword{ + token: FUNCTION, + }, + "continue": _keyword{ + token: CONTINUE, + }, + "debugger": _keyword{ + token: DEBUGGER, + }, + "instanceof": _keyword{ + token: INSTANCEOF, + }, + "const": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "class": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "enum": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "export": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "extends": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "import": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "super": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "implements": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "interface": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "let": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "package": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "private": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "protected": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "public": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "static": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, +} diff --git a/vendor/github.com/robertkrimen/otto/token/tokenfmt b/vendor/github.com/robertkrimen/otto/token/tokenfmt new file mode 100755 index 00000000..63dd5d9e --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/token/tokenfmt @@ -0,0 +1,222 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my (%token, @order, @keywords); + +{ + my $keywords; + my @const; + push @const, <<_END_; +package token + +const( + _ Token = iota +_END_ + + for (split m/\n/, <<_END_) { +ILLEGAL +EOF +COMMENT +KEYWORD + +STRING +BOOLEAN +NULL +NUMBER +IDENTIFIER + +PLUS + +MINUS - +MULTIPLY * +SLASH / +REMAINDER % + +AND & +OR | +EXCLUSIVE_OR ^ +SHIFT_LEFT << +SHIFT_RIGHT >> +UNSIGNED_SHIFT_RIGHT >>> +AND_NOT &^ + +ADD_ASSIGN += +SUBTRACT_ASSIGN -= +MULTIPLY_ASSIGN *= +QUOTIENT_ASSIGN /= +REMAINDER_ASSIGN %= + +AND_ASSIGN &= +OR_ASSIGN |= +EXCLUSIVE_OR_ASSIGN ^= +SHIFT_LEFT_ASSIGN <<= +SHIFT_RIGHT_ASSIGN >>= +UNSIGNED_SHIFT_RIGHT_ASSIGN >>>= +AND_NOT_ASSIGN &^= + +LOGICAL_AND && +LOGICAL_OR || +INCREMENT ++ +DECREMENT -- + +EQUAL == +STRICT_EQUAL === +LESS < +GREATER > +ASSIGN = +NOT ! + +BITWISE_NOT ~ + +NOT_EQUAL != +STRICT_NOT_EQUAL !== +LESS_OR_EQUAL <= +GREATER_OR_EQUAL <= + +LEFT_PARENTHESIS ( +LEFT_BRACKET [ +LEFT_BRACE { +COMMA , +PERIOD . + +RIGHT_PARENTHESIS ) +RIGHT_BRACKET ] +RIGHT_BRACE } +SEMICOLON ; +COLON : +QUESTION_MARK ? + +firstKeyword +IF +IN +DO + +VAR +FOR +NEW +TRY + +THIS +ELSE +CASE +VOID +WITH + +WHILE +BREAK +CATCH +THROW + +RETURN +TYPEOF +DELETE +SWITCH + +DEFAULT +FINALLY + +FUNCTION +CONTINUE +DEBUGGER + +INSTANCEOF +lastKeyword +_END_ + chomp; + + next if m/^\s*#/; + + my ($name, $symbol) = m/(\w+)\s*(\S+)?/; + + if (defined $symbol) { + push @order, $name; + push @const, "$name // $symbol"; + $token{$name} = $symbol; + } elsif (defined $name) { + $keywords ||= $name eq 'firstKeyword'; + + push @const, $name; + #$const[-1] .= " Token = iota" if 2 == @const; + if ($name =~ m/^([A-Z]+)/) { + push @keywords, $name if $keywords; + push @order, $name; + if ($token{SEMICOLON}) { + $token{$name} = lc $1; + } else { + $token{$name} = $name; + } + } + } else { + push @const, ""; + } + + } + push @const, ")"; + print join "\n", @const, ""; +} + +{ + print <<_END_; + +var token2string = [...]string{ +_END_ + for my $name (@order) { + print "$name: \"$token{$name}\",\n"; + } + print <<_END_; +} +_END_ + + print <<_END_; + +var keywordTable = map[string]_keyword{ +_END_ + for my $name (@keywords) { + print <<_END_ + "@{[ lc $name ]}": _keyword{ + token: $name, + }, +_END_ + } + + for my $name (qw/ + const + class + enum + export + extends + import + super + /) { + print <<_END_ + "$name": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, +_END_ + } + + for my $name (qw/ + implements + interface + let + package + private + protected + public + static + /) { + print <<_END_ + "$name": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, +_END_ + } + + print <<_END_; +} +_END_ +} diff --git a/vendor/github.com/robertkrimen/otto/type_arguments.go b/vendor/github.com/robertkrimen/otto/type_arguments.go new file mode 100644 index 00000000..841d7585 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_arguments.go @@ -0,0 +1,106 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newArgumentsObject(indexOfParameterName []string, stash _stash, length int) *_object { + self := runtime.newClassObject("Arguments") + + for index, _ := range indexOfParameterName { + name := strconv.FormatInt(int64(index), 10) + objectDefineOwnProperty(self, name, _property{Value{}, 0111}, false) + } + + self.objectClass = _classArguments + self.value = _argumentsObject{ + indexOfParameterName: indexOfParameterName, + stash: stash, + } + + self.prototype = runtime.global.ObjectPrototype + + self.defineProperty("length", toValue_int(length), 0101, false) + + return self +} + +type _argumentsObject struct { + indexOfParameterName []string + // function(abc, def, ghi) + // indexOfParameterName[0] = "abc" + // indexOfParameterName[1] = "def" + // indexOfParameterName[2] = "ghi" + // ... + stash _stash +} + +func (in _argumentsObject) clone(clone *_clone) _argumentsObject { + indexOfParameterName := make([]string, len(in.indexOfParameterName)) + copy(indexOfParameterName, in.indexOfParameterName) + return _argumentsObject{ + indexOfParameterName, + clone.stash(in.stash), + } +} + +func (self _argumentsObject) get(name string) (Value, bool) { + index := stringToArrayIndex(name) + if index >= 0 && index < int64(len(self.indexOfParameterName)) { + name := self.indexOfParameterName[index] + if name == "" { + return Value{}, false + } + return self.stash.getBinding(name, false), true + } + return Value{}, false +} + +func (self _argumentsObject) put(name string, value Value) { + index := stringToArrayIndex(name) + name = self.indexOfParameterName[index] + self.stash.setBinding(name, value, false) +} + +func (self _argumentsObject) delete(name string) { + index := stringToArrayIndex(name) + self.indexOfParameterName[index] = "" +} + +func argumentsGet(self *_object, name string) Value { + if value, exists := self.value.(_argumentsObject).get(name); exists { + return value + } + return objectGet(self, name) +} + +func argumentsGetOwnProperty(self *_object, name string) *_property { + property := objectGetOwnProperty(self, name) + if value, exists := self.value.(_argumentsObject).get(name); exists { + property.value = value + } + return property +} + +func argumentsDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + if _, exists := self.value.(_argumentsObject).get(name); exists { + if !objectDefineOwnProperty(self, name, descriptor, false) { + return self.runtime.typeErrorResult(throw) + } + if value, valid := descriptor.value.(Value); valid { + self.value.(_argumentsObject).put(name, value) + } + return true + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func argumentsDelete(self *_object, name string, throw bool) bool { + if !objectDelete(self, name, throw) { + return false + } + if _, exists := self.value.(_argumentsObject).get(name); exists { + self.value.(_argumentsObject).delete(name) + } + return true +} diff --git a/vendor/github.com/robertkrimen/otto/type_array.go b/vendor/github.com/robertkrimen/otto/type_array.go new file mode 100644 index 00000000..c8a974b0 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_array.go @@ -0,0 +1,109 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newArrayObject(length uint32) *_object { + self := runtime.newObject() + self.class = "Array" + self.defineProperty("length", toValue_uint32(length), 0100, false) + self.objectClass = _classArray + return self +} + +func isArray(object *_object) bool { + return object != nil && (object.class == "Array" || object.class == "GoArray") +} + +func objectLength(object *_object) uint32 { + if object == nil { + return 0 + } + switch object.class { + case "Array": + return object.get("length").value.(uint32) + case "String": + return uint32(object.get("length").value.(int)) + case "GoArray": + return uint32(object.get("length").value.(int)) + } + return 0 +} + +func arrayUint32(rt *_runtime, value Value) uint32 { + nm := value.number() + if nm.kind != numberInteger || !isUint32(nm.int64) { + // FIXME + panic(rt.panicRangeError()) + } + return uint32(nm.int64) +} + +func arrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + lengthProperty := self.getOwnProperty("length") + lengthValue, valid := lengthProperty.value.(Value) + if !valid { + panic("Array.length != Value{}") + } + length := lengthValue.value.(uint32) + if name == "length" { + if descriptor.value == nil { + return objectDefineOwnProperty(self, name, descriptor, throw) + } + newLengthValue, isValue := descriptor.value.(Value) + if !isValue { + panic(self.runtime.panicTypeError()) + } + newLength := arrayUint32(self.runtime, newLengthValue) + descriptor.value = toValue_uint32(newLength) + if newLength > length { + return objectDefineOwnProperty(self, name, descriptor, throw) + } + if !lengthProperty.writable() { + goto Reject + } + newWritable := true + if descriptor.mode&0700 == 0 { + // If writable is off + newWritable = false + descriptor.mode |= 0100 + } + if !objectDefineOwnProperty(self, name, descriptor, throw) { + return false + } + for newLength < length { + length-- + if !self.delete(strconv.FormatInt(int64(length), 10), false) { + descriptor.value = toValue_uint32(length + 1) + if !newWritable { + descriptor.mode &= 0077 + } + objectDefineOwnProperty(self, name, descriptor, false) + goto Reject + } + } + if !newWritable { + descriptor.mode &= 0077 + objectDefineOwnProperty(self, name, descriptor, false) + } + } else if index := stringToArrayIndex(name); index >= 0 { + if index >= int64(length) && !lengthProperty.writable() { + goto Reject + } + if !objectDefineOwnProperty(self, strconv.FormatInt(index, 10), descriptor, false) { + goto Reject + } + if index >= int64(length) { + lengthProperty.value = toValue_uint32(uint32(index + 1)) + objectDefineOwnProperty(self, "length", *lengthProperty, false) + return true + } + } + return objectDefineOwnProperty(self, name, descriptor, throw) +Reject: + if throw { + panic(self.runtime.panicTypeError()) + } + return false +} diff --git a/vendor/github.com/robertkrimen/otto/type_boolean.go b/vendor/github.com/robertkrimen/otto/type_boolean.go new file mode 100644 index 00000000..afc45c69 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_boolean.go @@ -0,0 +1,13 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newBooleanObject(value Value) *_object { + return runtime.newPrimitiveObject("Boolean", toValue_bool(value.bool())) +} + +func booleanToString(value bool) string { + return strconv.FormatBool(value) +} diff --git a/vendor/github.com/robertkrimen/otto/type_date.go b/vendor/github.com/robertkrimen/otto/type_date.go new file mode 100644 index 00000000..7079e649 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_date.go @@ -0,0 +1,299 @@ +package otto + +import ( + "fmt" + "math" + "regexp" + Time "time" +) + +type _dateObject struct { + time Time.Time // Time from the "time" package, a cached version of time + epoch int64 + value Value + isNaN bool +} + +var ( + invalidDateObject = _dateObject{ + time: Time.Time{}, + epoch: -1, + value: NaNValue(), + isNaN: true, + } +) + +type _ecmaTime struct { + year int + month int + day int + hour int + minute int + second int + millisecond int + location *Time.Location // Basically, either local or UTC +} + +func ecmaTime(goTime Time.Time) _ecmaTime { + return _ecmaTime{ + goTime.Year(), + dateFromGoMonth(goTime.Month()), + goTime.Day(), + goTime.Hour(), + goTime.Minute(), + goTime.Second(), + goTime.Nanosecond() / (100 * 100 * 100), + goTime.Location(), + } +} + +func (self *_ecmaTime) goTime() Time.Time { + return Time.Date( + self.year, + dateToGoMonth(self.month), + self.day, + self.hour, + self.minute, + self.second, + self.millisecond*(100*100*100), + self.location, + ) +} + +func (self *_dateObject) Time() Time.Time { + return self.time +} + +func (self *_dateObject) Epoch() int64 { + return self.epoch +} + +func (self *_dateObject) Value() Value { + return self.value +} + +// FIXME A date should only be in the range of -100,000,000 to +100,000,000 (1970): 15.9.1.1 +func (self *_dateObject) SetNaN() { + self.time = Time.Time{} + self.epoch = -1 + self.value = NaNValue() + self.isNaN = true +} + +func (self *_dateObject) SetTime(time Time.Time) { + self.Set(timeToEpoch(time)) +} + +func epoch2dateObject(epoch float64) _dateObject { + date := _dateObject{} + date.Set(epoch) + return date +} + +func (self *_dateObject) Set(epoch float64) { + // epoch + self.epoch = epochToInteger(epoch) + + // time + time, err := epochToTime(epoch) + self.time = time // Is either a valid time, or the zero-value for time.Time + + // value & isNaN + if err != nil { + self.isNaN = true + self.epoch = -1 + self.value = NaNValue() + } else { + self.value = toValue_int64(self.epoch) + } +} + +func epochToInteger(value float64) int64 { + if value > 0 { + return int64(math.Floor(value)) + } + return int64(math.Ceil(value)) +} + +func epochToTime(value float64) (time Time.Time, err error) { + epochWithMilli := value + if math.IsNaN(epochWithMilli) || math.IsInf(epochWithMilli, 0) { + err = fmt.Errorf("Invalid time %v", value) + return + } + + epoch := int64(epochWithMilli / 1000) + milli := int64(epochWithMilli) % 1000 + + time = Time.Unix(int64(epoch), milli*1000000).UTC() + return +} + +func timeToEpoch(time Time.Time) float64 { + return float64(time.UnixNano() / (1000 * 1000)) +} + +func (runtime *_runtime) newDateObject(epoch float64) *_object { + self := runtime.newObject() + self.class = "Date" + + // FIXME This is ugly... + date := _dateObject{} + date.Set(epoch) + self.value = date + return self +} + +func (self *_object) dateValue() _dateObject { + value, _ := self.value.(_dateObject) + return value +} + +func dateObjectOf(rt *_runtime, _dateObject *_object) _dateObject { + if _dateObject == nil || _dateObject.class != "Date" { + panic(rt.panicTypeError()) + } + return _dateObject.dateValue() +} + +// JavaScript is 0-based, Go is 1-based (15.9.1.4) +func dateToGoMonth(month int) Time.Month { + return Time.Month(month + 1) +} + +func dateFromGoMonth(month Time.Month) int { + return int(month) - 1 +} + +// Both JavaScript & Go are 0-based (Sunday == 0) +func dateToGoDay(day int) Time.Weekday { + return Time.Weekday(day) +} + +func dateFromGoDay(day Time.Weekday) int { + return int(day) +} + +func newDateTime(argumentList []Value, location *Time.Location) (epoch float64) { + + pick := func(index int, default_ float64) (float64, bool) { + if index >= len(argumentList) { + return default_, false + } + value := argumentList[index].float64() + if math.IsNaN(value) || math.IsInf(value, 0) { + return 0, true + } + return value, false + } + + if len(argumentList) >= 2 { // 2-argument, 3-argument, ... + var year, month, day, hour, minute, second, millisecond float64 + var invalid bool + if year, invalid = pick(0, 1900.0); invalid { + goto INVALID + } + if month, invalid = pick(1, 0.0); invalid { + goto INVALID + } + if day, invalid = pick(2, 1.0); invalid { + goto INVALID + } + if hour, invalid = pick(3, 0.0); invalid { + goto INVALID + } + if minute, invalid = pick(4, 0.0); invalid { + goto INVALID + } + if second, invalid = pick(5, 0.0); invalid { + goto INVALID + } + if millisecond, invalid = pick(6, 0.0); invalid { + goto INVALID + } + + if year >= 0 && year <= 99 { + year += 1900 + } + + time := Time.Date(int(year), dateToGoMonth(int(month)), int(day), int(hour), int(minute), int(second), int(millisecond)*1000*1000, location) + return timeToEpoch(time) + + } else if len(argumentList) == 0 { // 0-argument + time := Time.Now().UTC() + return timeToEpoch(time) + } else { // 1-argument + value := valueOfArrayIndex(argumentList, 0) + value = toPrimitive(value) + if value.IsString() { + return dateParse(value.string()) + } + + return value.float64() + } + +INVALID: + epoch = math.NaN() + return +} + +var ( + dateLayoutList = []string{ + "2006", + "2006-01", + "2006-01-02", + + "2006T15:04", + "2006-01T15:04", + "2006-01-02T15:04", + + "2006T15:04:05", + "2006-01T15:04:05", + "2006-01-02T15:04:05", + + "2006T15:04:05.000", + "2006-01T15:04:05.000", + "2006-01-02T15:04:05.000", + + "2006T15:04-0700", + "2006-01T15:04-0700", + "2006-01-02T15:04-0700", + + "2006T15:04:05-0700", + "2006-01T15:04:05-0700", + "2006-01-02T15:04:05-0700", + + "2006T15:04:05.000-0700", + "2006-01T15:04:05.000-0700", + "2006-01-02T15:04:05.000-0700", + + Time.RFC1123, + } + matchDateTimeZone = regexp.MustCompile(`^(.*)(?:(Z)|([\+\-]\d{2}):(\d{2}))$`) +) + +func dateParse(date string) (epoch float64) { + // YYYY-MM-DDTHH:mm:ss.sssZ + var time Time.Time + var err error + { + date := date + if match := matchDateTimeZone.FindStringSubmatch(date); match != nil { + if match[2] == "Z" { + date = match[1] + "+0000" + } else { + date = match[1] + match[3] + match[4] + } + } + for _, layout := range dateLayoutList { + time, err = Time.Parse(layout, date) + if err == nil { + break + } + } + } + if err != nil { + return math.NaN() + } + return float64(time.UnixNano()) / (1000 * 1000) // UnixMilli() +} diff --git a/vendor/github.com/robertkrimen/otto/type_error.go b/vendor/github.com/robertkrimen/otto/type_error.go new file mode 100644 index 00000000..84e5d79b --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_error.go @@ -0,0 +1,24 @@ +package otto + +func (rt *_runtime) newErrorObject(name string, message Value, stackFramesToPop int) *_object { + self := rt.newClassObject("Error") + if message.IsDefined() { + msg := message.string() + self.defineProperty("message", toValue_string(msg), 0111, false) + self.value = newError(rt, name, stackFramesToPop, msg) + } else { + self.value = newError(rt, name, stackFramesToPop) + } + + self.defineOwnProperty("stack", _property{ + value: _propertyGetSet{ + rt.newNativeFunction("get", "internal", 0, func(FunctionCall) Value { + return toValue_string(self.value.(_error).formatWithStack()) + }), + &_nilGetSetObject, + }, + mode: modeConfigureMask & modeOnMask, + }, false) + + return self +} diff --git a/vendor/github.com/robertkrimen/otto/type_function.go b/vendor/github.com/robertkrimen/otto/type_function.go new file mode 100644 index 00000000..ad4b1f16 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_function.go @@ -0,0 +1,340 @@ +package otto + +// _constructFunction +type _constructFunction func(*_object, []Value) Value + +// 13.2.2 [[Construct]] +func defaultConstruct(fn *_object, argumentList []Value) Value { + object := fn.runtime.newObject() + object.class = "Object" + + prototype := fn.get("prototype") + if prototype.kind != valueObject { + prototype = toValue_object(fn.runtime.global.ObjectPrototype) + } + object.prototype = prototype._object() + + this := toValue_object(object) + value := fn.call(this, argumentList, false, nativeFrame) + if value.kind == valueObject { + return value + } + return this +} + +// _nativeFunction +type _nativeFunction func(FunctionCall) Value + +// ===================== // +// _nativeFunctionObject // +// ===================== // + +type _nativeFunctionObject struct { + name string + file string + line int + call _nativeFunction // [[Call]] + construct _constructFunction // [[Construct]] +} + +func (runtime *_runtime) _newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { + self := runtime.newClassObject("Function") + self.value = _nativeFunctionObject{ + name: name, + file: file, + line: line, + call: native, + construct: defaultConstruct, + } + self.defineProperty("name", toValue_string(name), 0000, false) + self.defineProperty("length", toValue_int(length), 0000, false) + return self +} + +func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { + self := runtime._newNativeFunctionObject(name, file, line, native, length) + self.defineOwnProperty("caller", _property{ + value: _propertyGetSet{ + runtime._newNativeFunctionObject("get", "internal", 0, func(fc FunctionCall) Value { + for sc := runtime.scope; sc != nil; sc = sc.outer { + if sc.frame.fn == self { + if sc.outer == nil || sc.outer.frame.fn == nil { + return nullValue + } + + return runtime.toValue(sc.outer.frame.fn) + } + } + + return nullValue + }, 0), + &_nilGetSetObject, + }, + mode: 0000, + }, false) + return self +} + +// =================== // +// _bindFunctionObject // +// =================== // + +type _bindFunctionObject struct { + target *_object + this Value + argumentList []Value +} + +func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object { + self := runtime.newClassObject("Function") + self.value = _bindFunctionObject{ + target: target, + this: this, + argumentList: argumentList, + } + length := int(toInt32(target.get("length"))) + length -= len(argumentList) + if length < 0 { + length = 0 + } + self.defineProperty("name", toValue_string("bound "+target.get("name").String()), 0000, false) + self.defineProperty("length", toValue_int(length), 0000, false) + self.defineProperty("caller", Value{}, 0000, false) // TODO Should throw a TypeError + self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError + return self +} + +// [[Construct]] +func (fn _bindFunctionObject) construct(argumentList []Value) Value { + object := fn.target + switch value := object.value.(type) { + case _nativeFunctionObject: + return value.construct(object, fn.argumentList) + case _nodeFunctionObject: + argumentList = append(fn.argumentList, argumentList...) + return object.construct(argumentList) + } + panic(fn.target.runtime.panicTypeError()) +} + +// =================== // +// _nodeFunctionObject // +// =================== // + +type _nodeFunctionObject struct { + node *_nodeFunctionLiteral + stash _stash +} + +func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash _stash) *_object { + self := runtime.newClassObject("Function") + self.value = _nodeFunctionObject{ + node: node, + stash: stash, + } + self.defineProperty("name", toValue_string(node.name), 0000, false) + self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false) + self.defineOwnProperty("caller", _property{ + value: _propertyGetSet{ + runtime.newNativeFunction("get", "internal", 0, func(fc FunctionCall) Value { + for sc := runtime.scope; sc != nil; sc = sc.outer { + if sc.frame.fn == self { + if sc.outer == nil || sc.outer.frame.fn == nil { + return nullValue + } + + return runtime.toValue(sc.outer.frame.fn) + } + } + + return nullValue + }), + &_nilGetSetObject, + }, + mode: 0000, + }, false) + return self +} + +// ======= // +// _object // +// ======= // + +func (self *_object) isCall() bool { + switch fn := self.value.(type) { + case _nativeFunctionObject: + return fn.call != nil + case _bindFunctionObject: + return true + case _nodeFunctionObject: + return true + } + return false +} + +func (self *_object) call(this Value, argumentList []Value, eval bool, frame _frame) Value { + switch fn := self.value.(type) { + + case _nativeFunctionObject: + // Since eval is a native function, we only have to check for it here + if eval { + eval = self == self.runtime.eval // If eval is true, then it IS a direct eval + } + + // Enter a scope, name from the native object... + rt := self.runtime + if rt.scope != nil && !eval { + rt.enterFunctionScope(rt.scope.lexical, this) + rt.scope.frame = _frame{ + native: true, + nativeFile: fn.file, + nativeLine: fn.line, + callee: fn.name, + file: nil, + fn: self, + } + defer func() { + rt.leaveScope() + }() + } + + return fn.call(FunctionCall{ + runtime: self.runtime, + eval: eval, + + This: this, + ArgumentList: argumentList, + Otto: self.runtime.otto, + }) + + case _bindFunctionObject: + // TODO Passthrough site, do not enter a scope + argumentList = append(fn.argumentList, argumentList...) + return fn.target.call(fn.this, argumentList, false, frame) + + case _nodeFunctionObject: + rt := self.runtime + stash := rt.enterFunctionScope(fn.stash, this) + rt.scope.frame = _frame{ + callee: fn.node.name, + file: fn.node.file, + fn: self, + } + defer func() { + rt.leaveScope() + }() + callValue := rt.cmpl_call_nodeFunction(self, stash, fn.node, this, argumentList) + if value, valid := callValue.value.(_result); valid { + return value.value + } + return callValue + } + + panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) +} + +func (self *_object) construct(argumentList []Value) Value { + switch fn := self.value.(type) { + + case _nativeFunctionObject: + if fn.call == nil { + panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) + } + if fn.construct == nil { + panic(self.runtime.panicTypeError("%v is not a constructor", toValue_object(self))) + } + return fn.construct(self, argumentList) + + case _bindFunctionObject: + return fn.construct(argumentList) + + case _nodeFunctionObject: + return defaultConstruct(self, argumentList) + } + + panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) +} + +// 15.3.5.3 +func (self *_object) hasInstance(of Value) bool { + if !self.isCall() { + // We should not have a hasInstance method + panic(self.runtime.panicTypeError()) + } + if !of.IsObject() { + return false + } + prototype := self.get("prototype") + if !prototype.IsObject() { + panic(self.runtime.panicTypeError()) + } + prototypeObject := prototype._object() + + value := of._object().prototype + for value != nil { + if value == prototypeObject { + return true + } + value = value.prototype + } + return false +} + +// ============ // +// FunctionCall // +// ============ // + +// FunctionCall is an encapsulation of a JavaScript function call. +type FunctionCall struct { + runtime *_runtime + _thisObject *_object + eval bool // This call is a direct call to eval + + This Value + ArgumentList []Value + Otto *Otto +} + +// Argument will return the value of the argument at the given index. +// +// If no such argument exists, undefined is returned. +func (self FunctionCall) Argument(index int) Value { + return valueOfArrayIndex(self.ArgumentList, index) +} + +func (self FunctionCall) getArgument(index int) (Value, bool) { + return getValueOfArrayIndex(self.ArgumentList, index) +} + +func (self FunctionCall) slice(index int) []Value { + if index < len(self.ArgumentList) { + return self.ArgumentList[index:] + } + return []Value{} +} + +func (self *FunctionCall) thisObject() *_object { + if self._thisObject == nil { + this := self.This.resolve() // FIXME Is this right? + self._thisObject = self.runtime.toObject(this) + } + return self._thisObject +} + +func (self *FunctionCall) thisClassObject(class string) *_object { + thisObject := self.thisObject() + if thisObject.class != class { + panic(self.runtime.panicTypeError()) + } + return self._thisObject +} + +func (self FunctionCall) toObject(value Value) *_object { + return self.runtime.toObject(value) +} + +// CallerLocation will return file location information (file:line:pos) where this function is being called. +func (self FunctionCall) CallerLocation() string { + // see error.go for location() + return self.runtime.scope.outer.frame.location() +} diff --git a/vendor/github.com/robertkrimen/otto/type_go_array.go b/vendor/github.com/robertkrimen/otto/type_go_array.go new file mode 100644 index 00000000..13a0b10f --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_go_array.go @@ -0,0 +1,134 @@ +package otto + +import ( + "reflect" + "strconv" +) + +func (runtime *_runtime) newGoArrayObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "GoArray" + self.objectClass = _classGoArray + self.value = _newGoArrayObject(value) + return self +} + +type _goArrayObject struct { + value reflect.Value + writable bool + propertyMode _propertyMode +} + +func _newGoArrayObject(value reflect.Value) *_goArrayObject { + writable := value.Kind() == reflect.Ptr // The Array is addressable (like a Slice) + mode := _propertyMode(0010) + if writable { + mode = 0110 + } + self := &_goArrayObject{ + value: value, + writable: writable, + propertyMode: mode, + } + return self +} + +func (self _goArrayObject) getValue(index int64) (reflect.Value, bool) { + value := reflect.Indirect(self.value) + if index < int64(value.Len()) { + return value.Index(int(index)), true + } + return reflect.Value{}, false +} + +func (self _goArrayObject) setValue(index int64, value Value) bool { + indexValue, exists := self.getValue(index) + if !exists { + return false + } + reflectValue, err := value.toReflectValue(reflect.Indirect(self.value).Type().Elem().Kind()) + if err != nil { + panic(err) + } + indexValue.Set(reflectValue) + return true +} + +func goArrayGetOwnProperty(self *_object, name string) *_property { + // length + if name == "length" { + return &_property{ + value: toValue(reflect.Indirect(self.value.(*_goArrayObject).value).Len()), + mode: 0, + } + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goArrayObject) + value := Value{} + reflectValue, exists := object.getValue(index) + if exists { + value = self.runtime.toValue(reflectValue.Interface()) + } + return &_property{ + value: value, + mode: object.propertyMode, + } + } + + return objectGetOwnProperty(self, name) +} + +func goArrayEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goArrayObject) + // .0, .1, .2, ... + + for index, length := 0, object.value.Len(); index < length; index++ { + name := strconv.FormatInt(int64(index), 10) + if !each(name) { + return + } + } + + objectEnumerate(self, all, each) +} + +func goArrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + if name == "length" { + return self.runtime.typeErrorResult(throw) + } else if index := stringToArrayIndex(name); index >= 0 { + object := self.value.(*_goArrayObject) + if object.writable { + if self.value.(*_goArrayObject).setValue(index, descriptor.value.(Value)) { + return true + } + } + return self.runtime.typeErrorResult(throw) + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func goArrayDelete(self *_object, name string, throw bool) bool { + // length + if name == "length" { + return self.runtime.typeErrorResult(throw) + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goArrayObject) + if object.writable { + indexValue, exists := object.getValue(index) + if exists { + indexValue.Set(reflect.Zero(reflect.Indirect(object.value).Type().Elem())) + return true + } + } + return self.runtime.typeErrorResult(throw) + } + + return self.delete(name, throw) +} diff --git a/vendor/github.com/robertkrimen/otto/type_go_map.go b/vendor/github.com/robertkrimen/otto/type_go_map.go new file mode 100644 index 00000000..41b9ac0d --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_go_map.go @@ -0,0 +1,95 @@ +package otto + +import ( + "reflect" +) + +func (runtime *_runtime) newGoMapObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "Object" // TODO Should this be something else? + self.objectClass = _classGoMap + self.value = _newGoMapObject(value) + return self +} + +type _goMapObject struct { + value reflect.Value + keyKind reflect.Kind + valueKind reflect.Kind +} + +func _newGoMapObject(value reflect.Value) *_goMapObject { + if value.Kind() != reflect.Map { + dbgf("%/panic//%@: %v != reflect.Map", value.Kind()) + } + self := &_goMapObject{ + value: value, + keyKind: value.Type().Key().Kind(), + valueKind: value.Type().Elem().Kind(), + } + return self +} + +func (self _goMapObject) toKey(name string) reflect.Value { + reflectValue, err := stringToReflectValue(name, self.keyKind) + if err != nil { + panic(err) + } + return reflectValue +} + +func (self _goMapObject) toValue(value Value) reflect.Value { + reflectValue, err := value.toReflectValue(self.valueKind) + if err != nil { + panic(err) + } + return reflectValue +} + +func goMapGetOwnProperty(self *_object, name string) *_property { + object := self.value.(*_goMapObject) + value := object.value.MapIndex(object.toKey(name)) + if value.IsValid() { + return &_property{self.runtime.toValue(value.Interface()), 0111} + } + + // Other methods + if method := self.value.(*_goMapObject).value.MethodByName(name); (method != reflect.Value{}) { + return &_property{ + value: self.runtime.toValue(method.Interface()), + mode: 0110, + } + } + + return nil +} + +func goMapEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goMapObject) + keys := object.value.MapKeys() + for _, key := range keys { + if !each(toValue(key).String()) { + return + } + } +} + +func goMapDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + object := self.value.(*_goMapObject) + // TODO ...or 0222 + if descriptor.mode != 0111 { + return self.runtime.typeErrorResult(throw) + } + if !descriptor.isDataDescriptor() { + return self.runtime.typeErrorResult(throw) + } + object.value.SetMapIndex(object.toKey(name), object.toValue(descriptor.value.(Value))) + return true +} + +func goMapDelete(self *_object, name string, throw bool) bool { + object := self.value.(*_goMapObject) + object.value.SetMapIndex(object.toKey(name), reflect.Value{}) + // FIXME + return true +} diff --git a/vendor/github.com/robertkrimen/otto/type_go_map_test.go b/vendor/github.com/robertkrimen/otto/type_go_map_test.go new file mode 100644 index 00000000..a89892b6 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_go_map_test.go @@ -0,0 +1,40 @@ +package otto + +import ( + "fmt" + "sort" + "testing" +) + +type GoMapTest map[string]int + +func (s GoMapTest) Join() string { + joinedStr := "" + + // Ordering the map takes some effort + // because map iterators in golang are unordered by definition. + // So we need to extract keys, sort them, and then generate K/V pairs + // All of this is meant to ensure that the test is predictable. + keys := make([]string, len(s)) + i := 0 + for key, _ := range s { + keys[i] = key + i++ + } + + sort.Strings(keys) + + for _, key := range keys { + joinedStr += key + ": " + fmt.Sprintf("%d", s[key]) + " " + } + return joinedStr +} + +func TestGoMap(t *testing.T) { + tt(t, func() { + test, vm := test() + vm.Set("TestMap", GoMapTest{"one": 1, "two": 2, "three": 3}) + is(test(`TestMap["one"]`).export(), 1) + is(test(`TestMap.Join()`).export(), "one: 1 three: 3 two: 2 ") + }) +} diff --git a/vendor/github.com/robertkrimen/otto/type_go_slice.go b/vendor/github.com/robertkrimen/otto/type_go_slice.go new file mode 100644 index 00000000..78c69e68 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_go_slice.go @@ -0,0 +1,126 @@ +package otto + +import ( + "reflect" + "strconv" +) + +func (runtime *_runtime) newGoSliceObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "GoArray" // TODO GoSlice? + self.objectClass = _classGoSlice + self.value = _newGoSliceObject(value) + return self +} + +type _goSliceObject struct { + value reflect.Value +} + +func _newGoSliceObject(value reflect.Value) *_goSliceObject { + self := &_goSliceObject{ + value: value, + } + return self +} + +func (self _goSliceObject) getValue(index int64) (reflect.Value, bool) { + if index < int64(self.value.Len()) { + return self.value.Index(int(index)), true + } + return reflect.Value{}, false +} + +func (self _goSliceObject) setValue(index int64, value Value) bool { + indexValue, exists := self.getValue(index) + if !exists { + return false + } + reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind()) + if err != nil { + panic(err) + } + indexValue.Set(reflectValue) + return true +} + +func goSliceGetOwnProperty(self *_object, name string) *_property { + // length + if name == "length" { + return &_property{ + value: toValue(self.value.(*_goSliceObject).value.Len()), + mode: 0, + } + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + value := Value{} + reflectValue, exists := self.value.(*_goSliceObject).getValue(index) + if exists { + value = self.runtime.toValue(reflectValue.Interface()) + } + return &_property{ + value: value, + mode: 0110, + } + } + + // Other methods + if method := self.value.(*_goSliceObject).value.MethodByName(name); (method != reflect.Value{}) { + return &_property{ + value: self.runtime.toValue(method.Interface()), + mode: 0110, + } + } + + return objectGetOwnProperty(self, name) +} + +func goSliceEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goSliceObject) + // .0, .1, .2, ... + + for index, length := 0, object.value.Len(); index < length; index++ { + name := strconv.FormatInt(int64(index), 10) + if !each(name) { + return + } + } + + objectEnumerate(self, all, each) +} + +func goSliceDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + if name == "length" { + return self.runtime.typeErrorResult(throw) + } else if index := stringToArrayIndex(name); index >= 0 { + if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) { + return true + } + return self.runtime.typeErrorResult(throw) + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func goSliceDelete(self *_object, name string, throw bool) bool { + // length + if name == "length" { + return self.runtime.typeErrorResult(throw) + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goSliceObject) + indexValue, exists := object.getValue(index) + if exists { + indexValue.Set(reflect.Zero(object.value.Type().Elem())) + return true + } + return self.runtime.typeErrorResult(throw) + } + + return self.delete(name, throw) +} diff --git a/vendor/github.com/robertkrimen/otto/type_go_slice_test.go b/vendor/github.com/robertkrimen/otto/type_go_slice_test.go new file mode 100644 index 00000000..b604aae8 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_go_slice_test.go @@ -0,0 +1,23 @@ +package otto + +import "testing" + +type GoSliceTest []int + +func (s GoSliceTest) Sum() int { + sum := 0 + for _, v := range s { + sum += v + } + return sum +} + +func TestGoSlice(t *testing.T) { + tt(t, func() { + test, vm := test() + vm.Set("TestSlice", GoSliceTest{1, 2, 3}) + is(test(`TestSlice.length`).export(), 3) + is(test(`TestSlice[1]`).export(), 2) + is(test(`TestSlice.Sum()`).export(), 6) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/type_go_struct.go b/vendor/github.com/robertkrimen/otto/type_go_struct.go new file mode 100644 index 00000000..608ac666 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_go_struct.go @@ -0,0 +1,146 @@ +package otto + +import ( + "encoding/json" + "reflect" +) + +// FIXME Make a note about not being able to modify a struct unless it was +// passed as a pointer-to: &struct{ ... } +// This seems to be a limitation of the reflect package. +// This goes for the other Go constructs too. +// I guess we could get around it by either: +// 1. Creating a new struct every time +// 2. Creating an addressable? struct in the constructor + +func (runtime *_runtime) newGoStructObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "Object" // TODO Should this be something else? + self.objectClass = _classGoStruct + self.value = _newGoStructObject(value) + return self +} + +type _goStructObject struct { + value reflect.Value +} + +func _newGoStructObject(value reflect.Value) *_goStructObject { + if reflect.Indirect(value).Kind() != reflect.Struct { + dbgf("%/panic//%@: %v != reflect.Struct", value.Kind()) + } + self := &_goStructObject{ + value: value, + } + return self +} + +func (self _goStructObject) getValue(name string) reflect.Value { + if validGoStructName(name) { + // Do not reveal hidden or unexported fields + if field := reflect.Indirect(self.value).FieldByName(name); (field != reflect.Value{}) { + return field + } + + if method := self.value.MethodByName(name); (method != reflect.Value{}) { + return method + } + } + + return reflect.Value{} +} + +func (self _goStructObject) field(name string) (reflect.StructField, bool) { + return reflect.Indirect(self.value).Type().FieldByName(name) +} + +func (self _goStructObject) method(name string) (reflect.Method, bool) { + return reflect.Indirect(self.value).Type().MethodByName(name) +} + +func (self _goStructObject) setValue(name string, value Value) bool { + field, exists := self.field(name) + if !exists { + return false + } + fieldValue := self.getValue(name) + reflectValue, err := value.toReflectValue(field.Type.Kind()) + if err != nil { + panic(err) + } + fieldValue.Set(reflectValue) + + return true +} + +func goStructGetOwnProperty(self *_object, name string) *_property { + object := self.value.(*_goStructObject) + value := object.getValue(name) + if value.IsValid() { + return &_property{self.runtime.toValue(value.Interface()), 0110} + } + + return objectGetOwnProperty(self, name) +} + +func validGoStructName(name string) bool { + if name == "" { + return false + } + return 'A' <= name[0] && name[0] <= 'Z' // TODO What about Unicode? +} + +func goStructEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goStructObject) + + // Enumerate fields + for index := 0; index < reflect.Indirect(object.value).NumField(); index++ { + name := reflect.Indirect(object.value).Type().Field(index).Name + if validGoStructName(name) { + if !each(name) { + return + } + } + } + + // Enumerate methods + for index := 0; index < object.value.NumMethod(); index++ { + name := object.value.Type().Method(index).Name + if validGoStructName(name) { + if !each(name) { + return + } + } + } + + objectEnumerate(self, all, each) +} + +func goStructCanPut(self *_object, name string) bool { + object := self.value.(*_goStructObject) + value := object.getValue(name) + if value.IsValid() { + return true + } + + return objectCanPut(self, name) +} + +func goStructPut(self *_object, name string, value Value, throw bool) { + object := self.value.(*_goStructObject) + if object.setValue(name, value) { + return + } + + objectPut(self, name, value, throw) +} + +func goStructMarshalJSON(self *_object) json.Marshaler { + object := self.value.(*_goStructObject) + goValue := reflect.Indirect(object.value).Interface() + switch marshaler := goValue.(type) { + case json.Marshaler: + return marshaler + } + return nil +} diff --git a/vendor/github.com/robertkrimen/otto/type_number.go b/vendor/github.com/robertkrimen/otto/type_number.go new file mode 100644 index 00000000..28de4444 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_number.go @@ -0,0 +1,5 @@ +package otto + +func (runtime *_runtime) newNumberObject(value Value) *_object { + return runtime.newPrimitiveObject("Number", value.numberValue()) +} diff --git a/vendor/github.com/robertkrimen/otto/type_reference.go b/vendor/github.com/robertkrimen/otto/type_reference.go new file mode 100644 index 00000000..fd770c6f --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_reference.go @@ -0,0 +1,103 @@ +package otto + +type _reference interface { + invalid() bool // IsUnresolvableReference + getValue() Value // getValue + putValue(Value) string // PutValue + delete() bool +} + +// PropertyReference + +type _propertyReference struct { + name string + strict bool + base *_object + runtime *_runtime + at _at +} + +func newPropertyReference(rt *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference { + return &_propertyReference{ + runtime: rt, + name: name, + strict: strict, + base: base, + at: at, + } +} + +func (self *_propertyReference) invalid() bool { + return self.base == nil +} + +func (self *_propertyReference) getValue() Value { + if self.base == nil { + panic(self.runtime.panicReferenceError("'%s' is not defined", self.name, self.at)) + } + return self.base.get(self.name) +} + +func (self *_propertyReference) putValue(value Value) string { + if self.base == nil { + return self.name + } + self.base.put(self.name, value, self.strict) + return "" +} + +func (self *_propertyReference) delete() bool { + if self.base == nil { + // TODO Throw an error if strict + return true + } + return self.base.delete(self.name, self.strict) +} + +// ArgumentReference + +func newArgumentReference(runtime *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference { + if base == nil { + panic(hereBeDragons()) + } + return newPropertyReference(runtime, base, name, strict, at) +} + +type _stashReference struct { + name string + strict bool + base _stash +} + +func (self *_stashReference) invalid() bool { + return false // The base (an environment) will never be nil +} + +func (self *_stashReference) getValue() Value { + return self.base.getBinding(self.name, self.strict) +} + +func (self *_stashReference) putValue(value Value) string { + self.base.setValue(self.name, value, self.strict) + return "" +} + +func (self *_stashReference) delete() bool { + if self.base == nil { + // This should never be reached, but just in case + return false + } + return self.base.deleteBinding(self.name) +} + +// getIdentifierReference + +func getIdentifierReference(runtime *_runtime, stash _stash, name string, strict bool, at _at) _reference { + if stash == nil { + return newPropertyReference(runtime, nil, name, strict, at) + } + if stash.hasBinding(name) { + return stash.newReference(name, strict, at) + } + return getIdentifierReference(runtime, stash.outer(), name, strict, at) +} diff --git a/vendor/github.com/robertkrimen/otto/type_regexp.go b/vendor/github.com/robertkrimen/otto/type_regexp.go new file mode 100644 index 00000000..57fe3164 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_regexp.go @@ -0,0 +1,146 @@ +package otto + +import ( + "fmt" + "regexp" + "unicode/utf8" + + "github.com/robertkrimen/otto/parser" +) + +type _regExpObject struct { + regularExpression *regexp.Regexp + global bool + ignoreCase bool + multiline bool + source string + flags string +} + +func (runtime *_runtime) newRegExpObject(pattern string, flags string) *_object { + self := runtime.newObject() + self.class = "RegExp" + + global := false + ignoreCase := false + multiline := false + re2flags := "" + + // TODO Maybe clean up the panicking here... TypeError, SyntaxError, ? + + for _, chr := range flags { + switch chr { + case 'g': + if global { + panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) + } + global = true + case 'm': + if multiline { + panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) + } + multiline = true + re2flags += "m" + case 'i': + if ignoreCase { + panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) + } + ignoreCase = true + re2flags += "i" + } + } + + re2pattern, err := parser.TransformRegExp(pattern) + if err != nil { + panic(runtime.panicTypeError("Invalid regular expression: %s", err.Error())) + } + if len(re2flags) > 0 { + re2pattern = fmt.Sprintf("(?%s:%s)", re2flags, re2pattern) + } + + regularExpression, err := regexp.Compile(re2pattern) + if err != nil { + panic(runtime.panicSyntaxError("Invalid regular expression: %s", err.Error()[22:])) + } + + self.value = _regExpObject{ + regularExpression: regularExpression, + global: global, + ignoreCase: ignoreCase, + multiline: multiline, + source: pattern, + flags: flags, + } + self.defineProperty("global", toValue_bool(global), 0, false) + self.defineProperty("ignoreCase", toValue_bool(ignoreCase), 0, false) + self.defineProperty("multiline", toValue_bool(multiline), 0, false) + self.defineProperty("lastIndex", toValue_int(0), 0100, false) + self.defineProperty("source", toValue_string(pattern), 0, false) + return self +} + +func (self *_object) regExpValue() _regExpObject { + value, _ := self.value.(_regExpObject) + return value +} + +func execRegExp(this *_object, target string) (match bool, result []int) { + if this.class != "RegExp" { + panic(this.runtime.panicTypeError("Calling RegExp.exec on a non-RegExp object")) + } + lastIndex := this.get("lastIndex").number().int64 + index := lastIndex + global := this.get("global").bool() + if !global { + index = 0 + } + if 0 > index || index > int64(len(target)) { + } else { + result = this.regExpValue().regularExpression.FindStringSubmatchIndex(target[index:]) + } + if result == nil { + //this.defineProperty("lastIndex", toValue_(0), 0111, true) + this.put("lastIndex", toValue_int(0), true) + return // !match + } + match = true + startIndex := index + endIndex := int(lastIndex) + result[1] + // We do this shift here because the .FindStringSubmatchIndex above + // was done on a local subordinate slice of the string, not the whole string + for index, _ := range result { + result[index] += int(startIndex) + } + if global { + //this.defineProperty("lastIndex", toValue_(endIndex), 0111, true) + this.put("lastIndex", toValue_int(endIndex), true) + } + return // match +} + +func execResultToArray(runtime *_runtime, target string, result []int) *_object { + captureCount := len(result) / 2 + valueArray := make([]Value, captureCount) + for index := 0; index < captureCount; index++ { + offset := 2 * index + if result[offset] != -1 { + valueArray[index] = toValue_string(target[result[offset]:result[offset+1]]) + } else { + valueArray[index] = Value{} + } + } + matchIndex := result[0] + if matchIndex != 0 { + matchIndex = 0 + // Find the rune index in the string, not the byte index + for index := 0; index < result[0]; { + _, size := utf8.DecodeRuneInString(target[index:]) + matchIndex += 1 + index += size + } + } + match := runtime.newArrayOf(valueArray) + match.defineProperty("input", toValue_string(target), 0111, false) + match.defineProperty("index", toValue_int(matchIndex), 0111, false) + return match +} diff --git a/vendor/github.com/robertkrimen/otto/type_string.go b/vendor/github.com/robertkrimen/otto/type_string.go new file mode 100644 index 00000000..ef3afa42 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/type_string.go @@ -0,0 +1,112 @@ +package otto + +import ( + "strconv" + "unicode/utf8" +) + +type _stringObject interface { + Length() int + At(int) rune + String() string +} + +type _stringASCII string + +func (str _stringASCII) Length() int { + return len(str) +} + +func (str _stringASCII) At(at int) rune { + return rune(str[at]) +} + +func (str _stringASCII) String() string { + return string(str) +} + +type _stringWide struct { + string string + length int + runes []rune +} + +func (str _stringWide) Length() int { + return str.length +} + +func (str _stringWide) At(at int) rune { + if str.runes == nil { + str.runes = []rune(str.string) + } + return str.runes[at] +} + +func (str _stringWide) String() string { + return str.string +} + +func _newStringObject(str string) _stringObject { + for i := 0; i < len(str); i++ { + if str[i] >= utf8.RuneSelf { + goto wide + } + } + + return _stringASCII(str) + +wide: + return &_stringWide{ + string: str, + length: utf8.RuneCountInString(str), + } +} + +func stringAt(str _stringObject, index int) rune { + if 0 <= index && index < str.Length() { + return str.At(index) + } + return utf8.RuneError +} + +func (runtime *_runtime) newStringObject(value Value) *_object { + str := _newStringObject(value.string()) + + self := runtime.newClassObject("String") + self.defineProperty("length", toValue_int(str.Length()), 0, false) + self.objectClass = _classString + self.value = str + return self +} + +func (self *_object) stringValue() _stringObject { + if str, ok := self.value.(_stringObject); ok { + return str + } + return nil +} + +func stringEnumerate(self *_object, all bool, each func(string) bool) { + if str := self.stringValue(); str != nil { + length := str.Length() + for index := 0; index < length; index++ { + if !each(strconv.FormatInt(int64(index), 10)) { + return + } + } + } + objectEnumerate(self, all, each) +} + +func stringGetOwnProperty(self *_object, name string) *_property { + if property := objectGetOwnProperty(self, name); property != nil { + return property + } + // TODO Test a string of length >= +int32 + 1? + if index := stringToArrayIndex(name); index >= 0 { + if chr := stringAt(self.stringValue(), int(index)); chr != utf8.RuneError { + return &_property{toValue_string(string(chr)), 0} + } + } + return nil +} diff --git a/vendor/github.com/robertkrimen/otto/underscore/Makefile b/vendor/github.com/robertkrimen/otto/underscore/Makefile new file mode 100644 index 00000000..fc872917 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore/Makefile @@ -0,0 +1,11 @@ +.PHONY: source + +source: source.go + +underscore.js: + curl -kL http://underscorejs.org/underscore.js > $@ + +source.go: underscore.js + go-bindata -f underscore -p underscore -u true < $< 2>/dev/null | grep -v '^//' | gofmt > $@ + head -4 $< >> $@ + mv $< .. diff --git a/vendor/github.com/robertkrimen/otto/underscore/README.markdown b/vendor/github.com/robertkrimen/otto/underscore/README.markdown new file mode 100644 index 00000000..bce37b69 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore/README.markdown @@ -0,0 +1,53 @@ +# underscore +-- + import "github.com/robertkrimen/otto/underscore" + +Package underscore contains the source for the JavaScript utility-belt library. + + import ( + _ "github.com/robertkrimen/otto/underscore" + ) + // Every Otto runtime will now include underscore + +http://underscorejs.org + +https://github.com/documentcloud/underscore + +By importing this package, you'll automatically load underscore every time you +create a new Otto runtime. + +To prevent this behavior, you can do the following: + + import ( + "github.com/robertkrimen/otto/underscore" + ) + + func init() { + underscore.Disable() + } + +## Usage + +#### func Disable + +```go +func Disable() +``` +Disable underscore runtime inclusion. + +#### func Enable + +```go +func Enable() +``` +Enable underscore runtime inclusion. + +#### func Source + +```go +func Source() string +``` +Source returns the underscore source. + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/robertkrimen/otto/underscore/source.go b/vendor/github.com/robertkrimen/otto/underscore/source.go new file mode 100644 index 00000000..7c5df971 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore/source.go @@ -0,0 +1,3463 @@ +package underscore + +func underscore() []byte { + return []byte{ + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x73, 0x20, 0x31, 0x2e, 0x34, + 0x2e, 0x34, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, + 0x6f, 0x72, 0x65, 0x6a, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x0a, 0x2f, 0x2f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, + 0x39, 0x2d, 0x32, 0x30, 0x31, 0x33, 0x20, 0x4a, 0x65, 0x72, 0x65, 0x6d, + 0x79, 0x20, 0x41, 0x73, 0x68, 0x6b, 0x65, 0x6e, 0x61, 0x73, 0x2c, 0x20, + 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6c, 0x6f, 0x75, + 0x64, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, + 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x66, 0x72, 0x65, 0x65, + 0x6c, 0x79, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x4d, 0x49, 0x54, 0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, + 0x2e, 0x0a, 0x0a, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, + 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x73, 0x65, 0x74, 0x75, + 0x70, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x60, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x60, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, + 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x60, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x60, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x0a, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x3d, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x53, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x5f, 0x60, 0x20, 0x76, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x55, + 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x3d, 0x20, + 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x5f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x67, 0x65, 0x74, 0x73, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, + 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x61, 0x76, 0x65, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6d, 0x69, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x28, 0x62, 0x75, + 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x67, 0x7a, 0x69, 0x70, 0x70, 0x65, + 0x64, 0x29, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x0a, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, + 0x4f, 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, + 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x20, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x70, 0x65, 0x65, + 0x64, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x63, 0x6f, 0x72, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, + 0x75, 0x73, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, + 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x4f, + 0x62, 0x6a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x68, 0x61, 0x73, 0x4f, + 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x2a, 0x2a, + 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, + 0x2a, 0x2a, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x65, 0x64, + 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, + 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x61, + 0x70, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, + 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x20, 0x3d, 0x20, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, + 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x65, 0x76, 0x65, 0x72, + 0x79, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x4f, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x4f, 0x66, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4b, 0x65, 0x79, + 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, + 0x69, 0x6e, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x62, 0x69, 0x6e, 0x64, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x61, 0x66, + 0x65, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x75, 0x73, 0x65, 0x20, 0x62, 0x65, 0x6c, + 0x6f, 0x77, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x5f, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x5f, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x64, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x78, 0x70, + 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, + 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x2a, 0x2a, 0x4e, 0x6f, 0x64, 0x65, + 0x2e, 0x6a, 0x73, 0x2a, 0x2a, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x77, 0x61, 0x72, + 0x64, 0x73, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x60, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x28, 0x29, 0x60, 0x20, 0x41, 0x50, 0x49, 0x2e, 0x20, 0x49, 0x66, + 0x20, 0x77, 0x65, 0x27, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, + 0x65, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x20, 0x60, 0x5f, 0x60, 0x20, + 0x61, 0x73, 0x20, 0x61, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x76, 0x69, 0x61, 0x20, 0x61, + 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x43, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, + 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x22, 0x61, + 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x20, 0x6d, 0x6f, 0x64, + 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, + 0x65, 0x6f, 0x66, 0x20, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, + 0x21, 0x3d, 0x3d, 0x20, 0x27, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x27, 0x75, 0x6e, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x27, 0x20, 0x26, 0x26, 0x20, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, + 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2e, + 0x5f, 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x20, 0x65, + 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x6f, + 0x6f, 0x74, 0x2e, 0x5f, 0x20, 0x3d, 0x20, 0x5f, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, + 0x20, 0x3d, 0x20, 0x27, 0x31, 0x2e, 0x34, 0x2e, 0x34, 0x27, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x6e, 0x65, 0x72, 0x73, 0x74, 0x6f, + 0x6e, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x20, 0x60, 0x65, 0x61, 0x63, 0x68, + 0x60, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x61, 0x6b, 0x61, 0x20, 0x60, 0x66, + 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x2d, 0x69, 0x6e, 0x20, + 0x60, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x60, 0x2c, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, + 0x61, 0x77, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x66, 0x6f, 0x72, + 0x45, 0x61, 0x63, 0x68, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x65, + 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x72, 0x45, + 0x61, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x46, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x26, 0x26, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, + 0x45, 0x61, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, + 0x68, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, + 0x20, 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, + 0x72, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, + 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, + 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, + 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x65, 0x72, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, + 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, + 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x6d, + 0x61, 0x70, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x61, + 0x70, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, + 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x20, 0x26, 0x26, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x6d, 0x61, 0x70, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x70, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6d, 0x61, 0x70, + 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, + 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x20, 0x3d, 0x20, 0x27, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x20, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2a, 0x2a, 0x52, + 0x65, 0x64, 0x75, 0x63, 0x65, 0x2a, 0x2a, 0x20, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x73, 0x20, 0x75, 0x70, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6b, 0x61, + 0x20, 0x60, 0x69, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x60, 0x2c, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x72, 0x20, 0x60, 0x66, 0x6f, 0x6c, 0x64, + 0x6c, 0x60, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x72, 0x65, 0x64, 0x75, + 0x63, 0x65, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x6c, + 0x64, 0x6c, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x32, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, + 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x6f, 0x62, + 0x6a, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, + 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x62, + 0x69, 0x6e, 0x64, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3f, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x28, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, + 0x29, 0x20, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, + 0x63, 0x65, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, + 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, + 0x2c, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x20, 0x61, 0x73, 0x20, 0x60, 0x66, 0x6f, 0x6c, 0x64, 0x72, 0x60, 0x2e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, + 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, + 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x72, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x60, 0x20, 0x69, + 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, + 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x66, 0x6f, 0x6c, + 0x64, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x32, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, + 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x6f, + 0x62, 0x6a, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, + 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x26, + 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, + 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, 0x69, + 0x67, 0x68, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x29, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, + 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, + 0x65, 0x52, 0x69, 0x67, 0x68, 0x74, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x29, 0x20, 0x3a, + 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x52, + 0x69, 0x67, 0x68, 0x74, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x2b, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, + 0x3d, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, + 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3f, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x5b, + 0x2d, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3a, 0x20, + 0x2d, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x6f, + 0x62, 0x6a, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, + 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2c, 0x20, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x21, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x29, 0x20, 0x74, 0x68, + 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, + 0x68, 0x69, 0x63, 0x68, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x73, 0x20, + 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x60, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x60, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x6e, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x70, 0x61, 0x73, 0x73, 0x20, 0x61, 0x20, 0x74, + 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, + 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, + 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, + 0x68, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x74, 0x65, + 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x20, 0x60, 0x65, 0x76, 0x65, 0x72, 0x79, 0x60, 0x20, 0x69, 0x66, + 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, + 0x20, 0x61, 0x73, 0x20, 0x60, 0x61, 0x6c, 0x6c, 0x60, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x3d, 0x20, 0x5f, + 0x2e, 0x61, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, + 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x45, 0x76, 0x65, 0x72, 0x79, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x45, 0x76, 0x65, 0x72, 0x79, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x65, + 0x76, 0x65, 0x72, 0x79, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x26, 0x26, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x21, 0x21, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x61, + 0x74, 0x20, 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x65, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, + 0x68, 0x20, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, + 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x20, 0x60, 0x73, 0x6f, 0x6d, 0x65, 0x60, 0x20, 0x69, + 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, + 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x61, 0x6e, 0x79, 0x60, 0x2e, 0x0a, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x3d, 0x20, + 0x5f, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x61, + 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, + 0x6f, 0x6d, 0x65, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x73, + 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x53, 0x6f, 0x6d, 0x65, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x28, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x7c, 0x7c, + 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x29, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x65, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, + 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x28, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x3d, 0x3d, 0x3d, 0x60, + 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x3d, 0x3d, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x2d, 0x31, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x6e, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, + 0x20, 0x61, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x28, 0x77, + 0x69, 0x74, 0x68, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x29, 0x20, 0x6f, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, + 0x69, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, + 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, + 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, + 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, + 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x28, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x20, 0x3f, 0x20, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x29, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, + 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, + 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x6d, 0x61, + 0x70, 0x60, 0x3a, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, + 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x20, 0x3d, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, + 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x7b, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x60, 0x3a, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x20, 0x60, 0x6b, 0x65, 0x79, 0x3a, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2c, 0x20, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, + 0x61, 0x74, 0x74, 0x72, 0x73, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x20, 0x3a, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x5b, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x20, 0x3f, 0x20, 0x27, 0x66, 0x69, 0x6e, 0x64, + 0x27, 0x20, 0x3a, 0x20, 0x27, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x27, + 0x5d, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x61, + 0x74, 0x74, 0x72, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x74, 0x74, 0x72, + 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x66, 0x69, 0x6e, 0x64, + 0x60, 0x3a, 0x20, 0x67, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x63, 0x20, 0x60, 0x6b, 0x65, 0x79, 0x3a, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x57, 0x68, 0x65, 0x72, + 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x5f, 0x2e, 0x77, 0x68, 0x65, 0x72, 0x65, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x61, 0x74, 0x74, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x28, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x61, 0x6e, 0x27, + 0x74, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x65, 0x72, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, + 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x36, 0x35, 0x2c, 0x35, 0x33, 0x35, + 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x65, 0x3a, 0x20, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x62, 0x75, 0x67, 0x73, 0x2e, 0x77, 0x65, + 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x73, 0x68, 0x6f, + 0x77, 0x5f, 0x62, 0x75, 0x67, 0x2e, 0x63, 0x67, 0x69, 0x3f, 0x69, 0x64, + 0x3d, 0x38, 0x30, 0x37, 0x39, 0x37, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, + 0x61, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, + 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x5b, 0x30, + 0x5d, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x3c, 0x20, 0x36, 0x35, 0x35, 0x33, 0x35, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, 0x61, 0x78, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2c, + 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, + 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x49, 0x6e, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, + 0x20, 0x7b, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, + 0x20, 0x2d, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x2d, 0x49, 0x6e, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x79, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, + 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3d, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3f, 0x20, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, + 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, + 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3e, 0x3d, 0x20, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, + 0x65, 0x64, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x28, 0x6f, 0x72, 0x20, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, + 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, + 0x2b, 0x6f, 0x62, 0x6a, 0x5b, 0x30, 0x5d, 0x20, 0x26, 0x26, 0x20, 0x6f, + 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3c, 0x20, + 0x36, 0x35, 0x35, 0x33, 0x35, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x61, + 0x74, 0x68, 0x2e, 0x6d, 0x69, 0x6e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x49, + 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x7d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x64, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x3f, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x3a, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3c, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x20, 0x26, 0x26, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x3a, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x64, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x72, 0x61, 0x6e, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x68, 0x75, + 0x66, 0x66, 0x6c, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6f, 0x62, 0x6a, + 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x61, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x72, + 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2b, + 0x2b, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x68, + 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x68, 0x75, 0x66, + 0x66, 0x6c, 0x65, 0x64, 0x5b, 0x72, 0x61, 0x6e, 0x64, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, + 0x65, 0x64, 0x5b, 0x72, 0x61, 0x6e, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6e, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x2e, + 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, + 0x20, 0x3f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, + 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x5b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5d, 0x3b, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, + 0x6f, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x27, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, + 0x62, 0x79, 0x20, 0x61, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, + 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, + 0x42, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x28, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, + 0x6b, 0x28, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x20, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, + 0x3a, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, + 0x61, 0x20, 0x3a, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x65, 0x66, 0x74, 0x2c, 0x20, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x20, 0x3d, 0x20, 0x6c, 0x65, + 0x66, 0x74, 0x2e, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, + 0x20, 0x3d, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x63, 0x72, 0x69, + 0x74, 0x65, 0x72, 0x69, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x62, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x3e, 0x20, 0x62, 0x20, 0x7c, 0x7c, + 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x30, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x31, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x61, 0x20, 0x3c, 0x20, 0x62, 0x20, 0x7c, 0x7c, 0x20, 0x62, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x65, 0x66, 0x74, + 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3c, 0x20, 0x72, 0x69, 0x67, + 0x68, 0x74, 0x2e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3f, 0x20, 0x2d, + 0x31, 0x20, 0x3a, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x29, 0x2c, 0x20, 0x27, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x27, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, + 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x20, 0x22, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, + 0x79, 0x22, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x62, + 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x7c, 0x7c, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x28, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x27, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x62, + 0x79, 0x20, 0x61, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, + 0x6e, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x20, 0x65, 0x69, 0x74, 0x68, + 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, + 0x62, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x3f, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3a, 0x20, 0x28, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, + 0x3d, 0x20, 0x5b, 0x5d, 0x29, 0x29, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, + 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, 0x79, 0x20, 0x61, + 0x20, 0x63, 0x65, 0x72, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x63, 0x72, 0x69, + 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x62, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x72, 0x69, + 0x74, 0x65, 0x72, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x28, 0x6f, + 0x62, 0x6a, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, + 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x68, 0x61, 0x73, + 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x6b, 0x65, 0x79, + 0x29, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, + 0x79, 0x5d, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6b, 0x65, 0x79, + 0x5d, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x55, 0x73, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x20, + 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6d, 0x61, 0x6c, + 0x6c, 0x65, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x61, + 0x74, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x73, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x73, + 0x65, 0x72, 0x74, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x20, 0x61, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x73, 0x20, + 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, + 0x20, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x3f, + 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, + 0x3a, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x28, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x6f, 0x77, + 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x68, 0x69, 0x67, 0x68, 0x20, 0x3d, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, + 0x20, 0x28, 0x6c, 0x6f, 0x77, 0x20, 0x3c, 0x20, 0x68, 0x69, 0x67, 0x68, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6d, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x28, 0x6c, 0x6f, 0x77, + 0x20, 0x2b, 0x20, 0x68, 0x69, 0x67, 0x68, 0x29, 0x20, 0x3e, 0x3e, 0x3e, + 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x5b, 0x6d, 0x69, 0x64, 0x5d, 0x29, 0x20, 0x3c, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3f, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x3d, + 0x20, 0x6d, 0x69, 0x64, 0x20, 0x2b, 0x20, 0x31, 0x20, 0x3a, 0x20, 0x68, + 0x69, 0x67, 0x68, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x6c, 0x6f, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x61, 0x66, 0x65, + 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x61, + 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x62, 0x6c, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, + 0x72, 0x65, 0x61, 0x6c, 0x2c, 0x20, 0x6c, 0x69, 0x76, 0x65, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x6f, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, + 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, + 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x6d, 0x61, + 0x70, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2b, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3a, 0x20, 0x5f, 0x2e, + 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x50, + 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, + 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x4e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, + 0x73, 0x20, 0x60, 0x68, 0x65, 0x61, 0x64, 0x60, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x60, 0x74, 0x61, 0x6b, 0x65, 0x60, 0x2e, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, + 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x5f, 0x2e, + 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x68, 0x65, 0x61, 0x64, + 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, + 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x6e, 0x20, 0x21, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x67, 0x75, 0x61, + 0x72, 0x64, 0x20, 0x3f, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x30, + 0x2c, 0x20, 0x6e, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x65, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x62, + 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, + 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x20, 0x45, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, + 0x6c, 0x20, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, + 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6c, + 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x65, 0x78, 0x63, 0x6c, + 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, + 0x73, 0x74, 0x20, 0x4e, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, + 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, + 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x28, 0x28, 0x6e, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7c, 0x7c, 0x20, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x3f, 0x20, 0x31, 0x20, 0x3a, 0x20, + 0x6e, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6c, 0x61, 0x73, 0x74, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x2a, 0x2a, + 0x6e, 0x2a, 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, + 0x20, 0x4e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, + 0x75, 0x61, 0x72, 0x64, 0x2a, 0x2a, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x6e, 0x2c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x6e, + 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x26, 0x26, + 0x20, 0x21, 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, + 0x61, 0x78, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x6e, 0x2c, 0x20, 0x30, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, + 0x20, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x74, + 0x68, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, + 0x73, 0x20, 0x60, 0x74, 0x61, 0x69, 0x6c, 0x60, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x60, 0x64, 0x72, 0x6f, 0x70, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x45, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x20, 0x50, 0x61, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x2a, 0x2a, 0x6e, 0x2a, + 0x2a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, + 0x65, 0x73, 0x74, 0x20, 0x4e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x2a, 0x2a, 0x67, 0x75, 0x61, + 0x72, 0x64, 0x2a, 0x2a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x69, + 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x60, 0x5f, 0x2e, 0x6d, 0x61, 0x70, 0x60, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5f, + 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x64, 0x72, + 0x6f, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6e, 0x2c, 0x20, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x28, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x20, 0x3f, + 0x20, 0x31, 0x20, 0x3a, 0x20, 0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x72, 0x69, 0x6d, + 0x20, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x66, 0x61, 0x6c, + 0x73, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x20, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, + 0x76, 0x65, 0x20, 0x60, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x60, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x2c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, + 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x3f, 0x20, + 0x70, 0x75, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x29, 0x20, 0x3a, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x2c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x6c, + 0x79, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x20, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x28, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2c, + 0x20, 0x5b, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x61, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x28, 0x73, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, 0x20, + 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2d, 0x66, 0x72, + 0x65, 0x65, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, + 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x68, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, + 0x20, 0x61, 0x20, 0x66, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x60, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x60, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, + 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3d, + 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x3f, 0x20, 0x5f, 0x2e, + 0x6d, 0x61, 0x70, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, 0x65, 0x65, + 0x6e, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3f, 0x20, + 0x28, 0x21, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x7c, 0x7c, 0x20, 0x73, + 0x65, 0x65, 0x6e, 0x5b, 0x73, 0x65, 0x65, 0x6e, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x21, 0x3d, 0x3d, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x20, 0x3a, 0x20, 0x21, 0x5f, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x28, 0x73, 0x65, + 0x65, 0x6e, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, + 0x65, 0x6e, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, + 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x65, 0x61, 0x63, + 0x68, 0x20, 0x64, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x63, 0x74, 0x20, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, + 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, + 0x75, 0x6e, 0x69, 0x71, 0x28, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, + 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, + 0x65, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, + 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, + 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, + 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, + 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x65, + 0x76, 0x65, 0x72, 0x79, 0x28, 0x72, 0x65, 0x73, 0x74, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2c, + 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x61, 0x6b, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x6f, 0x6e, + 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x61, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, + 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, + 0x6a, 0x75, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x77, 0x69, 0x6c, + 0x6c, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, + 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x28, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, + 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x7b, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x5f, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x28, 0x72, 0x65, 0x73, 0x74, 0x2c, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x5a, 0x69, 0x70, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x73, + 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, + 0x2d, 0x2d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x67, 0x6f, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x7a, 0x69, 0x70, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, + 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, + 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x6d, 0x61, + 0x78, 0x28, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x28, 0x61, 0x72, + 0x67, 0x73, 0x2c, 0x20, 0x27, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x27, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x70, 0x6c, 0x75, 0x63, 0x6b, 0x28, 0x61, + 0x72, 0x67, 0x73, 0x2c, 0x20, 0x22, 0x22, 0x20, 0x2b, 0x20, 0x69, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x73, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x20, 0x50, 0x61, 0x73, 0x73, + 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x60, 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x5d, 0x60, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x61, + 0x69, 0x72, 0x73, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x77, 0x6f, 0x20, + 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, + 0x2d, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, 0x79, + 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, + 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x69, 0x73, 0x74, 0x2c, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, 0x69, 0x73, 0x74, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, + 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, + 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6c, 0x69, 0x73, 0x74, 0x5b, + 0x69, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6c, + 0x69, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x5b, 0x30, 0x5d, 0x5d, 0x20, 0x3d, + 0x20, 0x6c, 0x69, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x5b, 0x31, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x20, + 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, + 0x6c, 0x79, 0x20, 0x75, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x28, 0x49, 0x27, 0x6d, 0x20, + 0x6c, 0x6f, 0x6f, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x74, 0x20, 0x79, + 0x6f, 0x75, 0x2c, 0x20, 0x2a, 0x2a, 0x4d, 0x53, 0x49, 0x45, 0x2a, 0x2a, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x77, 0x65, 0x20, 0x6e, + 0x65, 0x65, 0x64, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6e, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x2d, + 0x31, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x74, 0x65, + 0x6d, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, + 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x20, 0x60, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, + 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x69, 0x73, + 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, + 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, + 0x72, 0x74, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x20, 0x60, 0x74, 0x72, 0x75, 0x65, 0x60, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x2a, 0x2a, 0x69, 0x73, 0x53, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x2a, 0x2a, 0x20, 0x74, 0x6f, 0x20, 0x75, + 0x73, 0x65, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x73, 0x53, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, + 0x66, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x3d, + 0x3d, 0x20, 0x27, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x27, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x28, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, + 0x3c, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x6d, + 0x61, 0x78, 0x28, 0x30, 0x2c, 0x20, 0x6c, 0x20, 0x2b, 0x20, 0x69, 0x73, + 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x3a, 0x20, 0x69, 0x73, + 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x5f, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, + 0x6d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, + 0x20, 0x3f, 0x20, 0x69, 0x20, 0x3a, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x26, + 0x26, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, + 0x2c, 0x20, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x3b, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x20, 0x60, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x60, 0x20, 0x69, 0x66, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6c, 0x61, + 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x66, 0x72, + 0x6f, 0x6d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x21, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x20, 0x26, 0x26, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x61, + 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3f, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x2c, + 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x29, 0x20, 0x3a, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2e, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x4f, 0x66, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x28, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x20, 0x3f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x3a, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, + 0x28, 0x69, 0x2d, 0x2d, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x69, + 0x74, 0x65, 0x6d, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x2d, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x69, + 0x74, 0x68, 0x6d, 0x65, 0x74, 0x69, 0x63, 0x20, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x41, 0x20, 0x70, + 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x50, + 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x20, 0x60, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x28, 0x29, 0x60, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x20, 0x53, 0x65, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x5b, + 0x74, 0x68, 0x65, 0x20, 0x50, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x20, 0x64, + 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5d, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, + 0x73, 0x2e, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2e, 0x6f, 0x72, 0x67, + 0x2f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x2f, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x23, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, + 0x73, 0x74, 0x6f, 0x70, 0x2c, 0x20, 0x73, 0x74, 0x65, 0x70, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x20, 0x3d, 0x20, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x7c, 0x7c, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5b, 0x32, 0x5d, 0x20, 0x7c, + 0x7c, 0x20, 0x31, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x68, + 0x2e, 0x6d, 0x61, 0x78, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x63, 0x65, + 0x69, 0x6c, 0x28, 0x28, 0x73, 0x74, 0x6f, 0x70, 0x20, 0x2d, 0x20, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x29, 0x20, 0x2f, 0x20, 0x73, 0x74, 0x65, 0x70, + 0x29, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x69, 0x64, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x28, 0x6c, 0x65, 0x6e, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x69, 0x64, 0x78, 0x20, 0x3c, + 0x20, 0x6c, 0x65, 0x6e, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x5b, 0x69, 0x64, 0x78, 0x2b, + 0x2b, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, + 0x2b, 0x3d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, 0x68, 0x65, 0x6d, 0x29, + 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x67, 0x69, + 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x28, + 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x74, + 0x68, 0x69, 0x73, 0x60, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, + 0x29, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, + 0x20, 0x74, 0x6f, 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x60, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x60, 0x20, 0x69, 0x66, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, + 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, + 0x6e, 0x64, 0x20, 0x26, 0x26, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x42, 0x69, 0x6e, 0x64, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x69, 0x6e, 0x64, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x2c, 0x20, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x2e, 0x63, 0x6f, + 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x61, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x68, 0x61, + 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x74, + 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x72, 0x65, 0x2d, 0x66, 0x69, 0x6c, + 0x6c, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, + 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, + 0x73, 0x20, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x20, 0x60, 0x74, + 0x68, 0x69, 0x73, 0x60, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, + 0x6c, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, + 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, + 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x42, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x27, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, 0x6c, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x20, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x62, 0x65, 0x6c, 0x6f, + 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x20, + 0x5f, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2e, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, + 0x29, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, + 0x28, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x20, 0x7b, 0x20, 0x6f, 0x62, + 0x6a, 0x5b, 0x66, 0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x62, 0x69, 0x6e, + 0x64, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x66, 0x5d, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4d, + 0x65, 0x6d, 0x6f, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, + 0x70, 0x65, 0x6e, 0x73, 0x69, 0x76, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x65, 0x6d, + 0x6f, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x68, 0x61, + 0x73, 0x68, 0x65, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x7b, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, + 0x72, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, + 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x68, 0x61, 0x73, 0x68, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x6d, 0x65, + 0x6d, 0x6f, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x3f, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3a, 0x20, 0x28, + 0x6d, 0x65, 0x6d, 0x6f, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x74, 0x68, 0x65, 0x6e, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, + 0x63, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, + 0x3d, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, + 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, + 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, + 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6e, 0x75, 0x6c, 0x6c, + 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x20, 0x7d, 0x2c, 0x20, + 0x77, 0x61, 0x69, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x65, 0x72, 0x73, + 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, + 0x20, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x69, 0x6e, 0x67, 0x20, + 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x61, 0x66, + 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x20, 0x68, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x64, 0x65, 0x66, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x28, 0x5f, 0x2c, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x2c, + 0x20, 0x31, 0x5d, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x73, + 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x76, + 0x6f, 0x6b, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x6d, 0x6f, 0x73, 0x74, + 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x64, + 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, + 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x68, + 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x2c, 0x20, + 0x77, 0x61, 0x69, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, + 0x20, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, + 0x44, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x44, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, + 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x69, 0x74, 0x20, 0x2d, 0x20, + 0x28, 0x6e, 0x6f, 0x77, 0x20, 0x2d, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x6f, 0x75, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72, + 0x67, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, + 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x6f, + 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x21, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, + 0x67, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x2c, 0x20, 0x61, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67, + 0x20, 0x61, 0x73, 0x20, 0x69, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, + 0x6e, 0x75, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x69, + 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x2c, 0x20, 0x77, 0x69, 0x6c, 0x6c, + 0x20, 0x6e, 0x6f, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x65, + 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x2e, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x64, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x69, 0x74, + 0x20, 0x73, 0x74, 0x6f, 0x70, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2e, 0x20, 0x49, 0x66, 0x20, + 0x60, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x60, 0x20, + 0x69, 0x73, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x2c, 0x20, 0x74, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6c, 0x65, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x20, 0x65, 0x64, 0x67, 0x65, 0x2c, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x62, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, + 0x75, 0x6e, 0x63, 0x2c, 0x20, 0x77, 0x61, 0x69, 0x74, 0x2c, 0x20, 0x69, + 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x21, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, + 0x74, 0x65, 0x29, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, + 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, + 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x69, 0x6d, 0x6d, + 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x26, 0x26, 0x20, 0x21, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x28, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x77, + 0x61, 0x69, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x63, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x77, 0x29, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, + 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x6d, + 0x65, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x20, 0x68, 0x6f, 0x77, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x66, + 0x74, 0x65, 0x6e, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6c, 0x6c, + 0x20, 0x69, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, + 0x63, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x72, 0x61, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x61, 0x6e, 0x29, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6e, 0x20, 0x3d, 0x20, + 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x65, 0x6d, 0x6f, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, + 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x65, 0x6d, 0x6f, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, + 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6e, 0x20, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x2c, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, + 0x67, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x6a, + 0x75, 0x73, 0x74, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x2c, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, + 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x63, + 0x2c, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x73, 0x68, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, + 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, + 0x67, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x65, 0x61, 0x63, 0x68, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x73, 0x20, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, + 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x73, 0x2e, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x20, + 0x69, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x2d, 0x2d, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x73, + 0x5b, 0x69, 0x5d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, 0x29, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x72, 0x67, + 0x73, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, + 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, + 0x20, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x20, 0x61, 0x66, + 0x74, 0x65, 0x72, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x61, + 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x4e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x2d, + 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x3c, 0x20, 0x31, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, + 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x2a, 0x2a, 0x45, 0x43, 0x4d, 0x41, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x20, 0x35, 0x2a, 0x2a, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x20, 0x60, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, + 0x65, 0x79, 0x73, 0x60, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, + 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4b, 0x65, + 0x79, 0x73, 0x20, 0x7c, 0x7c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x21, 0x3d, + 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x29, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x27, + 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, + 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, + 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x6b, + 0x65, 0x79, 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x73, 0x2e, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6b, + 0x65, 0x79, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, + 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, + 0x68, 0x61, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, + 0x29, 0x29, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x70, 0x75, + 0x73, 0x68, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5d, 0x60, 0x20, 0x70, 0x61, 0x69, 0x72, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x61, 0x69, 0x72, 0x73, + 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x5b, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, + 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, + 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, + 0x5b, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, + 0x79, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6b, 0x65, + 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x7b, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, + 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5b, 0x6f, 0x62, 0x6a, 0x5b, + 0x6b, 0x65, 0x79, 0x5d, 0x5d, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x79, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x61, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x60, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x73, 0x60, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, + 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, + 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, + 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x29, 0x29, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6b, 0x65, 0x79, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x65, 0x64, 0x2d, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x28, 0x73, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, + 0x78, 0x74, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x73, 0x6c, 0x69, + 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x20, + 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, + 0x62, 0x6a, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x20, 0x3d, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x69, + 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x3d, 0x20, + 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, + 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x73, 0x6c, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x6b, 0x65, 0x79, 0x73, + 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6b, + 0x65, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, + 0x62, 0x6a, 0x29, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x5b, 0x6b, 0x65, 0x79, + 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x70, + 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, + 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6c, 0x61, 0x63, 0x6b, + 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6f, + 0x6d, 0x69, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x3d, + 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x63, + 0x61, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x73, 0x6c, 0x69, + 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, + 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x21, 0x5f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, + 0x28, 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, + 0x20, 0x63, 0x6f, 0x70, 0x79, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x20, 0x3d, + 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6b, 0x65, 0x79, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x69, 0x6c, + 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, + 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x31, + 0x29, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, 0x70, + 0x72, 0x6f, 0x70, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x70, + 0x72, 0x6f, 0x70, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, + 0x29, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x70, 0x72, 0x6f, 0x70, 0x5d, 0x20, + 0x3d, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5b, 0x70, 0x72, 0x6f, + 0x70, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x28, 0x73, 0x68, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x2d, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x29, 0x20, 0x64, + 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x20, 0x3d, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, + 0x5f, 0x2e, 0x69, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, + 0x62, 0x6a, 0x29, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3f, 0x20, 0x6f, 0x62, 0x6a, + 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x29, 0x20, 0x3a, 0x20, 0x5f, + 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x28, 0x7b, 0x7d, 0x2c, 0x20, + 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x6f, 0x62, 0x6a, 0x2e, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x22, + 0x74, 0x61, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x22, 0x20, 0x61, 0x20, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x2c, 0x20, 0x69, 0x6e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x65, 0x72, 0x66, 0x6f, + 0x72, 0x6d, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, + 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x74, 0x61, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, + 0x6f, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x63, + 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x60, 0x69, 0x73, 0x45, 0x71, + 0x75, 0x61, 0x6c, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x65, 0x71, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, + 0x2e, 0x20, 0x60, 0x30, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x2d, 0x30, 0x60, + 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x61, + 0x72, 0x65, 0x6e, 0x27, 0x74, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x63, 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x61, 0x72, 0x6d, + 0x6f, 0x6e, 0x79, 0x20, 0x60, 0x65, 0x67, 0x61, 0x6c, 0x60, 0x20, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x3a, 0x20, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x69, 0x6b, 0x69, 0x2e, 0x65, 0x63, 0x6d, + 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, + 0x64, 0x6f, 0x6b, 0x75, 0x2e, 0x70, 0x68, 0x70, 0x3f, 0x69, 0x64, 0x3d, + 0x68, 0x61, 0x72, 0x6d, 0x6f, 0x6e, 0x79, 0x3a, 0x65, 0x67, 0x61, 0x6c, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x62, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x61, 0x20, 0x21, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x7c, 0x7c, + 0x20, 0x31, 0x20, 0x2f, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, + 0x2f, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x41, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x6e, + 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, 0x79, 0x20, 0x62, 0x65, 0x63, + 0x61, 0x75, 0x73, 0x65, 0x20, 0x60, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x3d, + 0x3d, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x60, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x62, + 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x62, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x6e, 0x77, + 0x72, 0x61, 0x70, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x5f, 0x29, + 0x20, 0x61, 0x20, 0x3d, 0x20, 0x61, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x62, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, + 0x66, 0x20, 0x5f, 0x29, 0x20, 0x62, 0x20, 0x3d, 0x20, 0x62, 0x2e, 0x5f, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, + 0x60, 0x5b, 0x5b, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x5d, 0x5d, 0x60, 0x20, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, + 0x20, 0x3d, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, + 0x6d, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x62, 0x29, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, + 0x20, 0x28, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, + 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, + 0x69, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, + 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x3b, + 0x20, 0x74, 0x68, 0x75, 0x73, 0x2c, 0x20, 0x60, 0x22, 0x35, 0x22, 0x60, + 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, + 0x74, 0x20, 0x74, 0x6f, 0x20, 0x60, 0x6e, 0x65, 0x77, 0x20, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x28, 0x22, 0x35, 0x22, 0x29, 0x60, 0x2e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x28, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5d, 0x27, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x65, + 0x71, 0x75, 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x62, + 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x66, 0x6c, 0x65, + 0x78, 0x69, 0x76, 0x65, 0x2e, 0x20, 0x41, 0x6e, 0x20, 0x60, 0x65, 0x67, + 0x61, 0x6c, 0x60, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, + 0x6d, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x21, 0x3d, + 0x20, 0x2b, 0x61, 0x20, 0x3f, 0x20, 0x62, 0x20, 0x21, 0x3d, 0x20, 0x2b, + 0x62, 0x20, 0x3a, 0x20, 0x28, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, + 0x3f, 0x20, 0x31, 0x20, 0x2f, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x31, + 0x20, 0x2f, 0x20, 0x62, 0x20, 0x3a, 0x20, 0x61, 0x20, 0x3d, 0x3d, 0x20, + 0x2b, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x44, 0x61, 0x74, 0x65, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x27, 0x5b, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x20, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, + 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x65, 0x72, 0x63, 0x65, 0x20, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6c, + 0x65, 0x61, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6e, 0x75, 0x6d, 0x65, + 0x72, 0x69, 0x63, 0x20, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x20, 0x44, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x69, + 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, + 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x72, + 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, + 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x2b, 0x61, + 0x20, 0x3d, 0x3d, 0x20, 0x2b, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x73, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x52, 0x65, + 0x67, 0x45, 0x78, 0x70, 0x5d, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, + 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, + 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x26, 0x26, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x3d, + 0x3d, 0x20, 0x62, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x26, + 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x69, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x69, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x26, 0x26, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x2e, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x61, + 0x73, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x62, 0x2e, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65, 0x43, 0x61, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, + 0x70, 0x65, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x6f, 0x66, 0x20, 0x62, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x27, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x20, + 0x65, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x63, 0x79, 0x63, 0x6c, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x20, + 0x63, 0x79, 0x63, 0x6c, 0x69, 0x63, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x64, 0x61, 0x70, 0x74, 0x65, 0x64, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x45, 0x53, 0x20, 0x35, 0x2e, 0x31, 0x20, + 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x35, 0x2e, 0x31, + 0x32, 0x2e, 0x33, 0x2c, 0x20, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x60, 0x4a, 0x4f, 0x60, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, + 0x72, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x61, + 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, + 0x28, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x2d, 0x2d, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4c, 0x69, + 0x6e, 0x65, 0x61, 0x72, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, + 0x20, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x6c, + 0x79, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, + 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x5b, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x61, + 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x53, 0x74, + 0x61, 0x63, 0x6b, 0x5b, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5d, 0x20, + 0x3d, 0x3d, 0x20, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x74, 0x61, 0x63, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, 0x76, + 0x65, 0x72, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, + 0x6b, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x61, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x75, + 0x73, 0x68, 0x28, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x2c, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, + 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, + 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x5d, 0x27, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x61, 0x20, 0x64, 0x65, 0x65, + 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, + 0x20, 0x69, 0x73, 0x20, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, + 0x79, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x69, 0x7a, + 0x65, 0x20, 0x3d, 0x20, 0x61, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3d, 0x3d, + 0x20, 0x62, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x65, 0x70, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x69, 0x67, 0x6e, + 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x6e, 0x75, + 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, + 0x65, 0x2d, 0x2d, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x65, 0x71, 0x28, 0x61, + 0x5b, 0x73, 0x69, 0x7a, 0x65, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x73, 0x69, + 0x7a, 0x65, 0x5d, 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2c, + 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x29, 0x29, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x64, + 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x61, 0x72, + 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, + 0x6c, 0x65, 0x6e, 0x74, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x60, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x60, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x64, 0x69, + 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x61, 0x6d, + 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x20, + 0x3d, 0x20, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x3d, + 0x20, 0x62, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x21, 0x3d, 0x3d, 0x20, + 0x62, 0x43, 0x74, 0x6f, 0x72, 0x20, 0x26, 0x26, 0x20, 0x21, 0x28, 0x5f, + 0x2e, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x61, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x61, + 0x43, 0x74, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x6f, 0x66, 0x20, 0x61, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, + 0x26, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x69, + 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x62, 0x43, + 0x74, 0x6f, 0x72, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x62, 0x43, 0x74, + 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, + 0x66, 0x20, 0x62, 0x43, 0x74, 0x6f, 0x72, 0x29, 0x29, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x28, 0x76, 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, + 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x61, + 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x65, 0x70, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x3d, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, 0x28, 0x62, 0x2c, 0x20, 0x6b, + 0x65, 0x79, 0x29, 0x20, 0x26, 0x26, 0x20, 0x65, 0x71, 0x28, 0x61, 0x5b, + 0x6b, 0x65, 0x79, 0x5d, 0x2c, 0x20, 0x62, 0x5b, 0x6b, 0x65, 0x79, 0x5d, + 0x2c, 0x20, 0x61, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2c, 0x20, 0x62, 0x53, + 0x74, 0x61, 0x63, 0x6b, 0x29, 0x29, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6b, 0x65, 0x79, 0x20, 0x69, + 0x6e, 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, + 0x61, 0x73, 0x28, 0x62, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x26, + 0x26, 0x20, 0x21, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x2d, 0x29, 0x29, + 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x21, + 0x73, 0x69, 0x7a, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x72, 0x61, + 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x2e, 0x70, 0x6f, 0x70, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x62, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x6f, 0x70, + 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x65, + 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x61, 0x20, 0x64, 0x65, 0x65, 0x70, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x20, + 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x69, 0x66, 0x20, + 0x74, 0x77, 0x6f, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x3d, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, + 0x20, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x71, 0x28, 0x61, 0x2c, 0x20, 0x62, + 0x2c, 0x20, 0x5b, 0x5d, 0x2c, 0x20, 0x5b, 0x5d, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, + 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, + 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x65, 0x6d, + 0x70, 0x74, 0x79, 0x3f, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x6e, + 0x20, 0x22, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x65, + 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6f, 0x77, + 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x5f, 0x2e, 0x69, 0x73, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x2e, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, + 0x61, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, + 0x6a, 0x29, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x2e, 0x68, 0x61, 0x73, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x29, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, + 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, + 0x20, 0x44, 0x4f, 0x4d, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x45, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x28, + 0x6f, 0x62, 0x6a, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6e, + 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x3d, 0x20, + 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, + 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x3f, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x45, 0x43, 0x4d, 0x41, 0x35, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x69, 0x73, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x20, 0x3d, 0x20, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x49, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x7c, 0x7c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, + 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, + 0x20, 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x5d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, + 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3f, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, + 0x69, 0x73, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x73, 0x3a, 0x20, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x2c, 0x20, 0x69, 0x73, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2c, + 0x20, 0x69, 0x73, 0x44, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x69, 0x73, 0x52, + 0x65, 0x67, 0x45, 0x78, 0x70, 0x2e, 0x0a, 0x20, 0x20, 0x65, 0x61, 0x63, + 0x68, 0x28, 0x5b, 0x27, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x27, 0x2c, 0x20, 0x27, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x27, + 0x2c, 0x20, 0x27, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x27, 0x2c, 0x20, + 0x27, 0x44, 0x61, 0x74, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x52, 0x65, 0x67, + 0x45, 0x78, 0x70, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5b, 0x27, 0x69, 0x73, 0x27, 0x20, 0x2b, + 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x20, + 0x27, 0x5b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x27, 0x20, 0x2b, + 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2b, 0x20, 0x27, 0x5d, 0x27, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x20, 0x61, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, + 0x69, 0x6e, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x73, 0x20, + 0x28, 0x61, 0x68, 0x65, 0x6d, 0x2c, 0x20, 0x49, 0x45, 0x29, 0x2c, 0x20, + 0x77, 0x68, 0x65, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x61, + 0x6e, 0x79, 0x20, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x22, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x21, 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x5f, 0x2e, 0x69, 0x73, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x21, 0x21, 0x28, + 0x6f, 0x62, 0x6a, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x2e, 0x68, 0x61, 0x73, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x27, 0x63, 0x61, 0x6c, 0x6c, 0x65, + 0x65, 0x27, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, + 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x20, 0x60, 0x69, 0x73, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x60, 0x20, 0x69, 0x66, 0x20, + 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x2e, + 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x6f, + 0x66, 0x20, 0x28, 0x2f, 0x2e, 0x2f, 0x29, 0x20, 0x21, 0x3d, 0x3d, 0x20, + 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6f, 0x62, 0x6a, + 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, + 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, + 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3f, 0x0a, 0x20, 0x20, 0x5f, + 0x2e, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x69, 0x73, 0x4e, + 0x61, 0x4e, 0x28, 0x70, 0x61, 0x72, 0x73, 0x65, 0x46, 0x6c, 0x6f, 0x61, + 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x20, 0x60, 0x4e, 0x61, 0x4e, 0x60, 0x3f, 0x20, 0x28, 0x4e, + 0x61, 0x4e, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, + 0x69, 0x63, 0x68, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, + 0x66, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, 0x61, + 0x4e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x26, + 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x21, 0x3d, 0x20, 0x2b, 0x6f, 0x62, + 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, + 0x6c, 0x65, 0x61, 0x6e, 0x3f, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x73, + 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, + 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, + 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x6f, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x5b, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x20, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, + 0x5d, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, 0x65, + 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x65, 0x71, 0x75, 0x61, + 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x73, 0x20, 0x61, 0x20, 0x67, 0x69, 0x76, + 0x65, 0x6e, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x3f, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x20, + 0x3d, 0x3d, 0x3d, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x63, 0x75, 0x74, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x66, 0x20, 0x61, 0x6e, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, + 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x79, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x6e, 0x20, 0x69, 0x74, 0x73, + 0x65, 0x6c, 0x66, 0x20, 0x28, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, + 0x79, 0x70, 0x65, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x68, 0x61, + 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x2c, + 0x20, 0x6b, 0x65, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x55, 0x6e, 0x64, + 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x73, 0x20, 0x69, + 0x6e, 0x20, 0x2a, 0x6e, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, + 0x74, 0x2a, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, + 0x5f, 0x60, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x69, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x61, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6e, 0x6f, 0x43, 0x6f, 0x6e, 0x66, + 0x6c, 0x69, 0x63, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x5f, 0x20, 0x3d, 0x20, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, + 0x6f, 0x72, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4b, 0x65, 0x65, + 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, + 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2a, + 0x2a, 0x6e, 0x2a, 0x2a, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x20, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x63, 0x63, 0x75, 0x6d, 0x20, 0x3d, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x6e, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, + 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x61, 0x63, 0x63, 0x75, 0x6d, + 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x75, + 0x6d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x72, + 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, + 0x72, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x6d, 0x69, + 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x78, 0x20, 0x28, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x29, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x20, 0x3d, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x69, 0x6e, + 0x2c, 0x20, 0x6d, 0x61, 0x78, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x61, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x69, 0x6e, + 0x20, 0x2b, 0x20, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, + 0x72, 0x28, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x28, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x6d, 0x61, 0x78, 0x20, 0x2d, + 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4c, + 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x20, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x20, + 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, + 0x61, 0x70, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x73, 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x27, 0x26, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x61, 0x6d, + 0x70, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, + 0x3c, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x6c, 0x74, 0x3b, 0x27, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x3e, 0x27, 0x3a, 0x20, 0x27, + 0x26, 0x67, 0x74, 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x27, 0x22, 0x27, 0x3a, 0x20, 0x27, 0x26, 0x71, 0x75, 0x6f, 0x74, + 0x3b, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, + 0x22, 0x3a, 0x20, 0x27, 0x26, 0x23, 0x78, 0x32, 0x37, 0x3b, 0x27, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x2f, 0x27, 0x3a, 0x20, + 0x27, 0x26, 0x23, 0x78, 0x32, 0x46, 0x3b, 0x27, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x75, 0x6e, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x69, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, + 0x70, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, 0x67, 0x65, 0x78, 0x65, 0x73, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, + 0x65, 0x64, 0x20, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, + 0x6c, 0x79, 0x20, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x2e, 0x0a, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, + 0x67, 0x65, 0x78, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x20, 0x20, + 0x6e, 0x65, 0x77, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x28, 0x27, + 0x5b, 0x27, 0x20, 0x2b, 0x20, 0x5f, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x2e, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x29, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x27, + 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x5d, 0x27, 0x2c, 0x20, 0x27, 0x67, + 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6e, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x65, + 0x67, 0x45, 0x78, 0x70, 0x28, 0x27, 0x28, 0x27, 0x20, 0x2b, 0x20, 0x5f, + 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x4d, 0x61, 0x70, 0x2e, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, + 0x29, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x27, 0x7c, 0x27, 0x29, 0x20, + 0x2b, 0x20, 0x27, 0x29, 0x27, 0x2c, 0x20, 0x27, 0x67, 0x27, 0x29, 0x0a, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x75, 0x6e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, + 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x74, 0x6f, 0x2f, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, + 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x75, 0x6e, + 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x27, 0x5d, 0x2c, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5b, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x27, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x27, 0x27, 0x20, 0x2b, 0x20, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x29, 0x2e, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x28, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, + 0x67, 0x65, 0x78, 0x65, 0x73, 0x5b, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x5b, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x5d, 0x5b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x20, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x6e, + 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x69, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x77, 0x69, + 0x73, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, + 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5b, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x79, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x2e, 0x69, 0x73, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x20, 0x3f, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x29, 0x20, + 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, + 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e, 0x20, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x64, + 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, 0x78, 0x69, + 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x28, 0x5f, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, + 0x29, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x5f, 0x5b, 0x6e, 0x61, + 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x6e, 0x61, + 0x6d, 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, + 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x61, 0x72, 0x67, 0x73, + 0x20, 0x3d, 0x20, 0x5b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x64, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x28, 0x5f, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x73, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, + 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x69, 0x64, + 0x20, 0x28, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x69, + 0x72, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x55, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x20, 0x44, 0x4f, + 0x4d, 0x20, 0x69, 0x64, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x69, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x75, 0x6e, 0x69, 0x71, + 0x75, 0x65, 0x49, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x64, + 0x20, 0x3d, 0x20, 0x2b, 0x2b, 0x69, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x20, 0x2b, 0x20, 0x27, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x20, 0x3f, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, + 0x2b, 0x20, 0x69, 0x64, 0x20, 0x3a, 0x20, 0x69, 0x64, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x79, + 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x55, 0x6e, + 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x75, 0x73, 0x65, + 0x73, 0x20, 0x45, 0x52, 0x42, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x64, 0x65, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x20, + 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, + 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x0a, + 0x20, 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x65, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x20, 0x2f, 0x3c, 0x25, 0x28, 0x5b, + 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, 0x25, 0x3e, 0x2f, 0x67, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3a, 0x20, 0x2f, 0x3c, 0x25, 0x3d, + 0x28, 0x5b, 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, 0x25, 0x3e, + 0x2f, 0x67, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x73, 0x63, 0x61, + 0x70, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x20, 0x2f, 0x3c, + 0x25, 0x2d, 0x28, 0x5b, 0x5c, 0x73, 0x5c, 0x53, 0x5d, 0x2b, 0x3f, 0x29, + 0x25, 0x3e, 0x2f, 0x67, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x57, 0x68, 0x65, 0x6e, 0x20, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x60, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x60, 0x2c, 0x20, 0x69, 0x66, 0x20, 0x79, 0x6f, 0x75, 0x20, + 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x61, 0x6e, 0x0a, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x67, 0x65, 0x78, + 0x2c, 0x20, 0x77, 0x65, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x6f, 0x6e, + 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, + 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x6f, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x3d, 0x20, 0x2f, 0x28, 0x2e, 0x29, + 0x5e, 0x2f, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, + 0x74, 0x65, 0x72, 0x73, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x64, 0x20, + 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x79, + 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x70, 0x75, 0x74, 0x20, + 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x6c, 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x27, 0x22, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x27, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x5c, + 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x5c, 0x27, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x72, 0x27, 0x3a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x27, 0x72, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x27, 0x5c, 0x6e, 0x27, 0x3a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x6e, + 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x27, 0x5c, 0x74, 0x27, 0x3a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x74, 0x27, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x27, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x38, 0x27, 0x3a, 0x20, + 0x27, 0x75, 0x32, 0x30, 0x32, 0x38, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x27, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x39, 0x27, 0x3a, 0x20, 0x27, + 0x75, 0x32, 0x30, 0x32, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, + 0x65, 0x72, 0x20, 0x3d, 0x20, 0x2f, 0x5c, 0x5c, 0x7c, 0x27, 0x7c, 0x5c, + 0x72, 0x7c, 0x5c, 0x6e, 0x7c, 0x5c, 0x74, 0x7c, 0x5c, 0x75, 0x32, 0x30, + 0x32, 0x38, 0x7c, 0x5c, 0x75, 0x32, 0x30, 0x32, 0x39, 0x2f, 0x67, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4a, 0x61, 0x76, 0x61, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2d, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x20, + 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x4a, + 0x6f, 0x68, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x69, 0x67, 0x27, 0x73, 0x20, + 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x6e, 0x64, + 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x20, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, + 0x20, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x2c, + 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x73, 0x20, 0x77, + 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2c, 0x0a, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x6f, 0x72, 0x72, + 0x65, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, + 0x73, 0x20, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, + 0x61, 0x74, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x0a, 0x20, + 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, + 0x65, 0x78, 0x74, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x5f, 0x2e, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x73, 0x28, 0x7b, 0x7d, 0x2c, 0x20, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x20, 0x5f, 0x2e, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x65, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, + 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x20, + 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x76, + 0x69, 0x61, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x52, 0x65, 0x67, 0x45, 0x78, 0x70, 0x28, 0x5b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x7c, 0x7c, + 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x7c, 0x7c, + 0x20, 0x6e, 0x6f, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x65, 0x76, + 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x6f, + 0x4d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, + 0x28, 0x27, 0x7c, 0x27, 0x29, 0x20, 0x2b, 0x20, 0x27, 0x7c, 0x24, 0x27, + 0x2c, 0x20, 0x27, 0x67, 0x27, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2c, 0x20, 0x65, 0x73, 0x63, + 0x61, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x20, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x73, 0x20, 0x61, 0x70, + 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, + 0x20, 0x22, 0x5f, 0x5f, 0x70, 0x2b, 0x3d, 0x27, 0x22, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2c, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x2c, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2c, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, + 0x2c, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x2c, 0x20, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, + 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, + 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x28, 0x65, 0x73, 0x63, + 0x61, 0x70, 0x65, 0x72, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x28, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x29, 0x20, 0x7b, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x27, 0x5c, 0x5c, 0x27, 0x20, + 0x2b, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x73, 0x5b, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x5d, 0x3b, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x73, 0x63, + 0x61, 0x70, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, + 0x20, 0x22, 0x27, 0x2b, 0x5c, 0x6e, 0x28, 0x28, 0x5f, 0x5f, 0x74, 0x3d, + 0x28, 0x22, 0x20, 0x2b, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, + 0x2b, 0x20, 0x22, 0x29, 0x29, 0x3d, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, + 0x27, 0x27, 0x3a, 0x5f, 0x2e, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x28, + 0x5f, 0x5f, 0x74, 0x29, 0x29, 0x2b, 0x5c, 0x6e, 0x27, 0x22, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, + 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x2b, 0x5c, 0x6e, 0x28, 0x28, 0x5f, 0x5f, + 0x74, 0x3d, 0x28, 0x22, 0x20, 0x2b, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x70, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x29, 0x29, + 0x3d, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x3f, 0x27, 0x27, 0x3a, 0x5f, 0x5f, + 0x74, 0x29, 0x2b, 0x5c, 0x6e, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x3b, + 0x5c, 0x6e, 0x22, 0x20, 0x2b, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x5c, 0x6e, 0x5f, 0x5f, 0x70, 0x2b, + 0x3d, 0x27, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x3d, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x2b, 0x20, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x3d, 0x20, 0x22, 0x27, 0x3b, 0x5c, + 0x6e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x49, 0x66, 0x20, 0x61, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2c, 0x20, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x21, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, + 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x27, 0x77, 0x69, 0x74, 0x68, + 0x28, 0x6f, 0x62, 0x6a, 0x7c, 0x7c, 0x7b, 0x7d, 0x29, 0x7b, 0x5c, 0x6e, + 0x27, 0x20, 0x2b, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, + 0x20, 0x27, 0x7d, 0x5c, 0x6e, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x76, + 0x61, 0x72, 0x20, 0x5f, 0x5f, 0x74, 0x2c, 0x5f, 0x5f, 0x70, 0x3d, 0x27, + 0x27, 0x2c, 0x5f, 0x5f, 0x6a, 0x3d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x6a, 0x6f, + 0x69, 0x6e, 0x2c, 0x22, 0x20, 0x2b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x5f, 0x5f, 0x70, 0x2b, 0x3d, + 0x5f, 0x5f, 0x6a, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2c, 0x27, 0x27, 0x29, 0x3b, 0x7d, + 0x3b, 0x5c, 0x6e, 0x22, 0x20, 0x2b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x22, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x5f, 0x70, 0x3b, 0x5c, 0x6e, + 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x79, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x2e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x7c, 0x7c, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x27, 0x2c, 0x20, 0x27, 0x5f, + 0x27, 0x2c, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x63, 0x61, 0x74, 0x63, 0x68, 0x20, + 0x28, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x65, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x28, 0x64, 0x61, 0x74, + 0x61, 0x2c, 0x20, 0x5f, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x5f, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x20, 0x3d, 0x20, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x27, 0x20, 0x2b, 0x20, 0x28, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x2e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, + 0x7c, 0x7c, 0x20, 0x27, 0x6f, 0x62, 0x6a, 0x27, 0x29, 0x20, 0x2b, 0x20, + 0x27, 0x29, 0x7b, 0x5c, 0x6e, 0x27, 0x20, 0x2b, 0x20, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x27, 0x7d, 0x27, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, + 0x61, 0x20, 0x22, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, + 0x68, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x64, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, + 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, + 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x28, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x4f, 0x4f, 0x50, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x66, 0x20, 0x55, + 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, + 0x74, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, + 0x20, 0x4f, 0x4f, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x20, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, + 0x68, 0x6f, 0x6c, 0x64, 0x73, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, + 0x65, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, + 0x69, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x2e, 0x0a, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3f, 0x20, 0x5f, 0x28, + 0x6f, 0x62, 0x6a, 0x29, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x28, 0x29, + 0x20, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, + 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x6e, + 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x6d, 0x69, + 0x78, 0x69, 0x6e, 0x28, 0x5f, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6d, 0x75, + 0x74, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x2e, 0x0a, 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x70, + 0x6f, 0x70, 0x27, 0x2c, 0x20, 0x27, 0x70, 0x75, 0x73, 0x68, 0x27, 0x2c, + 0x20, 0x27, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x27, 0x2c, 0x20, + 0x27, 0x73, 0x68, 0x69, 0x66, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x6f, + 0x72, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, + 0x27, 0x2c, 0x20, 0x27, 0x75, 0x6e, 0x73, 0x68, 0x69, 0x66, 0x74, 0x27, + 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, + 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3d, + 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5b, + 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, + 0x61, 0x6d, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x3d, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, + 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6f, 0x62, + 0x6a, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x28, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x73, + 0x68, 0x69, 0x66, 0x74, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, + 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x2e, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x30, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x63, + 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, 0x6f, 0x62, + 0x6a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, + 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, + 0x64, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x0a, + 0x20, 0x20, 0x65, 0x61, 0x63, 0x68, 0x28, 0x5b, 0x27, 0x63, 0x6f, 0x6e, + 0x63, 0x61, 0x74, 0x27, 0x2c, 0x20, 0x27, 0x6a, 0x6f, 0x69, 0x6e, 0x27, + 0x2c, 0x20, 0x27, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x27, 0x5d, 0x2c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, + 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5b, 0x6e, 0x61, 0x6d, + 0x65, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5b, 0x6e, 0x61, 0x6d, 0x65, + 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x20, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x5f, 0x2e, 0x65, + 0x78, 0x74, 0x65, 0x6e, 0x64, 0x28, 0x5f, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x7b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, + 0x63, 0x6f, 0x72, 0x65, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, + 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x20, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x0a, 0x7d, 0x29, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x29, 0x3b, 0x0a, + } +} + +// Underscore.js 1.4.4 +// http://underscorejs.org +// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. diff --git a/vendor/github.com/robertkrimen/otto/underscore/testify b/vendor/github.com/robertkrimen/otto/underscore/testify new file mode 100755 index 00000000..7f6e0f7c --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore/testify @@ -0,0 +1,84 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my $underscore_test = shift @ARGV || ""; +if (!-d $underscore_test) { + print <<_END_; +Usage: + + testify ./underscore/test + + # Should look something like: + arrays.js + chaining.js + collections.js + functions.js + index.html + objects.js + speed.js + utility.js + vendor + +_END_ + if ($underscore_test) { + die "!: Not a directory: $underscore_test\n" + } + exit; +} + +chdir $underscore_test or die "!: $!"; + +my @js = <*.js>; + +for my $file (@js) { + open my $fh, '<', $file or die "!: $!"; + my $tests = join "", <$fh>; + my @tests = $tests =~ m/ + ^(\s{2}test\(.*? + ^\s{2}}\);)$ + /mgxs; + close $fh; + next unless @tests; + print "$file: ", scalar(@tests), "\n"; + my $underscore_name = "underscore_$file"; + $underscore_name =~ s/.js$//; + my $go_file = "${underscore_name}_test.go"; + $go_file =~ s/.js$/.go/; + open $fh, '>', $go_file or die "!: $!"; + + $fh->print(<<_END_); +package otto + +import ( + "testing" +) + +_END_ + + my $count = 0; + for my $test (@tests) { + $test =~ s/`([^`]+)`/<$1>/g; + my ($name) = $test =~ m/^\s*test\(['"]([^'"]+)['"]/; + $fh->print(<<_END_); +// $name +func Test_${underscore_name}_$count(t *testing.T) { + tt(t, func(){ + test := underscoreTest() + + test(` +$test + `) + }) +} + +_END_ + $count++; + } +} + +# test('#779 - delimeters are applied to unescaped text.', 1, function() { +# var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g}); +# strictEqual(template(), '<<\nx\n>>'); +# }); diff --git a/vendor/github.com/robertkrimen/otto/underscore/underscore.go b/vendor/github.com/robertkrimen/otto/underscore/underscore.go new file mode 100644 index 00000000..714b8f3c --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore/underscore.go @@ -0,0 +1,49 @@ +/* +Package underscore contains the source for the JavaScript utility-belt library. + + import ( + _ "github.com/robertkrimen/otto/underscore" + ) + // Every Otto runtime will now include underscore + +http://underscorejs.org + +https://github.com/documentcloud/underscore + +By importing this package, you'll automatically load underscore every time you create a new Otto runtime. + +To prevent this behavior, you can do the following: + + import ( + "github.com/robertkrimen/otto/underscore" + ) + + func init() { + underscore.Disable() + } + +*/ +package underscore + +import ( + "github.com/robertkrimen/otto/registry" +) + +var entry *registry.Entry = registry.Register(func() string { + return Source() +}) + +// Enable underscore runtime inclusion. +func Enable() { + entry.Enable() +} + +// Disable underscore runtime inclusion. +func Disable() { + entry.Disable() +} + +// Source returns the underscore source. +func Source() string { + return string(underscore()) +} diff --git a/vendor/github.com/robertkrimen/otto/underscore_arrays_test.go b/vendor/github.com/robertkrimen/otto/underscore_arrays_test.go new file mode 100644 index 00000000..9d629782 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore_arrays_test.go @@ -0,0 +1,344 @@ +package otto + +import ( + "testing" +) + +// first +func Test_underscore_arrays_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("first", function() { + equal(_.first([1,2,3]), 1, 'can pull out the first element of an array'); + equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); + equal(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); + equal(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); + equal(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first'); + var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); + equal(result, 4, 'works on an arguments object.'); + result = _.map([[1,2,3],[1,2,3]], _.first); + equal(result.join(','), '1,1', 'works well with _.map'); + result = (function() { return _.take([1,2,3], 2); })(); + equal(result.join(','), '1,2', 'aliased as take'); + + equal(_.first(null), undefined, 'handles nulls'); + }); + `) + }) +} + +// rest +func Test_underscore_arrays_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("rest", function() { + var numbers = [1, 2, 3, 4]; + equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); + equal(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); + equal(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); + var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); + equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); + result = _.map([[1,2,3],[1,2,3]], _.rest); + equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); + result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4); + equal(result.join(', '), '2, 3, 4', 'aliased as drop and works on arguments object'); + }); + `) + }) +} + +// initial +func Test_underscore_arrays_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("initial", function() { + equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()'); + equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index'); + var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4); + equal(result.join(", "), "1, 2, 3", 'initial works on arguments object'); + result = _.map([[1,2,3],[1,2,3]], _.initial); + equal(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map'); + }); + `) + }) +} + +// last +func Test_underscore_arrays_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("last", function() { + equal(_.last([1,2,3]), 3, 'can pull out the last element of an array'); + equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last'); + equal(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last'); + equal(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last'); + var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4); + equal(result, 4, 'works on an arguments object'); + result = _.map([[1,2,3],[1,2,3]], _.last); + equal(result.join(','), '3,3', 'works well with _.map'); + + equal(_.last(null), undefined, 'handles nulls'); + }); + `) + }) +} + +// compact +func Test_underscore_arrays_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("compact", function() { + equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); + var result = (function(){ return _.compact(arguments).length; })(0, 1, false, 2, false, 3); + equal(result, 3, 'works on an arguments object'); + }); + `) + }) +} + +// flatten +func Test_underscore_arrays_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("flatten", function() { + var list = [1, [2], [3, [[[4]]]]]; + deepEqual(_.flatten(list), [1,2,3,4], 'can flatten nested arrays'); + deepEqual(_.flatten(list, true), [1,2,3,[[[4]]]], 'can shallowly flatten nested arrays'); + var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); + deepEqual(result, [1,2,3,4], 'works on an arguments object'); + }); + `) + }) +} + +// without +func Test_underscore_arrays_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("without", function() { + var list = [1, 2, 1, 0, 3, 1, 4]; + equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); + var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); + equal(result.join(', '), '2, 3, 4', 'works on an arguments object'); + + var list = [{one : 1}, {two : 2}]; + ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.'); + ok(_.without(list, list[0]).length == 1, 'ditto.'); + }); + `) + }) +} + +// uniq +func Test_underscore_arrays_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("uniq", function() { + var list = [1, 2, 1, 3, 1, 4]; + equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); + + var list = [1, 1, 1, 2, 2, 3]; + equal(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); + + var list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}]; + var iterator = function(value) { return value.name; }; + equal(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator'); + + equal(_.map(_.uniq(list, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); + + var iterator = function(value) { return value +1; }; + var list = [1, 2, 2, 3, 4, 4]; + equal(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array'); + + var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4); + equal(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); + }); + `) + }) +} + +// intersection +func Test_underscore_arrays_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("intersection", function() { + var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; + equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); + equal(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection'); + var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry'); + equal(result.join(''), 'moe', 'works on an arguments object'); + }); + `) + }) +} + +// union +func Test_underscore_arrays_9(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("union", function() { + var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); + equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); + + var result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]); + equal(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays'); + }); + `) + }) +} + +// difference +func Test_underscore_arrays_10(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("difference", function() { + var result = _.difference([1, 2, 3], [2, 30, 40]); + equal(result.join(' '), '1 3', 'takes the difference of two arrays'); + + var result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]); + equal(result.join(' '), '3 4', 'takes the difference of three arrays'); + }); + `) + }) +} + +// zip +func Test_underscore_arrays_11(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('zip', function() { + var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; + var stooges = _.zip(names, ages, leaders); + equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); + }); + `) + }) +} + +// object +func Test_underscore_arrays_12(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('object', function() { + var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); + var shouldBe = {moe: 30, larry: 40, curly: 50}; + ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object'); + + result = _.object([['one', 1], ['two', 2], ['three', 3]]); + shouldBe = {one: 1, two: 2, three: 3}; + ok(_.isEqual(result, shouldBe), 'an array of pairs zipped together into an object'); + + var stooges = {moe: 30, larry: 40, curly: 50}; + ok(_.isEqual(_.object(_.pairs(stooges)), stooges), 'an object converted to pairs and back to an object'); + + ok(_.isEqual(_.object(null), {}), 'handles nulls'); + }); + `) + }) +} + +// indexOf +func Test_underscore_arrays_13(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("indexOf", function() { + var numbers = [1, 2, 3]; + numbers.indexOf = null; + equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); + var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); + equal(result, 1, 'works on an arguments object'); + equal(_.indexOf(null, 2), -1, 'handles nulls properly'); + + var numbers = [10, 20, 30, 40, 50], num = 35; + var index = _.indexOf(numbers, num, true); + equal(index, -1, '35 is not in the list'); + + numbers = [10, 20, 30, 40, 50]; num = 40; + index = _.indexOf(numbers, num, true); + equal(index, 3, '40 is in the list'); + + numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; + index = _.indexOf(numbers, num, true); + equal(index, 1, '40 is in the list'); + + numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; + index = _.indexOf(numbers, 2, 5); + equal(index, 7, 'supports the fromIndex argument'); + }); + `) + }) +} + +// lastIndexOf +func Test_underscore_arrays_14(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("lastIndexOf", function() { + var numbers = [1, 0, 1]; + equal(_.lastIndexOf(numbers, 1), 2); + + numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; + numbers.lastIndexOf = null; + equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); + equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); + var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); + equal(result, 5, 'works on an arguments object'); + equal(_.indexOf(null, 2), -1, 'handles nulls properly'); + + numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; + var index = _.lastIndexOf(numbers, 2, 2); + equal(index, 1, 'supports the fromIndex argument'); + }); + `) + }) +} + +// range +func Test_underscore_arrays_15(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("range", function() { + equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); + equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); + equal(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); + equal(_.range(8, 5).join(''), '', 'range with two arguments a & b, b<a generates an empty array'); + equal(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); + equal(_.range(3, 10, 15).join(''), '3', 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); + equal(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); + equal(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); + }); + `) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/underscore_chaining_test.go b/vendor/github.com/robertkrimen/otto/underscore_chaining_test.go new file mode 100644 index 00000000..accf04fd --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore_chaining_test.go @@ -0,0 +1,95 @@ +package otto + +import ( + "testing" +) + +// map/flatten/reduce +func Test_underscore_chaining_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("map/flatten/reduce", function() { + var lyrics = [ + "I'm a lumberjack and I'm okay", + "I sleep all night and I work all day", + "He's a lumberjack and he's okay", + "He sleeps all night and he works all day" + ]; + var counts = _(lyrics).chain() + .map(function(line) { return line.split(''); }) + .flatten() + .reduce(function(hash, l) { + hash[l] = hash[l] || 0; + hash[l]++; + return hash; + }, {}).value(); + ok(counts['a'] == 16 && counts['e'] == 10, 'counted all the letters in the song'); + }); + `) + }) +} + +// select/reject/sortBy +func Test_underscore_chaining_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("select/reject/sortBy", function() { + var numbers = [1,2,3,4,5,6,7,8,9,10]; + numbers = _(numbers).chain().select(function(n) { + return n % 2 == 0; + }).reject(function(n) { + return n % 4 == 0; + }).sortBy(function(n) { + return -n; + }).value(); + equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + }); + `) + }) +} + +// select/reject/sortBy in functional style +func Test_underscore_chaining_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("select/reject/sortBy in functional style", function() { + var numbers = [1,2,3,4,5,6,7,8,9,10]; + numbers = _.chain(numbers).select(function(n) { + return n % 2 == 0; + }).reject(function(n) { + return n % 4 == 0; + }).sortBy(function(n) { + return -n; + }).value(); + equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + }); + `) + }) +} + +// reverse/concat/unshift/pop/map +func Test_underscore_chaining_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("reverse/concat/unshift/pop/map", function() { + var numbers = [1,2,3,4,5]; + numbers = _(numbers).chain() + .reverse() + .concat([5, 5, 5]) + .unshift(17) + .pop() + .map(function(n){ return n * 2; }) + .value(); + equal(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); + }); + `) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/underscore_collections_test.go b/vendor/github.com/robertkrimen/otto/underscore_collections_test.go new file mode 100644 index 00000000..9afc2a2b --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore_collections_test.go @@ -0,0 +1,698 @@ +package otto + +import ( + "testing" +) + +// each +func Test_underscore_collections_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("each", function() { + _.each([1, 2, 3], function(num, i) { + equal(num, i + 1, 'each iterators provide value and iteration count'); + }); + + var answers = []; + _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5}); + equal(answers.join(', '), '5, 10, 15', 'context object property accessed'); + + answers = []; + _.forEach([1, 2, 3], function(num){ answers.push(num); }); + equal(answers.join(', '), '1, 2, 3', 'aliased as "forEach"'); + + answers = []; + var obj = {one : 1, two : 2, three : 3}; + obj.constructor.prototype.four = 4; + _.each(obj, function(value, key){ answers.push(key); }); + equal(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); + delete obj.constructor.prototype.four; + + var answer = null; + _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); + ok(answer, 'can reference the original collection from inside the iterator'); + + answers = 0; + _.each(null, function(){ ++answers; }); + equal(answers, 0, 'handles a null properly'); + }); + `) + }) +} + +// map +func Test_underscore_collections_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('map', function() { + var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); + equal(doubled.join(', '), '2, 4, 6', 'doubled numbers'); + + doubled = _.collect([1, 2, 3], function(num){ return num * 2; }); + equal(doubled.join(', '), '2, 4, 6', 'aliased as "collect"'); + + var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); + equal(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); + + var doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); + equal(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); + + // TEST: ReferenceError: document is not defined + return; + + if (document.querySelectorAll) { + var ids = _.map(document.querySelectorAll('#map-test *'), function(n){ return n.id; }); + deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.'); + } + + var ids = _.map($('#map-test').children(), function(n){ return n.id; }); + deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on jQuery Array-likes.'); + + var ids = _.map(document.images, function(n){ return n.id; }); + ok(ids[0] == 'chart_image', 'can use collection methods on HTMLCollections'); + + var ifnull = _.map(null, function(){}); + ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly'); + }); + `) + }) +} + +// reduce +func Test_underscore_collections_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('reduce', function() { + var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); + equal(sum, 6, 'can sum up an array'); + + var context = {multiplier : 3}; + sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context); + equal(sum, 18, 'can reduce with a context object'); + + sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); + equal(sum, 6, 'aliased as "inject"'); + + sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); + equal(sum, 6, 'OO-style reduce'); + + var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }); + equal(sum, 6, 'default initial value'); + + var ifnull; + try { + _.reduce(null, function(){}); + } catch (ex) { + ifnull = ex; + } + ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); + + ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); + equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); + raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); + }); + `) + }) +} + +// reduceRight +func Test_underscore_collections_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('reduceRight', function() { + var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); + equal(list, 'bazbarfoo', 'can perform right folds'); + + var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); + equal(list, 'bazbarfoo', 'aliased as "foldr"'); + + var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }); + equal(list, 'bazbarfoo', 'default initial value'); + + var ifnull; + try { + _.reduceRight(null, function(){}); + } catch (ex) { + ifnull = ex; + } + ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); + + var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; }); + equal(sum, 6, 'default initial value on object'); + + ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); + + equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); + raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); + + // Assert that the correct arguments are being passed. + + var args, + memo = {}, + object = {a: 1, b: 2}, + lastKey = _.keys(object).pop(); + + var expected = lastKey == 'a' + ? [memo, 1, 'a', object] + : [memo, 2, 'b', object]; + + _.reduceRight(object, function() { + args || (args = _.toArray(arguments)); + }, memo); + + deepEqual(args, expected); + + // And again, with numeric keys. + + object = {'2': 'a', '1': 'b'}; + lastKey = _.keys(object).pop(); + args = null; + + expected = lastKey == '2' + ? [memo, 'a', '2', object] + : [memo, 'b', '1', object]; + + _.reduceRight(object, function() { + args || (args = _.toArray(arguments)); + }, memo); + + deepEqual(args, expected); + }); + `) + }) +} + +// find +func Test_underscore_collections_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('find', function() { + var array = [1, 2, 3, 4]; + strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found '); + strictEqual(_.find(array, function() { return false; }), void 0, 'should return if is not found'); + }); + `) + }) +} + +// detect +func Test_underscore_collections_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('detect', function() { + var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; }); + equal(result, 2, 'found the first "2" and broke the loop'); + }); + `) + }) +} + +// select +func Test_underscore_collections_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('select', function() { + var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equal(evens.join(', '), '2, 4, 6', 'selected each even number'); + + evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equal(evens.join(', '), '2, 4, 6', 'aliased as "filter"'); + }); + `) + }) +} + +// reject +func Test_underscore_collections_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('reject', function() { + var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equal(odds.join(', '), '1, 3, 5', 'rejected each even number'); + + var context = "obj"; + + var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){ + equal(context, "obj"); + return num % 2 != 0; + }, context); + equal(evens.join(', '), '2, 4, 6', 'rejected each odd number'); + }); + `) + }) +} + +// all +func Test_underscore_collections_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('all', function() { + ok(_.all([], _.identity), 'the empty set'); + ok(_.all([true, true, true], _.identity), 'all true values'); + ok(!_.all([true, false, true], _.identity), 'one false value'); + ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers'); + ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number'); + ok(_.all([1], _.identity) === true, 'cast to boolean - true'); + ok(_.all([0], _.identity) === false, 'cast to boolean - false'); + ok(_.every([true, true, true], _.identity), 'aliased as "every"'); + ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined'); + }); + `) + }) +} + +// any +func Test_underscore_collections_9(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('any', function() { + var nativeSome = Array.prototype.some; + Array.prototype.some = null; + ok(!_.any([]), 'the empty set'); + ok(!_.any([false, false, false]), 'all false values'); + ok(_.any([false, false, true]), 'one true value'); + ok(_.any([null, 0, 'yes', false]), 'a string'); + ok(!_.any([null, 0, '', false]), 'falsy values'); + ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers'); + ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number'); + ok(_.any([1], _.identity) === true, 'cast to boolean - true'); + ok(_.any([0], _.identity) === false, 'cast to boolean - false'); + ok(_.some([false, false, true]), 'aliased as "some"'); + Array.prototype.some = nativeSome; + }); + `) + }) +} + +// include +func Test_underscore_collections_10(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('include', function() { + ok(_.include([1,2,3], 2), 'two is in the array'); + ok(!_.include([1,3,9], 2), 'two is not in the array'); + ok(_.contains({moe:1, larry:3, curly:9}, 3) === true, '_.include on objects checks their values'); + ok(_([1,2,3]).include(2), 'OO-style include'); + }); + `) + }) +} + +// invoke +func Test_underscore_collections_11(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('invoke', function() { + var list = [[5, 1, 7], [3, 2, 1]]; + var result = _.invoke(list, 'sort'); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + }); + `) + }) +} + +// invoke w/ function reference +func Test_underscore_collections_12(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('invoke w/ function reference', function() { + var list = [[5, 1, 7], [3, 2, 1]]; + var result = _.invoke(list, Array.prototype.sort); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + }); + `) + }) +} + +// invoke when strings have a call method +func Test_underscore_collections_13(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('invoke when strings have a call method', function() { + String.prototype.call = function() { + return 42; + }; + var list = [[5, 1, 7], [3, 2, 1]]; + var s = "foo"; + equal(s.call(), 42, "call function exists"); + var result = _.invoke(list, 'sort'); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + delete String.prototype.call; + equal(s.call, undefined, "call function removed"); + }); + `) + }) +} + +// pluck +func Test_underscore_collections_14(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('pluck', function() { + var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}]; + equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects'); + }); + `) + }) +} + +// where +func Test_underscore_collections_15(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('where', function() { + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; + var result = _.where(list, {a: 1}); + equal(result.length, 3); + equal(result[result.length - 1].b, 4); + result = _.where(list, {b: 2}); + equal(result.length, 2); + equal(result[0].a, 1); + }); + `) + }) +} + +// findWhere +func Test_underscore_collections_16(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('findWhere', function() { + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; + var result = _.findWhere(list, {a: 1}); + deepEqual(result, {a: 1, b: 2}); + result = _.findWhere(list, {b: 4}); + deepEqual(result, {a: 1, b: 4}); + }); + `) + }) +} + +// max +func Test_underscore_collections_17(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('max', function() { + equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); + + var neg = _.max([1, 2, 3], function(num){ return -num; }); + equal(neg, 1, 'can perform a computation-based max'); + + equal(-Infinity, _.max({}), 'Maximum value of an empty object'); + equal(-Infinity, _.max([]), 'Maximum value of an empty array'); + equal(_.max({'a': 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); + + // TEST: Takes too long + return; + + equal(299999, _.max(_.range(1,300000)), "Maximum value of a too-big array"); + }); + `) + }) +} + +// min +func Test_underscore_collections_18(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('min', function() { + equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); + + var neg = _.min([1, 2, 3], function(num){ return -num; }); + equal(neg, 3, 'can perform a computation-based min'); + + equal(Infinity, _.min({}), 'Minimum value of an empty object'); + equal(Infinity, _.min([]), 'Minimum value of an empty array'); + equal(_.min({'a': 'a'}), Infinity, 'Minimum value of a non-numeric collection'); + + var now = new Date(9999999999); + var then = new Date(0); + equal(_.min([now, then]), then); + + // TEST: Takes too long + return; + + equal(1, _.min(_.range(1,300000)), "Minimum value of a too-big array"); + }); + `) + }) +} + +// sortBy +func Test_underscore_collections_19(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('sortBy', function() { + var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; + people = _.sortBy(people, function(person){ return person.age; }); + equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); + + var list = [undefined, 4, 1, undefined, 3, 2]; + equal(_.sortBy(list, _.identity).join(','), '1,2,3,4,,', 'sortBy with undefined values'); + + var list = ["one", "two", "three", "four", "five"]; + var sorted = _.sortBy(list, 'length'); + equal(sorted.join(' '), 'one two four five three', 'sorted by length'); + + function Pair(x, y) { + this.x = x; + this.y = y; + } + + var collection = [ + new Pair(1, 1), new Pair(1, 2), + new Pair(1, 3), new Pair(1, 4), + new Pair(1, 5), new Pair(1, 6), + new Pair(2, 1), new Pair(2, 2), + new Pair(2, 3), new Pair(2, 4), + new Pair(2, 5), new Pair(2, 6), + new Pair(undefined, 1), new Pair(undefined, 2), + new Pair(undefined, 3), new Pair(undefined, 4), + new Pair(undefined, 5), new Pair(undefined, 6) + ]; + + var actual = _.sortBy(collection, function(pair) { + return pair.x; + }); + + deepEqual(actual, collection, 'sortBy should be stable'); + }); + `) + }) +} + +// groupBy +func Test_underscore_collections_20(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('groupBy', function() { + var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; }); + ok('0' in parity && '1' in parity, 'created a group for each value'); + equal(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group'); + + var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var grouped = _.groupBy(list, 'length'); + equal(grouped['3'].join(' '), 'one two six ten'); + equal(grouped['4'].join(' '), 'four five nine'); + equal(grouped['5'].join(' '), 'three seven eight'); + + var context = {}; + _.groupBy([{}], function(){ ok(this === context); }, context); + + grouped = _.groupBy([4.2, 6.1, 6.4], function(num) { + return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; + }); + equal(grouped.constructor.length, 1); + equal(grouped.hasOwnProperty.length, 2); + + var array = [{}]; + _.groupBy(array, function(value, index, obj){ ok(obj === array); }); + + var array = [1, 2, 1, 2, 3]; + var grouped = _.groupBy(array); + equal(grouped['1'].length, 2); + equal(grouped['3'].length, 1); + }); + `) + }) +} + +// countBy +func Test_underscore_collections_21(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('countBy', function() { + var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; }); + equal(parity['true'], 2); + equal(parity['false'], 3); + + var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var grouped = _.countBy(list, 'length'); + equal(grouped['3'], 4); + equal(grouped['4'], 3); + equal(grouped['5'], 3); + + var context = {}; + _.countBy([{}], function(){ ok(this === context); }, context); + + grouped = _.countBy([4.2, 6.1, 6.4], function(num) { + return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; + }); + equal(grouped.constructor, 1); + equal(grouped.hasOwnProperty, 2); + + var array = [{}]; + _.countBy(array, function(value, index, obj){ ok(obj === array); }); + + var array = [1, 2, 1, 2, 3]; + var grouped = _.countBy(array); + equal(grouped['1'], 2); + equal(grouped['3'], 1); + }); + `) + }) +} + +// sortedIndex +func Test_underscore_collections_22(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('sortedIndex', function() { + var numbers = [10, 20, 30, 40, 50], num = 35; + var indexForNum = _.sortedIndex(numbers, num); + equal(indexForNum, 3, '35 should be inserted at index 3'); + + var indexFor30 = _.sortedIndex(numbers, 30); + equal(indexFor30, 2, '30 should be inserted at index 2'); + + var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; + var iterator = function(obj){ return obj.x; }; + strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2); + strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3); + + var context = {1: 2, 2: 3, 3: 4}; + iterator = function(obj){ return this[obj]; }; + strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1); + }); + `) + }) +} + +// shuffle +func Test_underscore_collections_23(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('shuffle', function() { + var numbers = _.range(10); + var shuffled = _.shuffle(numbers).sort(); + notStrictEqual(numbers, shuffled, 'original object is unmodified'); + equal(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle'); + }); + `) + }) +} + +// toArray +func Test_underscore_collections_24(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('toArray', function() { + ok(!_.isArray(arguments), 'arguments object is not an array'); + ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); + var a = [1,2,3]; + ok(_.toArray(a) !== a, 'array is cloned'); + equal(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements'); + + var numbers = _.toArray({one : 1, two : 2, three : 3}); + equal(numbers.join(', '), '1, 2, 3', 'object flattened into array'); + + // TEST: ReferenceError: document is not defined + return; + + // test in IE < 9 + try { + var actual = _.toArray(document.childNodes); + } catch(ex) { } + + ok(_.isArray(actual), 'should not throw converting a node list'); + }); + `) + }) +} + +// size +func Test_underscore_collections_25(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test('size', function() { + equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object'); + equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); + + var func = function() { + return _.size(arguments); + }; + + equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object'); + + equal(_.size('hello'), 5, 'can compute the size of a string'); + + equal(_.size(null), 0, 'handles nulls'); + }); + `) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/underscore_functions_test.go b/vendor/github.com/robertkrimen/otto/underscore_functions_test.go new file mode 100644 index 00000000..be59e16b --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore_functions_test.go @@ -0,0 +1,208 @@ +package otto + +import ( + "testing" +) + +// bind +func Test_underscore_functions_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("bind", function() { + var context = {name : 'moe'}; + var func = function(arg) { return "name: " + (this.name || arg); }; + var bound = _.bind(func, context); + equal(bound(), 'name: moe', 'can bind a function to a context'); + + bound = _(func).bind(context); + equal(bound(), 'name: moe', 'can do OO-style binding'); + + bound = _.bind(func, null, 'curly'); + equal(bound(), 'name: curly', 'can bind without specifying a context'); + + func = function(salutation, name) { return salutation + ': ' + name; }; + func = _.bind(func, this, 'hello'); + equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); + + func = _.bind(func, this, 'curly'); + equal(func(), 'hello: curly', 'the function was completely applied in advance'); + + func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; + func = _.bind(func, this, 'hello', 'moe', 'curly'); + equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); + + func = function(context, message) { equal(this, context, message); }; + _.bind(func, 0, 0, 'can bind a function to <0>')(); + _.bind(func, '', '', 'can bind a function to an empty string')(); + _.bind(func, false, false, 'can bind a function to ')(); + + // These tests are only meaningful when using a browser without a native bind function + // To test this with a modern browser, set underscore's nativeBind to undefined + var F = function () { return this; }; + var Boundf = _.bind(F, {hello: "moe curly"}); + equal(Boundf().hello, "moe curly", "When called without the new operator, it's OK to be bound to the context"); + }); + `) + }) +} + +// partial +func Test_underscore_functions_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("partial", function() { + var obj = {name: 'moe'}; + var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); }; + + obj.func = _.partial(func, 'a', 'b'); + equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); + }); + `) + }) +} + +// bindAll +func Test_underscore_functions_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("bindAll", function() { + var curly = {name : 'curly'}, moe = { + name : 'moe', + getName : function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; } + }; + curly.getName = moe.getName; + _.bindAll(moe, 'getName', 'sayHi'); + curly.sayHi = moe.sayHi; + equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); + equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); + + curly = {name : 'curly'}; + moe = { + name : 'moe', + getName : function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; } + }; + _.bindAll(moe); + curly.sayHi = moe.sayHi; + equal(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object'); + }); + `) + }) +} + +// memoize +func Test_underscore_functions_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("memoize", function() { + var fib = function(n) { + return n < 2 ? n : fib(n - 1) + fib(n - 2); + }; + var fastFib = _.memoize(fib); + equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + equal(fastFib(10), 55, 'a memoized version of fibonacci produces identical results'); + + var o = function(str) { + return str; + }; + var fastO = _.memoize(o); + equal(o('toString'), 'toString', 'checks hasOwnProperty'); + equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); + }); + `) + }) +} + +// once +func Test_underscore_functions_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("once", function() { + var num = 0; + var increment = _.once(function(){ num++; }); + increment(); + increment(); + equal(num, 1); + }); + `) + }) +} + +// wrap +func Test_underscore_functions_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("wrap", function() { + var greet = function(name){ return "hi: " + name; }; + var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); + equal(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function'); + + var inner = function(){ return "Hello "; }; + var obj = {name : "Moe"}; + obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); + equal(obj.hi(), "Hello Moe"); + + var noop = function(){}; + var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); }); + var ret = wrapped(['whats', 'your'], 'vector', 'victor'); + deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); + }); + `) + }) +} + +// compose +func Test_underscore_functions_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("compose", function() { + var greet = function(name){ return "hi: " + name; }; + var exclaim = function(sentence){ return sentence + '!'; }; + var composed = _.compose(exclaim, greet); + equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); + + composed = _.compose(greet, exclaim); + equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); + }); + `) + }) +} + +// after +func Test_underscore_functions_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("after", function() { + var testAfter = function(afterAmount, timesCalled) { + var afterCalled = 0; + var after = _.after(afterAmount, function() { + afterCalled++; + }); + while (timesCalled--) after(); + return afterCalled; + }; + + equal(testAfter(5, 5), 1, "after(N) should fire after being called N times"); + equal(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); + equal(testAfter(0, 0), 1, "after(0) should fire immediately"); + }); + `) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/underscore_objects_test.go b/vendor/github.com/robertkrimen/otto/underscore_objects_test.go new file mode 100644 index 00000000..1973af8d --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore_objects_test.go @@ -0,0 +1,826 @@ +package otto + +import ( + "testing" +) + +// keys +func Test_underscore_objects_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("keys", function() { + equal(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object'); + // the test above is not safe because it relies on for-in enumeration order + var a = []; a[1] = 0; + equal(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); + raises(function() { _.keys(null); }, TypeError, 'throws an error for values'); + raises(function() { _.keys(void 0); }, TypeError, 'throws an error for values'); + raises(function() { _.keys(1); }, TypeError, 'throws an error for number primitives'); + raises(function() { _.keys('a'); }, TypeError, 'throws an error for string primitives'); + raises(function() { _.keys(true); }, TypeError, 'throws an error for boolean primitives'); + }); + `) + }) +} + +// values +func Test_underscore_objects_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("values", function() { + equal(_.values({one: 1, two: 2}).join(', '), '1, 2', 'can extract the values from an object'); + equal(_.values({one: 1, two: 2, length: 3}).join(', '), '1, 2, 3', '... even when one of them is "length"'); + }); + `) + }) +} + +// pairs +func Test_underscore_objects_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("pairs", function() { + deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); + deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); + }); + `) + }) +} + +// invert +func Test_underscore_objects_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("invert", function() { + var obj = {first: 'Moe', second: 'Larry', third: 'Curly'}; + equal(_.keys(_.invert(obj)).join(' '), 'Moe Larry Curly', 'can invert an object'); + ok(_.isEqual(_.invert(_.invert(obj)), obj), 'two inverts gets you back where you started'); + + var obj = {length: 3}; + ok(_.invert(obj)['3'] == 'length', 'can invert an object with "length"') + }); + `) + }) +} + +// functions +func Test_underscore_objects_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("functions", function() { + var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce}; + ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object'); + + var Animal = function(){}; + Animal.prototype.run = function(){}; + equal(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype'); + }); + `) + }) +} + +// extend +func Test_underscore_objects_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("extend", function() { + var result; + equal(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); + equal(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); + equal(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden'); + result = _.extend({x:'x'}, {a:'a'}, {b:'b'}); + ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects'); + result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'}); + ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps'); + result = _.extend({}, {a: void 0, b: null}); + equal(_.keys(result).join(''), 'ab', 'extend does not copy undefined values'); + + try { + result = {}; + _.extend(result, null, undefined, {a:1}); + } catch(ex) {} + + equal(result.a, 1, 'should not error on or sources'); + }); + `) + }) +} + +// pick +func Test_underscore_objects_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("pick", function() { + var result; + result = _.pick({a:1, b:2, c:3}, 'a', 'c'); + ok(_.isEqual(result, {a:1, c:3}), 'can restrict properties to those named'); + result = _.pick({a:1, b:2, c:3}, ['b', 'c']); + ok(_.isEqual(result, {b:2, c:3}), 'can restrict properties to those named in an array'); + result = _.pick({a:1, b:2, c:3}, ['a'], 'b'); + ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args'); + + var Obj = function(){}; + Obj.prototype = {a: 1, b: 2, c: 3}; + ok(_.isEqual(_.pick(new Obj, 'a', 'c'), {a:1, c: 3}), 'include prototype props'); + }); + `) + }) +} + +// omit +func Test_underscore_objects_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("omit", function() { + var result; + result = _.omit({a:1, b:2, c:3}, 'b'); + ok(_.isEqual(result, {a:1, c:3}), 'can omit a single named property'); + result = _.omit({a:1, b:2, c:3}, 'a', 'c'); + ok(_.isEqual(result, {b:2}), 'can omit several named properties'); + result = _.omit({a:1, b:2, c:3}, ['b', 'c']); + ok(_.isEqual(result, {a:1}), 'can omit properties named in an array'); + + var Obj = function(){}; + Obj.prototype = {a: 1, b: 2, c: 3}; + ok(_.isEqual(_.omit(new Obj, 'b'), {a:1, c: 3}), 'include prototype props'); + }); + `) + }) +} + +// defaults +func Test_underscore_objects_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("defaults", function() { + var result; + var options = {zero: 0, one: 1, empty: "", nan: NaN, string: "string"}; + + _.defaults(options, {zero: 1, one: 10, twenty: 20}); + equal(options.zero, 0, 'value exists'); + equal(options.one, 1, 'value exists'); + equal(options.twenty, 20, 'default applied'); + + _.defaults(options, {empty: "full"}, {nan: "nan"}, {word: "word"}, {word: "dog"}); + equal(options.empty, "", 'value exists'); + ok(_.isNaN(options.nan), "NaN isn't overridden"); + equal(options.word, "word", 'new value is added, first one wins'); + + try { + options = {}; + _.defaults(options, null, undefined, {a:1}); + } catch(ex) {} + + equal(options.a, 1, 'should not error on or sources'); + }); + `) + }) +} + +// clone +func Test_underscore_objects_9(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("clone", function() { + var moe = {name : 'moe', lucky : [13, 27, 34]}; + var clone = _.clone(moe); + equal(clone.name, 'moe', 'the clone as the attributes of the original'); + + clone.name = 'curly'; + ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original'); + + clone.lucky.push(101); + equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); + + equal(_.clone(undefined), void 0, 'non objects should not be changed by clone'); + equal(_.clone(1), 1, 'non objects should not be changed by clone'); + equal(_.clone(null), null, 'non objects should not be changed by clone'); + }); + `) + }) +} + +// isEqual +func Test_underscore_objects_10(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isEqual", function() { + function First() { + this.value = 1; + } + First.prototype.value = 1; + function Second() { + this.value = 1; + } + Second.prototype.value = 2; + + // Basic equality and identity comparisons. + ok(_.isEqual(null, null), " is equal to "); + ok(_.isEqual(), " is equal to "); + + ok(!_.isEqual(0, -0), "<0> is not equal to <-0>"); + ok(!_.isEqual(-0, 0), "Commutative equality is implemented for <0> and <-0>"); + ok(!_.isEqual(null, undefined), " is not equal to "); + ok(!_.isEqual(undefined, null), "Commutative equality is implemented for and "); + + // String object and primitive comparisons. + ok(_.isEqual("Curly", "Curly"), "Identical string primitives are equal"); + ok(_.isEqual(new String("Curly"), new String("Curly")), "String objects with identical primitive values are equal"); + ok(_.isEqual(new String("Curly"), "Curly"), "String primitives and their corresponding object wrappers are equal"); + ok(_.isEqual("Curly", new String("Curly")), "Commutative equality is implemented for string objects and primitives"); + + ok(!_.isEqual("Curly", "Larry"), "String primitives with different values are not equal"); + ok(!_.isEqual(new String("Curly"), new String("Larry")), "String objects with different primitive values are not equal"); + ok(!_.isEqual(new String("Curly"), {toString: function(){ return "Curly"; }}), "String objects and objects with a custom method are not equal"); + + // Number object and primitive comparisons. + ok(_.isEqual(75, 75), "Identical number primitives are equal"); + ok(_.isEqual(new Number(75), new Number(75)), "Number objects with identical primitive values are equal"); + ok(_.isEqual(75, new Number(75)), "Number primitives and their corresponding object wrappers are equal"); + ok(_.isEqual(new Number(75), 75), "Commutative equality is implemented for number objects and primitives"); + ok(!_.isEqual(new Number(0), -0), " and <-0> are not equal"); + ok(!_.isEqual(0, new Number(-0)), "Commutative equality is implemented for and <-0>"); + + ok(!_.isEqual(new Number(75), new Number(63)), "Number objects with different primitive values are not equal"); + ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), "Number objects and objects with a method are not equal"); + + // Comparisons involving . + ok(_.isEqual(NaN, NaN), " is equal to "); + ok(!_.isEqual(61, NaN), "A number primitive is not equal to "); + ok(!_.isEqual(new Number(79), NaN), "A number object is not equal to "); + ok(!_.isEqual(Infinity, NaN), " is not equal to "); + + // Boolean object and primitive comparisons. + ok(_.isEqual(true, true), "Identical boolean primitives are equal"); + ok(_.isEqual(new Boolean, new Boolean), "Boolean objects with identical primitive values are equal"); + ok(_.isEqual(true, new Boolean(true)), "Boolean primitives and their corresponding object wrappers are equal"); + ok(_.isEqual(new Boolean(true), true), "Commutative equality is implemented for booleans"); + ok(!_.isEqual(new Boolean(true), new Boolean), "Boolean objects with different primitive values are not equal"); + + // Common type coercions. + ok(!_.isEqual(true, new Boolean(false)), "Boolean objects are not equal to the boolean primitive "); + ok(!_.isEqual("75", 75), "String and number primitives with like values are not equal"); + ok(!_.isEqual(new Number(63), new String(63)), "String and number objects with like values are not equal"); + ok(!_.isEqual(75, "75"), "Commutative equality is implemented for like string and number values"); + ok(!_.isEqual(0, ""), "Number and string primitives with like values are not equal"); + ok(!_.isEqual(1, true), "Number and boolean primitives with like values are not equal"); + ok(!_.isEqual(new Boolean(false), new Number(0)), "Boolean and number objects with like values are not equal"); + ok(!_.isEqual(false, new String("")), "Boolean primitives and string objects with like values are not equal"); + ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), "Dates and their corresponding numeric primitive values are not equal"); + + // Dates. + ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), "Date objects referencing identical times are equal"); + ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), "Date objects referencing different times are not equal"); + ok(!_.isEqual(new Date(2009, 11, 13), { + getTime: function(){ + return 12606876e5; + } + }), "Date objects and objects with a method are not equal"); + ok(!_.isEqual(new Date("Curly"), new Date("Curly")), "Invalid dates are not equal"); + + // Functions. + ok(!_.isEqual(First, Second), "Different functions with identical bodies and source code representations are not equal"); + + // RegExps. + ok(_.isEqual(/(?:)/gim, /(?:)/gim), "RegExps with equivalent patterns and flags are equal"); + ok(!_.isEqual(/(?:)/g, /(?:)/gi), "RegExps with equivalent patterns and different flags are not equal"); + ok(!_.isEqual(/Moe/gim, /Curly/gim), "RegExps with different patterns and equivalent flags are not equal"); + ok(!_.isEqual(/(?:)/gi, /(?:)/g), "Commutative equality is implemented for RegExps"); + ok(!_.isEqual(/Curly/g, {source: "Larry", global: true, ignoreCase: false, multiline: false}), "RegExps and RegExp-like objects are not equal"); + + // Empty arrays, array-like objects, and object literals. + ok(_.isEqual({}, {}), "Empty object literals are equal"); + ok(_.isEqual([], []), "Empty array literals are equal"); + ok(_.isEqual([{}], [{}]), "Empty nested arrays and objects are equal"); + ok(!_.isEqual({length: 0}, []), "Array-like objects and arrays are not equal."); + ok(!_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects"); + + ok(!_.isEqual({}, []), "Object literals and array literals are not equal"); + ok(!_.isEqual([], {}), "Commutative equality is implemented for objects and arrays"); + + // Arrays with primitive and object values. + ok(_.isEqual([1, "Larry", true], [1, "Larry", true]), "Arrays containing identical primitives are equal"); + ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal"); + + // Multi-dimensional arrays. + var a = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; + var b = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; + ok(_.isEqual(a, b), "Arrays containing nested arrays and objects are recursively compared"); + + // Overwrite the methods defined in ES 5.1 section 15.4.4. + a.forEach = a.map = a.filter = a.every = a.indexOf = a.lastIndexOf = a.some = a.reduce = a.reduceRight = null; + b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null; + + // Array elements and properties. + ok(_.isEqual(a, b), "Arrays containing equivalent elements and different non-numeric properties are equal"); + a.push("White Rocks"); + ok(!_.isEqual(a, b), "Arrays of different lengths are not equal"); + a.push("East Boulder"); + b.push("Gunbarrel Ranch", "Teller Farm"); + ok(!_.isEqual(a, b), "Arrays of identical lengths containing different elements are not equal"); + + // Sparse arrays. + ok(_.isEqual(Array(3), Array(3)), "Sparse arrays of identical lengths are equal"); + ok(!_.isEqual(Array(3), Array(6)), "Sparse arrays of different lengths are not equal when both are empty"); + + // Simple objects. + ok(_.isEqual({a: "Curly", b: 1, c: true}, {a: "Curly", b: 1, c: true}), "Objects containing identical primitives are equal"); + ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), "Objects containing equivalent members are equal"); + ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), "Objects of identical sizes with different values are not equal"); + ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), "Objects of identical sizes with different property names are not equal"); + ok(!_.isEqual({a: 1, b: 2}, {a: 1}), "Objects of different sizes are not equal"); + ok(!_.isEqual({a: 1}, {a: 1, b: 2}), "Commutative equality is implemented for objects"); + ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), "Objects with identical keys and different values are not equivalent"); + + // contains nested objects and arrays. + a = { + name: new String("Moe Howard"), + age: new Number(77), + stooge: true, + hobbies: ["acting"], + film: { + name: "Sing a Song of Six Pants", + release: new Date(1947, 9, 30), + stars: [new String("Larry Fine"), "Shemp Howard"], + minutes: new Number(16), + seconds: 54 + } + }; + + // contains equivalent nested objects and arrays. + b = { + name: new String("Moe Howard"), + age: new Number(77), + stooge: true, + hobbies: ["acting"], + film: { + name: "Sing a Song of Six Pants", + release: new Date(1947, 9, 30), + stars: [new String("Larry Fine"), "Shemp Howard"], + minutes: new Number(16), + seconds: 54 + } + }; + ok(_.isEqual(a, b), "Objects with nested equivalent members are recursively compared"); + + // Instances. + ok(_.isEqual(new First, new First), "Object instances are equal"); + ok(!_.isEqual(new First, new Second), "Objects with different constructors and identical own properties are not equal"); + ok(!_.isEqual({value: 1}, new First), "Object instances and objects sharing equivalent properties are not equal"); + ok(!_.isEqual({value: 2}, new Second), "The prototype chain of objects should not be examined"); + + // Circular Arrays. + (a = []).push(a); + (b = []).push(b); + ok(_.isEqual(a, b), "Arrays containing circular references are equal"); + a.push(new String("Larry")); + b.push(new String("Larry")); + ok(_.isEqual(a, b), "Arrays containing circular references and equivalent properties are equal"); + a.push("Shemp"); + b.push("Curly"); + ok(!_.isEqual(a, b), "Arrays containing circular references and different properties are not equal"); + + // More circular arrays #767. + a = ["everything is checked but", "this", "is not"]; + a[1] = a; + b = ["everything is checked but", ["this", "array"], "is not"]; + ok(!_.isEqual(a, b), "Comparison of circular references with non-circular references are not equal"); + + // Circular Objects. + a = {abc: null}; + b = {abc: null}; + a.abc = a; + b.abc = b; + ok(_.isEqual(a, b), "Objects containing circular references are equal"); + a.def = 75; + b.def = 75; + ok(_.isEqual(a, b), "Objects containing circular references and equivalent properties are equal"); + a.def = new Number(75); + b.def = new Number(63); + ok(!_.isEqual(a, b), "Objects containing circular references and different properties are not equal"); + + // More circular objects #767. + a = {everything: "is checked", but: "this", is: "not"}; + a.but = a; + b = {everything: "is checked", but: {that:"object"}, is: "not"}; + ok(!_.isEqual(a, b), "Comparison of circular references with non-circular object references are not equal"); + + // Cyclic Structures. + a = [{abc: null}]; + b = [{abc: null}]; + (a[0].abc = a).push(a); + (b[0].abc = b).push(b); + ok(_.isEqual(a, b), "Cyclic structures are equal"); + a[0].def = "Larry"; + b[0].def = "Larry"; + ok(_.isEqual(a, b), "Cyclic structures containing equivalent properties are equal"); + a[0].def = new String("Larry"); + b[0].def = new String("Curly"); + ok(!_.isEqual(a, b), "Cyclic structures containing different properties are not equal"); + + // Complex Circular References. + a = {foo: {b: {foo: {c: {foo: null}}}}}; + b = {foo: {b: {foo: {c: {foo: null}}}}}; + a.foo.b.foo.c.foo = a; + b.foo.b.foo.c.foo = b; + ok(_.isEqual(a, b), "Cyclic structures with nested and identically-named properties are equal"); + + // Chaining. + ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); + + a = _({x: 1, y: 2}).chain(); + b = _({x: 1, y: 2}).chain(); + equal(_.isEqual(a.isEqual(b), _(true)), true, ' can be chained'); + + // TEST: ??? + return; + + // Objects from another frame. + ok(_.isEqual({}, iObject)); + }); + `) + }) +} + +// isEmpty +func Test_underscore_objects_11(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isEmpty", function() { + ok(!_([1]).isEmpty(), '[1] is not empty'); + ok(_.isEmpty([]), '[] is empty'); + ok(!_.isEmpty({one : 1}), '{one : 1} is not empty'); + ok(_.isEmpty({}), '{} is empty'); + ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); + ok(_.isEmpty(null), 'null is empty'); + ok(_.isEmpty(), 'undefined is empty'); + ok(_.isEmpty(''), 'the empty string is empty'); + ok(!_.isEmpty('moe'), 'but other strings are not'); + + var obj = {one : 1}; + delete obj.one; + ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); + }); + `) + }) +} + +// isElement +func Test_underscore_objects_12(t *testing.T) { + // TEST: ReferenceError: $ is not defined + if true { + return + } + + tt(t, func() { + test, _ := test_() + + test(` + test("isElement", function() { + ok(!_.isElement('div'), 'strings are not dom elements'); + ok(_.isElement($('html')[0]), 'the html tag is a DOM element'); + ok(_.isElement(iElement), 'even from another frame'); + }); + `) + }) +} + +// isArguments +func Test_underscore_objects_13(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isArguments", function() { + var args = (function(){ return arguments; })(1, 2, 3); + ok(!_.isArguments('string'), 'a string is not an arguments object'); + ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); + ok(_.isArguments(args), 'but the arguments object is an arguments object'); + ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); + ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.'); + + // TEST: ReferenceError: iArguments is not defined + return; + ok(_.isArguments(iArguments), 'even from another frame'); + }); + `) + }) +} + +// isObject +func Test_underscore_objects_14(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isObject", function() { + ok(_.isObject(arguments), 'the arguments object is object'); + ok(_.isObject([1, 2, 3]), 'and arrays'); + // TEST: ReferenceError: $ is not defined + return; + ok(_.isObject($('html')[0]), 'and DOM element'); + ok(_.isObject(iElement), 'even from another frame'); + ok(_.isObject(function () {}), 'and functions'); + ok(_.isObject(iFunction), 'even from another frame'); + ok(!_.isObject(null), 'but not null'); + ok(!_.isObject(undefined), 'and not undefined'); + ok(!_.isObject('string'), 'and not string'); + ok(!_.isObject(12), 'and not number'); + ok(!_.isObject(true), 'and not boolean'); + ok(_.isObject(new String('string')), 'but new String()'); + }); + `) + }) +} + +// isArray +func Test_underscore_objects_15(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isArray", function() { + ok(!_.isArray(arguments), 'the arguments object is not an array'); + ok(_.isArray([1, 2, 3]), 'but arrays are'); + // TEST: ??? + return; + ok(_.isArray(iArray), 'even from another frame'); + }); + `) + }) +} + +// isString +func Test_underscore_objects_16(t *testing.T) { + // TEST: ReferenceError: document is not defined + if true { + return + } + + tt(t, func() { + test, _ := test_() + + test(` + test("isString", function() { + ok(!_.isString(document.body), 'the document body is not a string'); + ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); + // TEST: ??? + return; + ok(_.isString(iString), 'even from another frame'); + }); + `) + }) +} + +// isNumber +func Test_underscore_objects_17(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isNumber", function() { + ok(!_.isNumber('string'), 'a string is not a number'); + ok(!_.isNumber(arguments), 'the arguments object is not a number'); + ok(!_.isNumber(undefined), 'undefined is not a number'); + ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); + ok(_.isNumber(NaN), 'NaN *is* a number'); + ok(_.isNumber(Infinity), 'Infinity is a number'); + // TEST: ??? + return; + ok(_.isNumber(iNumber), 'even from another frame'); + ok(!_.isNumber('1'), 'numeric strings are not numbers'); + }); + `) + }) +} + +// isBoolean +func Test_underscore_objects_18(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isBoolean", function() { + ok(!_.isBoolean(2), 'a number is not a boolean'); + ok(!_.isBoolean("string"), 'a string is not a boolean'); + ok(!_.isBoolean("false"), 'the string "false" is not a boolean'); + ok(!_.isBoolean("true"), 'the string "true" is not a boolean'); + ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); + ok(!_.isBoolean(undefined), 'undefined is not a boolean'); + ok(!_.isBoolean(NaN), 'NaN is not a boolean'); + ok(!_.isBoolean(null), 'null is not a boolean'); + ok(_.isBoolean(true), 'but true is'); + ok(_.isBoolean(false), 'and so is false'); + // TEST: ??? + return; + ok(_.isBoolean(iBoolean), 'even from another frame'); + }); + `) + }) +} + +// isFunction +func Test_underscore_objects_19(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isFunction", function() { + ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); + ok(!_.isFunction('moe'), 'strings are not functions'); + ok(_.isFunction(_.isFunction), 'but functions are'); + // TEST: ??? + return; + ok(_.isFunction(iFunction), 'even from another frame'); + }); + `) + }) +} + +// isDate +func Test_underscore_objects_20(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isDate", function() { + ok(!_.isDate(100), 'numbers are not dates'); + ok(!_.isDate({}), 'objects are not dates'); + ok(_.isDate(new Date()), 'but dates are'); + // TEST: ??? + return; + ok(_.isDate(iDate), 'even from another frame'); + }); + `) + }) +} + +// isRegExp +func Test_underscore_objects_21(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isRegExp", function() { + ok(!_.isRegExp(_.identity), 'functions are not RegExps'); + ok(_.isRegExp(/identity/), 'but RegExps are'); + // TEST: ??? + return; + ok(_.isRegExp(iRegExp), 'even from another frame'); + }); + `) + }) +} + +// isFinite +func Test_underscore_objects_22(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isFinite", function() { + ok(!_.isFinite(undefined), 'undefined is not Finite'); + ok(!_.isFinite(null), 'null is not Finite'); + ok(!_.isFinite(NaN), 'NaN is not Finite'); + ok(!_.isFinite(Infinity), 'Infinity is not Finite'); + ok(!_.isFinite(-Infinity), '-Infinity is not Finite'); + ok(_.isFinite('12'), 'Numeric strings are numbers'); + ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); + ok(!_.isFinite(''), 'Empty strings are not numbers'); + var obj = new Number(5); + ok(_.isFinite(obj), 'Number instances can be finite'); + ok(_.isFinite(0), '0 is Finite'); + ok(_.isFinite(123), 'Ints are Finite'); + ok(_.isFinite(-12.44), 'Floats are Finite'); + }); + `) + }) +} + +// isNaN +func Test_underscore_objects_23(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isNaN", function() { + ok(!_.isNaN(undefined), 'undefined is not NaN'); + ok(!_.isNaN(null), 'null is not NaN'); + ok(!_.isNaN(0), '0 is not NaN'); + ok(_.isNaN(NaN), 'but NaN is'); + // TEST: ??? + return; + ok(_.isNaN(iNaN), 'even from another frame'); + ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); + }); + `) + }) +} + +// isNull +func Test_underscore_objects_24(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isNull", function() { + ok(!_.isNull(undefined), 'undefined is not null'); + ok(!_.isNull(NaN), 'NaN is not null'); + ok(_.isNull(null), 'but null is'); + // TEST: ??? + return; + ok(_.isNull(iNull), 'even from another frame'); + }); + `) + }) +} + +// isUndefined +func Test_underscore_objects_25(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("isUndefined", function() { + ok(!_.isUndefined(1), 'numbers are defined'); + ok(!_.isUndefined(null), 'null is defined'); + ok(!_.isUndefined(false), 'false is defined'); + ok(!_.isUndefined(NaN), 'NaN is defined'); + ok(_.isUndefined(), 'nothing is undefined'); + ok(_.isUndefined(undefined), 'undefined is undefined'); + // TEST: ??? + return; + ok(_.isUndefined(iUndefined), 'even from another frame'); + }); + `) + }) +} + +// tap +func Test_underscore_objects_26(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("tap", function() { + var intercepted = null; + var interceptor = function(obj) { intercepted = obj; }; + var returned = _.tap(1, interceptor); + equal(intercepted, 1, "passes tapped object to interceptor"); + equal(returned, 1, "returns tapped object"); + + returned = _([1,2,3]).chain(). + map(function(n){ return n * 2; }). + max(). + tap(interceptor). + value(); + ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain'); + }); + `) + }) +} + +// has +func Test_underscore_objects_27(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("has", function () { + var obj = {foo: "bar", func: function () {} }; + ok (_.has(obj, "foo"), "has() checks that the object has a property."); + ok (_.has(obj, "baz") == false, "has() returns false if the object doesn't have the property."); + ok (_.has(obj, "func"), "has() works for functions too."); + obj.hasOwnProperty = null; + ok (_.has(obj, "foo"), "has() works even when the hasOwnProperty method is deleted."); + var child = {}; + child.prototype = obj; + ok (_.has(child, "foo") == false, "has() does not check the prototype chain for a property.") + }); + `) + }) +} diff --git a/vendor/github.com/robertkrimen/otto/underscore_test.go b/vendor/github.com/robertkrimen/otto/underscore_test.go new file mode 100644 index 00000000..a01aa132 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore_test.go @@ -0,0 +1,165 @@ +package otto + +import ( + "testing" + + "github.com/robertkrimen/otto/terst" + "github.com/robertkrimen/otto/underscore" +) + +func init() { + underscore.Disable() +} + +// A persistent handle for the underscore tester +// We do not run underscore tests in parallel, so it is okay to stash globally +// (Maybe use sync.Pool in the future...) +var tester_ *_tester + +// A tester for underscore: test_ => test(underscore) :) +func test_(arguments ...interface{}) (func(string, ...interface{}) Value, *_tester) { + tester := tester_ + if tester == nil { + tester = newTester() + tester.underscore() // Load underscore and testing shim, etc. + tester_ = tester + } + + return tester.test, tester +} + +func (self *_tester) underscore() { + vm := self.vm + _, err := vm.Run(underscore.Source()) + if err != nil { + panic(err) + } + + vm.Set("assert", func(call FunctionCall) Value { + if !call.Argument(0).bool() { + message := "Assertion failed" + if len(call.ArgumentList) > 1 { + message = call.ArgumentList[1].string() + } + t := terst.Caller().T() + is(message, nil) + t.Fail() + return falseValue + } + return trueValue + }) + + vm.Run(` + var templateSettings; + + function _setup() { + templateSettings = _.clone(_.templateSettings); + } + + function _teardown() { + _.templateSettings = templateSettings; + } + + function module() { + /* Nothing happens. */ + } + + function equals(a, b, emit) { + assert(a == b, emit + ", <" + a + "> != <" + b + ">"); + } + var equal = equals; + + function notStrictEqual(a, b, emit) { + assert(a !== b, emit); + } + + function strictEqual(a, b, emit) { + assert(a === b, emit); + } + + function ok(a, emit) { + assert(a, emit); + } + + function raises(fn, want, emit) { + var have, _ok = false; + if (typeof want === "string") { + emit = want; + want = null; + } + + try { + fn(); + } catch(tmp) { + have = tmp; + } + + if (have) { + if (!want) { + _ok = true; + } + else if (want instanceof RegExp) { + _ok = want.test(have); + } + else if (have instanceof want) { + _ok = true + } + else if (want.call({}, have) === true) { + _ok = true; + } + } + + ok(_ok, emit); + } + + function test(name){ + _setup() + try { + templateSettings = _.clone(_.templateSettings); + if (arguments.length == 3) { + count = 0 + for (count = 0; count < arguments[1]; count++) { + arguments[2]() + } + } else { + // For now. + arguments[1]() + } + } + finally { + _teardown() + } + } + + function deepEqual(a, b, emit) { + // Also, for now. + assert(_.isEqual(a, b), emit) + } + `) +} + +func Test_underscore(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + _.map([1, 2, 3], function(value){ + return value + 1 + }) + `, "2,3,4") + + test(` + abc = _.find([1, 2, 3, -1], function(value) { return value == -1 }) + `, -1) + + test(`_.isEqual(1, 1)`, true) + test(`_.isEqual([], [])`, true) + test(`_.isEqual(['b', 'd'], ['b', 'd'])`, true) + test(`_.isEqual(['b', 'd', 'c'], ['b', 'd', 'e'])`, false) + test(`_.isFunction(function(){})`, true) + test(`_.template('

\u2028<%= "\\u2028\\u2029" %>\u2029

')()`, "

\u2028\u2028\u2029\u2029

") + }) +} + +// TODO Test: typeof An argument reference +// TODO Test: abc = {}; abc == Object(abc) diff --git a/vendor/github.com/robertkrimen/otto/underscore_utility_test.go b/vendor/github.com/robertkrimen/otto/underscore_utility_test.go new file mode 100644 index 00000000..ebabb083 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/underscore_utility_test.go @@ -0,0 +1,419 @@ +package otto + +import ( + "testing" +) + +// #750 - Return _ instance. +func Test_underscore_utility_0(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("#750 - Return _ instance.", 2, function() { + var instance = _([]); + ok(_(instance) === instance); + ok(new _(instance) === instance); + }); + `) + }) +} + +// identity +func Test_underscore_utility_1(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("identity", function() { + var moe = {name : 'moe'}; + equal(_.identity(moe), moe, 'moe is the same as his identity'); + }); + `) + }) +} + +// random +func Test_underscore_utility_2(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("random", function() { + var array = _.range(1000); + var min = Math.pow(2, 31); + var max = Math.pow(2, 62); + + ok(_.every(array, function() { + return _.random(min, max) >= min; + }), "should produce a random number greater than or equal to the minimum number"); + + ok(_.some(array, function() { + return _.random(Number.MAX_VALUE) > 0; + }), "should produce a random number when passed "); + }); + `) + }) +} + +// uniqueId +func Test_underscore_utility_3(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("uniqueId", function() { + var ids = [], i = 0; + while(i++ < 100) ids.push(_.uniqueId()); + equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); + }); + `) + }) +} + +// times +func Test_underscore_utility_4(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("times", function() { + var vals = []; + _.times(3, function (i) { vals.push(i); }); + ok(_.isEqual(vals, [0,1,2]), "is 0 indexed"); + // + vals = []; + _(3).times(function(i) { vals.push(i); }); + ok(_.isEqual(vals, [0,1,2]), "works as a wrapper"); + // collects return values + ok(_.isEqual([0, 1, 2], _.times(3, function(i) { return i; })), "collects return values"); + }); + `) + }) +} + +// mixin +func Test_underscore_utility_5(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("mixin", function() { + _.mixin({ + myReverse: function(string) { + return string.split('').reverse().join(''); + } + }); + equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); + equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); + }); + `) + }) +} + +// _.escape +func Test_underscore_utility_6(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("_.escape", function() { + equal(_.escape("Curly & Moe"), "Curly & Moe"); + equal(_.escape("Curly & Moe"), "Curly &amp; Moe"); + equal(_.escape(null), ''); + }); + `) + }) +} + +// _.unescape +func Test_underscore_utility_7(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("_.unescape", function() { + var string = "Curly & Moe"; + equal(_.unescape("Curly & Moe"), string); + equal(_.unescape("Curly &amp; Moe"), "Curly & Moe"); + equal(_.unescape(null), ''); + equal(_.unescape(_.escape(string)), string); + }); + `) + }) +} + +// template +func Test_underscore_utility_8(t *testing.T) { + tt(t, func() { + test, _ := test_() + + test(` + test("template", function() { + var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); + var result = basicTemplate({thing : 'This'}); + equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); + + var sansSemicolonTemplate = _.template("A <% this %> B"); + equal(sansSemicolonTemplate(), "A B"); + + var backslashTemplate = _.template("<%= thing %> is \\ridanculous"); + equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); + + var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); + equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); + + var fancyTemplate = _.template("
    <% \ + for (var key in people) { \ + %>
  • <%= people[key] %>
  • <% } %>
"); + result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); + equal(result, "
  • Moe
  • Larry
  • Curly
", 'can run arbitrary javascript in templates'); + + var escapedCharsInJavascriptTemplate = _.template("
    <% _.each(numbers.split('\\n'), function(item) { %>
  • <%= item %>
  • <% }) %>
"); + result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"}); + equal(result, "
  • one
  • two
  • three
  • four
", 'Can use escaped characters (e.g. \\n) in Javascript'); + + var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
\">
<% }); %>"); + result = namespaceCollisionTemplate({ + pageCount: 3, + thumbnails: { + 1: "p1-thumbnail.gif", + 2: "p2-thumbnail.gif", + 3: "p3-thumbnail.gif" + } + }); + equal(result, "3 p3-thumbnail.gif
"); + + var noInterpolateTemplate = _.template("

Just some text. Hey, I know this is silly but it aids consistency.

"); + result = noInterpolateTemplate(); + equal(result, "

Just some text. Hey, I know this is silly but it aids consistency.

"); + + var quoteTemplate = _.template("It's its, not it's"); + equal(quoteTemplate({}), "It's its, not it's"); + + var quoteInStatementAndBody = _.template("<%\ + if(foo == 'bar'){ \ + %>Statement quotes and 'quotes'.<% } %>"); + equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + + var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); + equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + + var template = _.template("<%- value %>"); + var result = template({value: "